@hailer/mcp 1.0.29 → 1.1.2
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/.session-checked +1 -0
- package/.claude/agents/agent-ada-skill-builder.md +10 -2
- package/.claude/agents/agent-alejandro-function-fields.md +104 -37
- package/.claude/agents/agent-bjorn-config-audit.md +41 -21
- package/.claude/agents/agent-builder-agent-creator.md +13 -3
- package/.claude/agents/agent-code-simplifier.md +53 -0
- package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
- package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
- package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
- package/.claude/agents/agent-helga-workflow-config.md +75 -10
- package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
- package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
- package/.claude/agents/agent-ivan-monolith.md +154 -0
- package/.claude/agents/agent-kenji-data-reader.md +15 -8
- package/.claude/agents/agent-lars-code-inspector.md +56 -8
- package/.claude/agents/agent-marco-mockup-builder.md +110 -0
- package/.claude/agents/agent-marcus-api-documenter.md +323 -0
- package/.claude/agents/agent-marketplace-publisher.md +232 -72
- package/.claude/agents/agent-marketplace-reviewer.md +255 -79
- package/.claude/agents/agent-permissions-handler.md +208 -0
- package/.claude/agents/agent-simple-writer.md +48 -0
- package/.claude/agents/agent-svetlana-code-review.md +127 -14
- package/.claude/agents/agent-tanya-test-runner.md +333 -0
- package/.claude/agents/agent-ui-designer.md +100 -0
- package/.claude/agents/agent-viktor-sql-insights.md +19 -6
- package/.claude/agents/agent-web-search.md +55 -0
- package/.claude/agents/agent-yevgeni-discussions.md +7 -1
- package/.claude/agents/agent-zara-zapier.md +159 -0
- package/.claude/commands/app-squad.md +135 -0
- package/.claude/commands/audit-squad.md +158 -0
- package/.claude/commands/autoplan.md +563 -0
- package/.claude/commands/cleanup-squad.md +98 -0
- package/.claude/commands/config-squad.md +106 -0
- package/.claude/commands/crud-squad.md +87 -0
- package/.claude/commands/data-squad.md +97 -0
- package/.claude/commands/debug-squad.md +303 -0
- package/.claude/commands/doc-squad.md +65 -0
- package/.claude/commands/handoff.md +137 -0
- package/.claude/commands/health.md +49 -0
- package/.claude/commands/help.md +2 -1
- package/.claude/commands/help:agents.md +96 -16
- package/.claude/commands/help:commands.md +55 -11
- package/.claude/commands/help:faq.md +16 -1
- package/.claude/commands/help:skills.md +93 -0
- package/.claude/commands/hotfix-squad.md +112 -0
- package/.claude/commands/integration-squad.md +82 -0
- package/.claude/commands/janitor-squad.md +167 -0
- package/.claude/commands/learn-auto.md +120 -0
- package/.claude/commands/learn.md +120 -0
- package/.claude/commands/mcp-list.md +27 -0
- package/.claude/commands/onboard-squad.md +140 -0
- package/.claude/commands/plan-workspace.md +732 -0
- package/.claude/commands/prd.md +131 -0
- package/.claude/commands/project-status.md +82 -0
- package/.claude/commands/publish.md +138 -0
- package/.claude/commands/recap.md +69 -0
- package/.claude/commands/restore.md +64 -0
- package/.claude/commands/review-squad.md +152 -0
- package/.claude/commands/save.md +24 -0
- package/.claude/commands/stats.md +19 -0
- package/.claude/commands/swarm.md +210 -0
- package/.claude/commands/tool-builder.md +3 -1
- package/.claude/commands/ws-pull.md +1 -1
- package/.claude/commands/yolo-off.md +17 -0
- package/.claude/commands/yolo.md +82 -0
- package/.claude/hooks/_shared-memory.cjs +305 -0
- package/.claude/hooks/_utils.cjs +134 -0
- package/.claude/hooks/agent-failure-detector.cjs +164 -79
- package/.claude/hooks/agent-usage-logger.cjs +204 -0
- package/.claude/hooks/app-edit-guard.cjs +20 -4
- package/.claude/hooks/auto-learn.cjs +316 -0
- package/.claude/hooks/bash-guard.cjs +282 -0
- package/.claude/hooks/builder-mode-manager.cjs +183 -54
- package/.claude/hooks/bulk-activity-guard.cjs +283 -0
- package/.claude/hooks/context-watchdog.cjs +292 -0
- package/.claude/hooks/delegation-reminder.cjs +478 -0
- package/.claude/hooks/design-system-lint.cjs +283 -0
- package/.claude/hooks/post-scaffold-hook.cjs +16 -3
- package/.claude/hooks/prompt-guard.cjs +366 -0
- package/.claude/hooks/publish-template-guard.cjs +16 -0
- package/.claude/hooks/session-start.cjs +35 -0
- package/.claude/hooks/shared-memory-writer.cjs +147 -0
- package/.claude/hooks/skill-injector.cjs +140 -0
- package/.claude/hooks/skill-usage-logger.cjs +258 -0
- package/.claude/hooks/src-edit-guard.cjs +16 -1
- package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
- package/.claude/scripts/yolo-toggle.cjs +142 -0
- package/.claude/settings.json +141 -14
- package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
- package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
- package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
- package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
- package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
- package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
- package/.claude/skills/agent-structure/SKILL.md +98 -0
- package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
- package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
- package/.claude/skills/delegation-routing/SKILL.md +202 -0
- package/.claude/skills/frontend-design/SKILL.md +254 -0
- package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
- package/.claude/skills/hailer-api-client/SKILL.md +518 -0
- package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
- package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
- package/.claude/skills/hailer-design-system/SKILL.md +235 -0
- package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
- package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
- package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
- package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
- package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
- package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
- package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
- package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
- package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
- package/.claude/skills/integration-patterns/SKILL.md +421 -0
- package/.claude/skills/json-only-output/SKILL.md +52 -12
- package/.claude/skills/lsp-setup/SKILL.md +160 -0
- package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
- package/.claude/skills/optional-parameters/SKILL.md +32 -23
- package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
- package/.claude/skills/testing-patterns/SKILL.md +630 -0
- package/.claude/skills/tool-builder/SKILL.md +250 -0
- package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
- package/.claude/skills/tool-response-verification/SKILL.md +82 -48
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
- package/.env.example +26 -7
- package/CLAUDE.md +290 -224
- package/dist/CLAUDE.md +370 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +101 -101
- package/dist/bot/bot-config.d.ts +26 -0
- package/dist/bot/bot-config.js +135 -0
- package/dist/bot/bot-manager.d.ts +40 -0
- package/dist/bot/bot-manager.js +137 -0
- package/dist/bot/bot.d.ts +127 -0
- package/dist/bot/bot.js +1328 -0
- package/dist/bot/operation-logger.d.ts +28 -0
- package/dist/bot/operation-logger.js +132 -0
- package/dist/bot/services/conversation-manager.d.ts +60 -0
- package/dist/bot/services/conversation-manager.js +246 -0
- package/dist/bot/services/index.d.ts +9 -0
- package/dist/bot/services/index.js +18 -0
- package/dist/bot/services/message-classifier.d.ts +42 -0
- package/dist/bot/services/message-classifier.js +228 -0
- package/dist/bot/services/message-formatter.d.ts +88 -0
- package/dist/bot/services/message-formatter.js +411 -0
- package/dist/bot/services/session-logger.d.ts +162 -0
- package/dist/bot/services/session-logger.js +724 -0
- package/dist/bot/services/token-billing.d.ts +78 -0
- package/dist/bot/services/token-billing.js +233 -0
- package/dist/bot/services/types.d.ts +169 -0
- package/dist/bot/services/types.js +12 -0
- package/dist/bot/services/typing-indicator.d.ts +23 -0
- package/dist/bot/services/typing-indicator.js +60 -0
- package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
- package/dist/bot/services/workspace-schema-cache.js +506 -0
- package/dist/bot/tool-executor.d.ts +28 -0
- package/dist/bot/tool-executor.js +48 -0
- package/dist/bot/workspace-overview.d.ts +12 -0
- package/dist/bot/workspace-overview.js +94 -0
- package/dist/cli.d.ts +1 -8
- package/dist/cli.js +1 -253
- package/dist/config.d.ts +96 -3
- package/dist/config.js +148 -37
- package/dist/core.d.ts +5 -0
- package/dist/core.js +61 -8
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/lib/logger.d.ts +0 -1
- package/dist/lib/logger.js +39 -23
- package/dist/lib/request-logger.d.ts +77 -0
- package/dist/lib/request-logger.js +147 -0
- package/dist/mcp/UserContextCache.js +16 -13
- package/dist/mcp/hailer-clients.js +18 -17
- package/dist/mcp/signal-handler.js +29 -13
- package/dist/mcp/tool-registry.d.ts +4 -15
- package/dist/mcp/tool-registry.js +94 -32
- package/dist/mcp/tools/activity.js +28 -69
- package/dist/mcp/tools/app-core.js +9 -4
- package/dist/mcp/tools/app-marketplace.js +22 -12
- package/dist/mcp/tools/app-member.js +5 -2
- package/dist/mcp/tools/app-scaffold.js +32 -18
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/discussion.js +107 -77
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/file.js +5 -2
- package/dist/mcp/tools/insight.js +36 -12
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/user.d.ts +2 -4
- package/dist/mcp/tools/user.js +9 -50
- package/dist/mcp/tools/workflow.d.ts +1 -0
- package/dist/mcp/tools/workflow.js +164 -52
- package/dist/mcp/utils/hailer-api-client.js +26 -17
- package/dist/mcp/webhook-handler.d.ts +64 -3
- package/dist/mcp/webhook-handler.js +219 -9
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +237 -25
- package/dist/plugins/bug-fixer/index.d.ts +2 -0
- package/dist/plugins/bug-fixer/index.js +18 -0
- package/dist/plugins/bug-fixer/tools.d.ts +45 -0
- package/dist/plugins/bug-fixer/tools.js +1096 -0
- package/package.json +10 -10
- package/scripts/test-hal-tools.ts +154 -0
- package/.claude/agents/agent-nora-name-functions.md +0 -123
- package/.claude/assistant-knowledge.md +0 -23
- package/.claude/commands/install-plugin.md +0 -261
- package/.claude/commands/list-plugins.md +0 -42
- package/.claude/commands/marketplace-setup.md +0 -33
- package/.claude/commands/publish-plugin.md +0 -55
- package/.claude/commands/uninstall-plugin.md +0 -87
- package/.claude/hooks/interactive-mode.cjs +0 -87
- package/.claude/hooks/mcp-server-guard.cjs +0 -108
- package/.claude/skills/marketplace-publishing.md +0 -155
- package/dist/bot/chat-bot.d.ts +0 -31
- package/dist/bot/chat-bot.js +0 -357
- package/dist/mcp/tools/metrics.d.ts +0 -13
- package/dist/mcp/tools/metrics.js +0 -546
- package/dist/stdio-server.d.ts +0 -14
- package/dist/stdio-server.js +0 -114
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integration-patterns
|
|
3
|
+
description: Hailer integration microservice patterns - activity movers, webhooks, SCIM, Kafka consumers
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
triggers: Build integration, activity mover, webhook handler, SCIM endpoint, Kafka consumer
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Integration Patterns
|
|
9
|
+
|
|
10
|
+
## Architecture Overview
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
14
|
+
│ External │ │ Integration │ │ Hailer API │
|
|
15
|
+
│ System │────▶│ Service │────▶│ │
|
|
16
|
+
│ (Kafka/HTTP) │ │ (Node.js) │ │ (WebSocket) │
|
|
17
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
18
|
+
│
|
|
19
|
+
▼
|
|
20
|
+
┌─────────────────┐
|
|
21
|
+
│ Logging & │
|
|
22
|
+
│ Monitoring │
|
|
23
|
+
└─────────────────┘
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Activity Mover Pattern
|
|
29
|
+
|
|
30
|
+
Monitors activities and moves linked activities when triggers fire.
|
|
31
|
+
|
|
32
|
+
### Configuration Structure
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
interface ActivityMoverConfig {
|
|
36
|
+
logDiscussionId: string; // Discussion for audit logging
|
|
37
|
+
email: string; // Integration user email
|
|
38
|
+
password: string; // Or "ENV:HAILER_PASSWORD"
|
|
39
|
+
project: string; // Project identifier
|
|
40
|
+
triggers: Trigger[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Trigger {
|
|
44
|
+
processId: string; // Source workflow ID
|
|
45
|
+
phaseId: string; // Phase that triggers action
|
|
46
|
+
metaDataId: string; // Hidden field for tracking ("Seen"/"Not seen")
|
|
47
|
+
linkedProcesses: LinkedProcess[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface LinkedProcess {
|
|
51
|
+
processId: string; // Target workflow ID
|
|
52
|
+
sourcePhases: string[]; // Move FROM these phases
|
|
53
|
+
targetPhase: string; // Move TO this phase
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Example Config
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"logDiscussionId": "694c9536acfa30f6df13201b",
|
|
62
|
+
"email": "integration@workspace.com",
|
|
63
|
+
"password": "ENV:HAILER_PASSWORD",
|
|
64
|
+
"project": "orders-sync",
|
|
65
|
+
"triggers": [
|
|
66
|
+
{
|
|
67
|
+
"processId": "67dc1b7d3d2c9f6cf9a5468d",
|
|
68
|
+
"phaseId": "67dc1b7d3d2c9f6cf9a546c4",
|
|
69
|
+
"metaDataId": "67e697da6ada809b961c35b5",
|
|
70
|
+
"linkedProcesses": [
|
|
71
|
+
{
|
|
72
|
+
"processId": "67dc1b7d3d2c9f6cf9a54690",
|
|
73
|
+
"sourcePhases": ["67dc1b7d3d2c9f6cf9a54691", "67dc1b7d3d2c9f6cf9a54692"],
|
|
74
|
+
"targetPhase": "67dc1b7d3d2c9f6cf9a54695"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Hailer Setup Requirements
|
|
83
|
+
|
|
84
|
+
1. **Metadata field** on trigger workflow:
|
|
85
|
+
- Type: TEXT (hidden)
|
|
86
|
+
- Default value: "Not seen"
|
|
87
|
+
- Used to track processed activities
|
|
88
|
+
|
|
89
|
+
2. **Link field** connecting workflows:
|
|
90
|
+
- Activity link from trigger workflow to target workflow
|
|
91
|
+
|
|
92
|
+
3. **Integration user**:
|
|
93
|
+
- Dedicated user account
|
|
94
|
+
- Edit permissions on both workflows
|
|
95
|
+
|
|
96
|
+
4. **Discussion activity**:
|
|
97
|
+
- For audit logging
|
|
98
|
+
- Integration user must have access
|
|
99
|
+
|
|
100
|
+
### Core Logic
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Listen for activity updates
|
|
104
|
+
client.on('activities.updated', async (data) => {
|
|
105
|
+
for (const trigger of config.triggers) {
|
|
106
|
+
if (data.processId === trigger.processId && data.phaseId === trigger.phaseId) {
|
|
107
|
+
await processTrigger(data.activityId, trigger);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
async function processTrigger(activityId: string, trigger: Trigger) {
|
|
113
|
+
// Check if already processed
|
|
114
|
+
const activity = await client.request('v3.activity.get', [activityId]);
|
|
115
|
+
if (activity.fields[trigger.metaDataId]?.value === 'Seen') return;
|
|
116
|
+
|
|
117
|
+
// Get linked activities
|
|
118
|
+
const linked = await client.request('v3.activity.linkedFrom.overview', [activityId]);
|
|
119
|
+
|
|
120
|
+
// Move matching activities
|
|
121
|
+
for (const process of trigger.linkedProcesses) {
|
|
122
|
+
const toMove = linked.filter(a =>
|
|
123
|
+
a.processId === process.processId &&
|
|
124
|
+
process.sourcePhases.includes(a.phaseId)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (toMove.length > 0) {
|
|
128
|
+
await client.request('v3.activity.updateMany', [
|
|
129
|
+
toMove.map(a => a._id),
|
|
130
|
+
{ phaseId: process.targetPhase }
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Mark as processed
|
|
136
|
+
await client.request('v3.activity.update', [activityId, {
|
|
137
|
+
fields: { [trigger.metaDataId]: 'Seen' }
|
|
138
|
+
}]);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 2. Webhook Handler Pattern
|
|
145
|
+
|
|
146
|
+
HTTP endpoints that trigger Hailer operations.
|
|
147
|
+
|
|
148
|
+
### Express Setup
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import express from 'express';
|
|
152
|
+
import crypto from 'crypto';
|
|
153
|
+
|
|
154
|
+
const app = express();
|
|
155
|
+
app.use(express.json());
|
|
156
|
+
|
|
157
|
+
// Webhook signature validation
|
|
158
|
+
function validateSignature(req: express.Request, secret: string): boolean {
|
|
159
|
+
const signature = req.headers['x-webhook-signature'];
|
|
160
|
+
const payload = JSON.stringify(req.body);
|
|
161
|
+
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
|
|
162
|
+
return signature === expected;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Webhook endpoint
|
|
166
|
+
app.post('/api/v1/webhook/:type', async (req, res) => {
|
|
167
|
+
const { type } = req.params;
|
|
168
|
+
|
|
169
|
+
if (!validateSignature(req, config.webhookSecret)) {
|
|
170
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
switch (type) {
|
|
175
|
+
case 'order.created':
|
|
176
|
+
await handleOrderCreated(req.body);
|
|
177
|
+
break;
|
|
178
|
+
case 'order.updated':
|
|
179
|
+
await handleOrderUpdated(req.body);
|
|
180
|
+
break;
|
|
181
|
+
default:
|
|
182
|
+
logger.warn({ type }, 'Unknown webhook type');
|
|
183
|
+
}
|
|
184
|
+
res.json({ status: 'ok' });
|
|
185
|
+
} catch (error) {
|
|
186
|
+
logger.error({ error, type }, 'Webhook processing failed');
|
|
187
|
+
res.status(500).json({ error: 'Processing failed' });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Webhook Handler Example
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
async function handleOrderCreated(payload: OrderPayload) {
|
|
196
|
+
// Map external data to Hailer fields
|
|
197
|
+
const fields = {
|
|
198
|
+
[FieldIds.external_id]: payload.orderId,
|
|
199
|
+
[FieldIds.customer_name]: payload.customer.name,
|
|
200
|
+
[FieldIds.total_amount]: payload.total,
|
|
201
|
+
[FieldIds.order_date]: new Date(payload.createdAt).getTime()
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Create activity in Hailer
|
|
205
|
+
const result = await hailerClient.createActivity(
|
|
206
|
+
WorkflowIds.orders,
|
|
207
|
+
PhaseIds.new,
|
|
208
|
+
fields
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
logger.info({ orderId: payload.orderId, activityId: result._id }, 'Order created in Hailer');
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 3. SCIM 2.0 Pattern
|
|
218
|
+
|
|
219
|
+
User provisioning from identity providers (Microsoft Entra, Okta).
|
|
220
|
+
|
|
221
|
+
### Endpoints
|
|
222
|
+
|
|
223
|
+
| Method | Path | Description |
|
|
224
|
+
|--------|------|-------------|
|
|
225
|
+
| POST | /scim/v2/Users | Create user |
|
|
226
|
+
| GET | /scim/v2/Users/:id | Get user |
|
|
227
|
+
| PATCH | /scim/v2/Users/:id | Update user |
|
|
228
|
+
| DELETE | /scim/v2/Users/:id | Deactivate user |
|
|
229
|
+
| GET | /scim/v2/Users | List/filter users |
|
|
230
|
+
|
|
231
|
+
### User Mapping
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
interface ScimUser {
|
|
235
|
+
schemas: string[];
|
|
236
|
+
userName: string;
|
|
237
|
+
name: { givenName: string; familyName: string };
|
|
238
|
+
emails: { value: string; primary: boolean }[];
|
|
239
|
+
active: boolean;
|
|
240
|
+
roles?: { value: string }[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function mapScimToHailer(scim: ScimUser): HailerUserPayload {
|
|
244
|
+
return {
|
|
245
|
+
email: scim.emails.find(e => e.primary)?.value || scim.userName,
|
|
246
|
+
firstName: scim.name.givenName,
|
|
247
|
+
lastName: scim.name.familyName,
|
|
248
|
+
active: scim.active,
|
|
249
|
+
teams: mapRolesToTeams(scim.roles)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function mapRolesToTeams(roles?: { value: string }[]): string[] {
|
|
254
|
+
const teamMapping: Record<string, string> = {
|
|
255
|
+
'admin': TeamIds.admins,
|
|
256
|
+
'manager': TeamIds.managers,
|
|
257
|
+
'user': TeamIds.users
|
|
258
|
+
};
|
|
259
|
+
return (roles || [])
|
|
260
|
+
.map(r => teamMapping[r.value])
|
|
261
|
+
.filter(Boolean);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### SCIM Response Format
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
function toScimUser(hailerUser: HailerUser, baseUrl: string): ScimUser {
|
|
269
|
+
return {
|
|
270
|
+
schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'],
|
|
271
|
+
id: hailerUser._id,
|
|
272
|
+
userName: hailerUser.email,
|
|
273
|
+
name: {
|
|
274
|
+
givenName: hailerUser.firstName,
|
|
275
|
+
familyName: hailerUser.lastName
|
|
276
|
+
},
|
|
277
|
+
emails: [{ value: hailerUser.email, primary: true }],
|
|
278
|
+
active: hailerUser.active,
|
|
279
|
+
meta: {
|
|
280
|
+
resourceType: 'User',
|
|
281
|
+
location: `${baseUrl}/scim/v2/Users/${hailerUser._id}`
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 4. Kafka Consumer Pattern
|
|
290
|
+
|
|
291
|
+
Event-driven processing from message queues.
|
|
292
|
+
|
|
293
|
+
### Consumer Setup
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { Kafka, Consumer, EachMessagePayload } from 'kafkajs';
|
|
297
|
+
|
|
298
|
+
const kafka = new Kafka({
|
|
299
|
+
clientId: 'hailer-integration',
|
|
300
|
+
brokers: config.kafkaBrokers,
|
|
301
|
+
ssl: {
|
|
302
|
+
ca: [fs.readFileSync(config.caCertPath)],
|
|
303
|
+
key: fs.readFileSync(config.keyPath),
|
|
304
|
+
cert: fs.readFileSync(config.certPath)
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const consumer = kafka.consumer({ groupId: 'hailer-consumer-group' });
|
|
309
|
+
|
|
310
|
+
async function startConsumer() {
|
|
311
|
+
await consumer.connect();
|
|
312
|
+
await consumer.subscribe({ topic: 'events', fromBeginning: false });
|
|
313
|
+
|
|
314
|
+
await consumer.run({
|
|
315
|
+
eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
|
|
316
|
+
const event = JSON.parse(message.value.toString());
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
await processEvent(event);
|
|
320
|
+
logger.info({ eventId: event.id, offset: message.offset }, 'Event processed');
|
|
321
|
+
} catch (error) {
|
|
322
|
+
logger.error({ error, event }, 'Event processing failed');
|
|
323
|
+
await sendToDeadLetterQueue(message);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Event Processing
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
async function processEvent(event: ExternalEvent) {
|
|
334
|
+
switch (event.type) {
|
|
335
|
+
case 'student.created':
|
|
336
|
+
await createStudent(event.data);
|
|
337
|
+
break;
|
|
338
|
+
case 'student.updated':
|
|
339
|
+
await updateStudent(event.data);
|
|
340
|
+
break;
|
|
341
|
+
case 'student.deleted':
|
|
342
|
+
await deactivateStudent(event.data);
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
logger.warn({ eventType: event.type }, 'Unknown event type');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Common Patterns
|
|
353
|
+
|
|
354
|
+
### Retry with Exponential Backoff
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
async function withRetry<T>(
|
|
358
|
+
fn: () => Promise<T>,
|
|
359
|
+
maxRetries: number = 3,
|
|
360
|
+
baseDelay: number = 1000
|
|
361
|
+
): Promise<T> {
|
|
362
|
+
let lastError: Error;
|
|
363
|
+
|
|
364
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
365
|
+
try {
|
|
366
|
+
return await fn();
|
|
367
|
+
} catch (error) {
|
|
368
|
+
lastError = error;
|
|
369
|
+
const delay = baseDelay * Math.pow(2, i);
|
|
370
|
+
logger.warn({ attempt: i + 1, delay, error: error.message }, 'Retrying...');
|
|
371
|
+
await sleep(delay);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
throw lastError;
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Graceful Shutdown
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
async function gracefulShutdown(signal: string) {
|
|
383
|
+
logger.info({ signal }, 'Shutdown signal received');
|
|
384
|
+
|
|
385
|
+
// Stop accepting new requests
|
|
386
|
+
server.close();
|
|
387
|
+
|
|
388
|
+
// Disconnect from Hailer
|
|
389
|
+
await hailerClient.disconnect();
|
|
390
|
+
|
|
391
|
+
// Disconnect from Kafka
|
|
392
|
+
await consumer.disconnect();
|
|
393
|
+
|
|
394
|
+
logger.info('Graceful shutdown complete');
|
|
395
|
+
process.exit(0);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
399
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Health Check
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
app.get('/api/v1/health', async (req, res) => {
|
|
406
|
+
const checks = {
|
|
407
|
+
hailer: await checkHailerConnection(),
|
|
408
|
+
kafka: await checkKafkaConnection(),
|
|
409
|
+
uptime: process.uptime(),
|
|
410
|
+
memory: process.memoryUsage(),
|
|
411
|
+
lastActivity: lastActivityTimestamp
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const healthy = checks.hailer.connected && checks.kafka.connected;
|
|
415
|
+
res.status(healthy ? 200 : 503).json({
|
|
416
|
+
status: healthy ? 'healthy' : 'unhealthy',
|
|
417
|
+
timestamp: new Date().toISOString(),
|
|
418
|
+
checks
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
```
|
|
@@ -1,21 +1,65 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: json-only-output
|
|
3
3
|
description: Fix agents adding prose after JSON responses
|
|
4
|
+
version: 1.0.1
|
|
4
5
|
triggers: Agent outputs explanation text after valid JSON
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
# JSON Only Output
|
|
9
|
+
|
|
10
|
+
<purpose>
|
|
11
|
+
Ensure agents output ONLY valid JSON with no prose, explanations, or commentary after the closing brace.
|
|
12
|
+
</purpose>
|
|
13
|
+
|
|
14
|
+
<why-this-happens>
|
|
15
|
+
## Why Agents Add Prose
|
|
16
|
+
|
|
17
|
+
LLMs have a natural tendency to be helpful and explanatory. After completing a task, they want to:
|
|
18
|
+
- Explain what they did
|
|
19
|
+
- Suggest next steps
|
|
20
|
+
- Provide context
|
|
21
|
+
- Confirm their understanding
|
|
22
|
+
|
|
23
|
+
This is normally good behavior, but for **subagents** returning structured data to an orchestrator, it breaks JSON parsing.
|
|
24
|
+
|
|
25
|
+
**The `<protocol>` section isn't enough** because:
|
|
26
|
+
- LLMs treat protocol as "guidelines" not hard rules
|
|
27
|
+
- The helpful instinct overrides weak instructions
|
|
28
|
+
- Protocol is at the end of the prompt, less salient
|
|
29
|
+
|
|
30
|
+
**Why identity/rules work:**
|
|
31
|
+
- `<identity>` shapes the agent's core persona ("I output JSON. Full stop.")
|
|
32
|
+
- `<rules>` are processed as constraints, not suggestions
|
|
33
|
+
- Positioned early in prompt, higher salience
|
|
34
|
+
- Explicit prohibition is stronger than implicit expectation
|
|
35
|
+
</why-this-happens>
|
|
36
|
+
|
|
37
|
+
<patterns>
|
|
38
|
+
## Pattern 1: Stop at the Closing Brace
|
|
39
|
+
|
|
40
|
+
Output JSON. Full stop. Nothing after the closing brace.
|
|
41
|
+
|
|
42
|
+
## Pattern 2: Agent Configuration Fix
|
|
43
|
+
|
|
44
|
+
1. Add to `<identity>`: "Output JSON. Full stop."
|
|
45
|
+
2. Add to `<rules>`: **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
|
|
46
|
+
3. Protocol section is NOT enough - agents ignore it without identity/rules reinforcement.
|
|
47
|
+
|
|
48
|
+
## Pattern 3: Include Summary Inside JSON
|
|
49
|
+
|
|
50
|
+
If context is helpful, include it IN the JSON `summary` field, not after the JSON.
|
|
51
|
+
</patterns>
|
|
52
|
+
|
|
53
|
+
<examples>
|
|
54
|
+
## Example 1: Correct - Pure JSON Output
|
|
10
55
|
|
|
11
|
-
<correct>
|
|
12
56
|
```json
|
|
13
57
|
{"status":"success","result":{"fields":["taskName","priority"]},"summary":"Read 2 fields"}
|
|
14
58
|
```
|
|
15
59
|
**STOP HERE. Nothing after closing brace.**
|
|
16
|
-
</correct>
|
|
17
60
|
|
|
18
|
-
|
|
61
|
+
## Example 2: Wrong - Prose After JSON
|
|
62
|
+
|
|
19
63
|
```json
|
|
20
64
|
{"status":"success","result":{"fields":["taskName","priority"]},"summary":"Read 2 fields"}
|
|
21
65
|
```
|
|
@@ -23,10 +67,6 @@ Agents violate JSON-only protocol by adding prose explanations AFTER their JSON
|
|
|
23
67
|
The workflow has 2 fields defined in workspace/Tasks_123/fields.ts. You can now use these field IDs with dmitri for activity creation.
|
|
24
68
|
|
|
25
69
|
**Action Required**: Run `npm run pull` to refresh.
|
|
26
|
-
</wrong>
|
|
27
70
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
2. Add to `<rules>`: **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
|
|
31
|
-
3. Protocol section is NOT enough - agents ignore it without identity/rules reinforcement.
|
|
32
|
-
</fix>
|
|
71
|
+
This violates JSON-only protocol by adding explanation text AFTER the valid JSON response.
|
|
72
|
+
</examples>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lsp-setup
|
|
3
|
+
description: How to set up LSP (Language Server Protocol) for Claude Code - enables code intelligence for Lars and other agents
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
triggers:
|
|
6
|
+
- LSP not available
|
|
7
|
+
- typescript-language-server
|
|
8
|
+
- code intelligence setup
|
|
9
|
+
- Lars can't find unused code
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# LSP Setup for Claude Code
|
|
13
|
+
|
|
14
|
+
LSP (Language Server Protocol) enables code intelligence features like go-to-definition, find-references, and accurate dead code detection.
|
|
15
|
+
|
|
16
|
+
## Why LSP?
|
|
17
|
+
|
|
18
|
+
Without LSP, agents like Lars must guess about code usage via grep/file reads. With LSP:
|
|
19
|
+
- **Accurate dead code detection** - knows if exports are used elsewhere
|
|
20
|
+
- **Type error detection** - without running compiler
|
|
21
|
+
- **Find all references** - across entire codebase
|
|
22
|
+
- **Go to definition** - jump to where symbols are defined
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
Three things needed for LSP to work:
|
|
27
|
+
|
|
28
|
+
| Requirement | How to Check | How to Install |
|
|
29
|
+
|-------------|--------------|----------------|
|
|
30
|
+
| `ENABLE_LSP_TOOL=1` | Check `.claude/settings.json` | Add to env section |
|
|
31
|
+
| `typescript-lsp` plugin | `claude plugin list` | `claude plugin install typescript-lsp@claude-plugins-official` |
|
|
32
|
+
| Language server binary | `which typescript-language-server` | `npm install -g typescript-language-server typescript` |
|
|
33
|
+
|
|
34
|
+
## Setup Steps
|
|
35
|
+
|
|
36
|
+
### Step 1: Install the language server binary
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g typescript-language-server typescript
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Verify:
|
|
43
|
+
```bash
|
|
44
|
+
typescript-language-server --version
|
|
45
|
+
# Should show version like: 5.1.3
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 2: Install Claude Code plugin
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude plugin install typescript-lsp@claude-plugins-official
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Verify:
|
|
55
|
+
```bash
|
|
56
|
+
claude plugin list
|
|
57
|
+
# Should show: typescript-lsp@claude-plugins-official ✔ enabled
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step 3: Ensure settings.json has LSP enabled
|
|
61
|
+
|
|
62
|
+
Check `.claude/settings.json` (project) or `~/.claude/settings.json` (global):
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"env": {
|
|
67
|
+
"ENABLE_LSP_TOOL": "1"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 4: Restart Claude
|
|
73
|
+
|
|
74
|
+
LSP plugin activates on Claude restart:
|
|
75
|
+
```bash
|
|
76
|
+
claude -c
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step 5: Test
|
|
80
|
+
|
|
81
|
+
Ask Lars to find unused code in a TypeScript file:
|
|
82
|
+
```
|
|
83
|
+
Find unused code in src/App.tsx
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or test LSP directly:
|
|
87
|
+
```
|
|
88
|
+
Use LSP documentSymbol on src/App.tsx
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Supported File Types
|
|
92
|
+
|
|
93
|
+
The `typescript-lsp` plugin supports:
|
|
94
|
+
- `.ts`, `.tsx` - TypeScript + React
|
|
95
|
+
- `.js`, `.jsx` - JavaScript + React
|
|
96
|
+
- `.mts`, `.cts` - TypeScript ES modules / CommonJS
|
|
97
|
+
- `.mjs`, `.cjs` - JavaScript ES modules / CommonJS
|
|
98
|
+
|
|
99
|
+
## LSP Operations Available
|
|
100
|
+
|
|
101
|
+
| Operation | Purpose |
|
|
102
|
+
|-----------|---------|
|
|
103
|
+
| `documentSymbol` | List all symbols in a file |
|
|
104
|
+
| `findReferences` | Find all usages of a symbol |
|
|
105
|
+
| `goToDefinition` | Jump to where symbol is defined |
|
|
106
|
+
| `hover` | Get type info and documentation |
|
|
107
|
+
| `goToImplementation` | Find implementations of interface |
|
|
108
|
+
| `incomingCalls` | What calls this function |
|
|
109
|
+
| `outgoingCalls` | What this function calls |
|
|
110
|
+
| `prepareCallHierarchy` | Get call hierarchy at position |
|
|
111
|
+
|
|
112
|
+
## Agents That Use LSP
|
|
113
|
+
|
|
114
|
+
| Agent | How They Use LSP |
|
|
115
|
+
|-------|------------------|
|
|
116
|
+
| **Lars** | Primary user - dead code, unused imports, type errors |
|
|
117
|
+
| **Svetlana** | Type checking during code review (optional) |
|
|
118
|
+
| **Giuseppe** | Verify code compiles before build (optional) |
|
|
119
|
+
|
|
120
|
+
## Troubleshooting
|
|
121
|
+
|
|
122
|
+
### "No LSP server available"
|
|
123
|
+
|
|
124
|
+
1. Check binary is installed: `which typescript-language-server`
|
|
125
|
+
2. Check plugin is installed: `claude plugin list`
|
|
126
|
+
3. Restart Claude: `claude -c`
|
|
127
|
+
|
|
128
|
+
### LSP times out on first query
|
|
129
|
+
|
|
130
|
+
Normal - language server is indexing the project. Wait a few seconds and retry.
|
|
131
|
+
|
|
132
|
+
### LSP works for .ts but not .tsx
|
|
133
|
+
|
|
134
|
+
Check `typescript` is installed alongside `typescript-language-server`:
|
|
135
|
+
```bash
|
|
136
|
+
npm install -g typescript-language-server typescript
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### "Executable not found in $PATH"
|
|
140
|
+
|
|
141
|
+
The binary isn't in your PATH. Either:
|
|
142
|
+
- Install globally: `npm install -g typescript-language-server`
|
|
143
|
+
- Or add npm global bin to PATH: `export PATH="$PATH:$(npm bin -g)"`
|
|
144
|
+
|
|
145
|
+
## Without LSP (Fallback)
|
|
146
|
+
|
|
147
|
+
If LSP isn't available, agents can fall back to:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Type errors
|
|
151
|
+
npx tsc --noEmit 2>&1
|
|
152
|
+
|
|
153
|
+
# Unused variables (requires eslint config)
|
|
154
|
+
npx eslint --rule '@typescript-eslint/no-unused-vars: error' src/
|
|
155
|
+
|
|
156
|
+
# Find references (grep-based, less accurate)
|
|
157
|
+
grep -r "functionName" src/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
This is less accurate but works without setup.
|