@amplis/mcp-server 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3758 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/index.ts
9
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import {
12
+ CallToolRequestSchema,
13
+ GetPromptRequestSchema,
14
+ ListPromptsRequestSchema,
15
+ ListResourcesRequestSchema,
16
+ ListResourceTemplatesRequestSchema,
17
+ ListToolsRequestSchema,
18
+ ReadResourceRequestSchema
19
+ } from "@modelcontextprotocol/sdk/types.js";
20
+
21
+ // src/lib/errors.ts
22
+ import { ZodError } from "zod";
23
+ var MCPNotFoundError = class extends Error {
24
+ constructor(entity, id) {
25
+ super(`${entity} ${id} not found`);
26
+ this.name = "MCPNotFoundError";
27
+ }
28
+ };
29
+ var MCPPermissionError = class extends Error {
30
+ constructor(action) {
31
+ super(`Permission denied: ${action}`);
32
+ this.name = "MCPPermissionError";
33
+ }
34
+ };
35
+ var MCPValidationError = class extends Error {
36
+ constructor(message) {
37
+ super(`Validation error: ${message}`);
38
+ this.name = "MCPValidationError";
39
+ }
40
+ };
41
+ var MCPAuthError = class extends Error {
42
+ constructor(message) {
43
+ super(`Authentication error: ${message}`);
44
+ this.name = "MCPAuthError";
45
+ }
46
+ };
47
+ var SENSITIVE_PATTERNS = [
48
+ /prisma/i,
49
+ /P\d{4}/,
50
+ // Prisma error codes (P2002, P2025, etc.)
51
+ /postgresql?:\/\//i,
52
+ // Connection strings
53
+ /at .*\.ts:\d+/,
54
+ // Stack trace lines
55
+ /FATAL|PANIC/,
56
+ // Postgres fatal errors
57
+ /relation ".*" does not exist/i,
58
+ // Table/relation names
59
+ /column ".*" of relation/i,
60
+ // Column constraint details
61
+ /unique constraint/i,
62
+ // Constraint names
63
+ /foreign key constraint/i,
64
+ // FK details
65
+ /violates .*constraint/i
66
+ // Generic constraint violations
67
+ ];
68
+ var MAX_ERROR_LENGTH = 500;
69
+ function sanitizeError(err) {
70
+ if (err instanceof MCPNotFoundError || err instanceof MCPPermissionError || err instanceof MCPValidationError || err instanceof MCPAuthError) {
71
+ return err.message;
72
+ }
73
+ if (err instanceof ZodError) {
74
+ const issues = err.issues.slice(0, 5).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
75
+ return `Validation error: ${issues}`;
76
+ }
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ for (const pattern of SENSITIVE_PATTERNS) {
79
+ if (pattern.test(message)) {
80
+ return "An internal error occurred. Please try again or contact support.";
81
+ }
82
+ }
83
+ if (message.length > MAX_ERROR_LENGTH) {
84
+ return message.slice(0, MAX_ERROR_LENGTH) + "...";
85
+ }
86
+ return message;
87
+ }
88
+
89
+ // src/resources/projects.ts
90
+ var projects_exports = {};
91
+ __export(projects_exports, {
92
+ handleRead: () => handleRead,
93
+ resourceTemplates: () => resourceTemplates,
94
+ staticResources: () => staticResources
95
+ });
96
+
97
+ // src/lib/credentials.ts
98
+ import fs from "fs";
99
+ import path from "path";
100
+ import os from "os";
101
+ var CREDENTIALS_DIR = path.join(os.homedir(), ".amplis");
102
+ var CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, "credentials.json");
103
+ function saveCredentials(data) {
104
+ if (!fs.existsSync(CREDENTIALS_DIR)) {
105
+ fs.mkdirSync(CREDENTIALS_DIR, { mode: 448, recursive: true });
106
+ }
107
+ fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2), {
108
+ mode: 384,
109
+ encoding: "utf-8"
110
+ });
111
+ }
112
+ function loadCredentials() {
113
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
114
+ return null;
115
+ }
116
+ if (process.platform !== "win32") {
117
+ try {
118
+ const stat = fs.statSync(CREDENTIALS_FILE);
119
+ const mode = stat.mode & 511;
120
+ if (mode & 63) {
121
+ console.error(
122
+ `WARNING: ${CREDENTIALS_FILE} has permissions ${mode.toString(8)}. It should be 600 (owner read/write only). Run: chmod 600 ${CREDENTIALS_FILE}`
123
+ );
124
+ }
125
+ } catch {
126
+ }
127
+ }
128
+ try {
129
+ const raw = fs.readFileSync(CREDENTIALS_FILE, "utf-8");
130
+ return JSON.parse(raw);
131
+ } catch {
132
+ return null;
133
+ }
134
+ }
135
+ function clearCredentials() {
136
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
137
+ return false;
138
+ }
139
+ fs.unlinkSync(CREDENTIALS_FILE);
140
+ return true;
141
+ }
142
+ function getCredentialsPath() {
143
+ return CREDENTIALS_FILE;
144
+ }
145
+
146
+ // src/lib/api-client.ts
147
+ var ApiError = class extends Error {
148
+ constructor(message, status, code, details) {
149
+ super(message);
150
+ this.status = status;
151
+ this.code = code;
152
+ this.details = details;
153
+ this.name = "ApiError";
154
+ }
155
+ };
156
+ var cachedConfig = null;
157
+ function getApiConfig() {
158
+ if (cachedConfig) return cachedConfig;
159
+ if (process.env.AMPLIS_API_URL && process.env.AMPLIS_API_KEY && process.env.AMPLIS_ORG_ID) {
160
+ cachedConfig = {
161
+ baseUrl: process.env.AMPLIS_API_URL,
162
+ apiKey: process.env.AMPLIS_API_KEY,
163
+ orgId: process.env.AMPLIS_ORG_ID
164
+ };
165
+ return cachedConfig;
166
+ }
167
+ const creds = loadCredentials();
168
+ if (!creds) {
169
+ throw new Error(
170
+ "No credentials found. Run `amplis-mcp login` to authenticate."
171
+ );
172
+ }
173
+ if (!creds.apiKey) {
174
+ throw new Error(
175
+ "No API key in credentials. Run `amplis-mcp login` to re-authenticate."
176
+ );
177
+ }
178
+ cachedConfig = {
179
+ baseUrl: process.env.AMPLIS_API_URL || "https://app.amplis.com.au",
180
+ apiKey: creds.apiKey,
181
+ orgId: creds.orgId
182
+ };
183
+ return cachedConfig;
184
+ }
185
+ async function mcpFetch(endpoint, options = {}) {
186
+ const config = getApiConfig();
187
+ const url = `${config.baseUrl}/api/mcp/v1${endpoint}`;
188
+ const response = await fetch(url, {
189
+ ...options,
190
+ headers: {
191
+ "Authorization": `Bearer ${config.apiKey}`,
192
+ "Content-Type": "application/json",
193
+ "X-MCP-Org-Id": config.orgId,
194
+ ...options.headers
195
+ }
196
+ });
197
+ if (!response.ok) {
198
+ let errorData = {};
199
+ try {
200
+ errorData = await response.json();
201
+ } catch {
202
+ }
203
+ throw new ApiError(
204
+ errorData.message || `API request failed: ${response.status} ${response.statusText}`,
205
+ response.status,
206
+ errorData.code,
207
+ errorData.details
208
+ );
209
+ }
210
+ if (response.status === 204) {
211
+ return {};
212
+ }
213
+ return response.json();
214
+ }
215
+ async function apiGet(endpoint, params) {
216
+ let url = endpoint;
217
+ if (params) {
218
+ const searchParams = new URLSearchParams();
219
+ for (const [key, value] of Object.entries(params)) {
220
+ if (value !== void 0) {
221
+ searchParams.set(key, String(value));
222
+ }
223
+ }
224
+ const queryString = searchParams.toString();
225
+ if (queryString) {
226
+ url = `${endpoint}?${queryString}`;
227
+ }
228
+ }
229
+ return mcpFetch(url, { method: "GET" });
230
+ }
231
+ async function apiPost(endpoint, body) {
232
+ return mcpFetch(endpoint, {
233
+ method: "POST",
234
+ body: body ? JSON.stringify(body) : void 0
235
+ });
236
+ }
237
+ async function apiPatch(endpoint, body) {
238
+ return mcpFetch(endpoint, {
239
+ method: "PATCH",
240
+ body: body ? JSON.stringify(body) : void 0
241
+ });
242
+ }
243
+
244
+ // src/lib/response.ts
245
+ function success(data) {
246
+ return {
247
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
248
+ };
249
+ }
250
+ function error(message) {
251
+ return {
252
+ content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }],
253
+ isError: true
254
+ };
255
+ }
256
+ function safeError(err) {
257
+ return error(sanitizeError(err));
258
+ }
259
+ function resourceContent(uri, data) {
260
+ return {
261
+ contents: [
262
+ { uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }
263
+ ]
264
+ };
265
+ }
266
+
267
+ // src/resources/projects.ts
268
+ var staticResources = [
269
+ { uri: "amplis://projects", name: "Projects", description: "List all projects with status, dates, and client", mimeType: "application/json" }
270
+ ];
271
+ var resourceTemplates = [
272
+ { uriTemplate: "amplis://projects/{projectId}", name: "Project Detail", description: "Detailed project view with tasks, team, and deliverables", mimeType: "application/json" }
273
+ ];
274
+ async function handleProjectsList() {
275
+ const result = await apiGet("/resources/projects");
276
+ return result.projects;
277
+ }
278
+ async function handleProjectDetail(projectId) {
279
+ return await apiGet(`/resources/projects/${projectId}`);
280
+ }
281
+ async function handleRead(uri) {
282
+ if (uri === "amplis://projects") return resourceContent(uri, await handleProjectsList());
283
+ const match = uri.match(/^amplis:\/\/projects\/([^/]+)$/);
284
+ if (match) return resourceContent(uri, await handleProjectDetail(match[1]));
285
+ return null;
286
+ }
287
+
288
+ // src/resources/pipeline.ts
289
+ var pipeline_exports = {};
290
+ __export(pipeline_exports, {
291
+ handleRead: () => handleRead2,
292
+ resourceTemplates: () => resourceTemplates2,
293
+ staticResources: () => staticResources2
294
+ });
295
+ var staticResources2 = [
296
+ {
297
+ uri: "amplis://pipeline",
298
+ name: "Pipeline",
299
+ description: "Sales pipeline stages with project counts and values",
300
+ mimeType: "application/json"
301
+ }
302
+ ];
303
+ var resourceTemplates2 = [];
304
+ async function handlePipelineStages() {
305
+ const result = await apiGet("/resources/pipeline");
306
+ return result;
307
+ }
308
+ async function handleRead2(uri) {
309
+ if (uri === "amplis://pipeline") {
310
+ return resourceContent(uri, await handlePipelineStages());
311
+ }
312
+ return null;
313
+ }
314
+
315
+ // src/resources/alerts.ts
316
+ var alerts_exports = {};
317
+ __export(alerts_exports, {
318
+ handleRead: () => handleRead3,
319
+ resourceTemplates: () => resourceTemplates3,
320
+ staticResources: () => staticResources3
321
+ });
322
+ var staticResources3 = [
323
+ { uri: "amplis://alerts", name: "Alerts", description: "Active alerts requiring attention", mimeType: "application/json" }
324
+ ];
325
+ var resourceTemplates3 = [];
326
+ async function handleActiveAlerts() {
327
+ const result = await apiGet("/resources/alerts");
328
+ return result.alerts;
329
+ }
330
+ async function handleRead3(uri) {
331
+ if (uri === "amplis://alerts") return resourceContent(uri, await handleActiveAlerts());
332
+ return null;
333
+ }
334
+
335
+ // src/resources/utilization.ts
336
+ var utilization_exports = {};
337
+ __export(utilization_exports, {
338
+ handleRead: () => handleRead4,
339
+ resourceTemplates: () => resourceTemplates4,
340
+ staticResources: () => staticResources4
341
+ });
342
+ var staticResources4 = [
343
+ {
344
+ uri: "amplis://team/utilization",
345
+ name: "Team Utilization",
346
+ description: "Team members with allocated hours and timesheet totals for current week",
347
+ mimeType: "application/json"
348
+ }
349
+ ];
350
+ var resourceTemplates4 = [];
351
+ async function handleTeamUtilization() {
352
+ const result = await apiGet("/resources/team/utilization");
353
+ return result.team;
354
+ }
355
+ async function handleRead4(uri) {
356
+ if (uri === "amplis://team/utilization") {
357
+ return resourceContent(uri, await handleTeamUtilization());
358
+ }
359
+ return null;
360
+ }
361
+
362
+ // src/resources/memory.ts
363
+ var memory_exports = {};
364
+ __export(memory_exports, {
365
+ handleRead: () => handleRead5,
366
+ resourceTemplates: () => resourceTemplates5,
367
+ staticResources: () => staticResources5
368
+ });
369
+ var staticResources5 = [
370
+ {
371
+ uri: "decisions://org",
372
+ name: "Organization Decisions",
373
+ description: "Recent decisions across the organization",
374
+ mimeType: "application/json"
375
+ }
376
+ ];
377
+ var resourceTemplates5 = [
378
+ {
379
+ uriTemplate: "context://project/{projectId}",
380
+ name: "Project Context",
381
+ description: "Full project context including memories, recent decisions, and risks",
382
+ mimeType: "application/json"
383
+ },
384
+ {
385
+ uriTemplate: "context://client/{clientId}",
386
+ name: "Client Context",
387
+ description: "Full client context including preferences, relationship history, and projects",
388
+ mimeType: "application/json"
389
+ },
390
+ {
391
+ uriTemplate: "decisions://project/{projectId}",
392
+ name: "Project Decisions",
393
+ description: "Decision history for a project",
394
+ mimeType: "application/json"
395
+ },
396
+ {
397
+ uriTemplate: "meetings://project/{projectId}",
398
+ name: "Project Meetings",
399
+ description: "Meeting notes for a project",
400
+ mimeType: "application/json"
401
+ },
402
+ {
403
+ uriTemplate: "memories://search?q={query}",
404
+ name: "Memory Search",
405
+ description: "Search all memories semantically",
406
+ mimeType: "application/json"
407
+ }
408
+ ];
409
+ async function handleProjectContext(projectId) {
410
+ const result = await apiGet(`/resources/context/project/${projectId}`);
411
+ return result;
412
+ }
413
+ async function handleClientContext(clientId) {
414
+ const result = await apiGet(`/resources/context/client/${clientId}`);
415
+ return result;
416
+ }
417
+ async function handleProjectDecisions(projectId) {
418
+ const result = await apiGet(`/resources/decisions/project/${projectId}`);
419
+ return result;
420
+ }
421
+ async function handleOrgDecisions() {
422
+ const result = await apiGet("/resources/decisions/org");
423
+ return result;
424
+ }
425
+ async function handleProjectMeetings(projectId) {
426
+ const result = await apiGet(`/resources/meetings/project/${projectId}`);
427
+ return result;
428
+ }
429
+ async function handleMemorySearch(query) {
430
+ const result = await apiGet("/resources/memories/search", { q: query });
431
+ return result;
432
+ }
433
+ async function handleRead5(uri) {
434
+ const projectContextMatch = uri.match(/^context:\/\/project\/(.+)$/);
435
+ if (projectContextMatch) {
436
+ return resourceContent(uri, await handleProjectContext(projectContextMatch[1]));
437
+ }
438
+ const clientContextMatch = uri.match(/^context:\/\/client\/(.+)$/);
439
+ if (clientContextMatch) {
440
+ return resourceContent(uri, await handleClientContext(clientContextMatch[1]));
441
+ }
442
+ const projectDecisionsMatch = uri.match(/^decisions:\/\/project\/(.+)$/);
443
+ if (projectDecisionsMatch) {
444
+ return resourceContent(uri, await handleProjectDecisions(projectDecisionsMatch[1]));
445
+ }
446
+ if (uri === "decisions://org") {
447
+ return resourceContent(uri, await handleOrgDecisions());
448
+ }
449
+ const projectMeetingsMatch = uri.match(/^meetings:\/\/project\/(.+)$/);
450
+ if (projectMeetingsMatch) {
451
+ return resourceContent(uri, await handleProjectMeetings(projectMeetingsMatch[1]));
452
+ }
453
+ const memorySearchMatch = uri.match(/^memories:\/\/search\?q=(.+)$/);
454
+ if (memorySearchMatch) {
455
+ const query = decodeURIComponent(memorySearchMatch[1]);
456
+ return resourceContent(uri, await handleMemorySearch(query));
457
+ }
458
+ return null;
459
+ }
460
+
461
+ // src/resources/clients.ts
462
+ var clients_exports = {};
463
+ __export(clients_exports, {
464
+ handleRead: () => handleRead6,
465
+ resourceTemplates: () => resourceTemplates6,
466
+ staticResources: () => staticResources6
467
+ });
468
+ var staticResources6 = [
469
+ { uri: "amplis://clients", name: "Clients", description: "List all clients", mimeType: "application/json" }
470
+ ];
471
+ var resourceTemplates6 = [
472
+ { uriTemplate: "amplis://clients/{clientId}", name: "Client Detail", description: "Detailed client view", mimeType: "application/json" }
473
+ ];
474
+ async function handleClientsList() {
475
+ const result = await apiGet("/resources/clients");
476
+ return result.clients;
477
+ }
478
+ async function handleClientDetail(clientId) {
479
+ return await apiGet(`/resources/clients/${clientId}`);
480
+ }
481
+ async function handleRead6(uri) {
482
+ if (uri === "amplis://clients") return resourceContent(uri, await handleClientsList());
483
+ const match = uri.match(/^amplis:\/\/clients\/([^/]+)$/);
484
+ if (match) return resourceContent(uri, await handleClientDetail(match[1]));
485
+ return null;
486
+ }
487
+
488
+ // src/resources/people.ts
489
+ var people_exports = {};
490
+ __export(people_exports, {
491
+ handleRead: () => handleRead7,
492
+ resourceTemplates: () => resourceTemplates7,
493
+ staticResources: () => staticResources7
494
+ });
495
+ var staticResources7 = [
496
+ { uri: "amplis://people", name: "People", description: "List all team members", mimeType: "application/json" }
497
+ ];
498
+ var resourceTemplates7 = [
499
+ { uriTemplate: "amplis://people/{personId}", name: "Person Detail", description: "Detailed person view", mimeType: "application/json" }
500
+ ];
501
+ async function handlePeopleList() {
502
+ const result = await apiGet("/resources/people");
503
+ return result.people;
504
+ }
505
+ async function handlePersonDetail(personId) {
506
+ return await apiGet(`/resources/people/${personId}`);
507
+ }
508
+ async function handleRead7(uri) {
509
+ if (uri === "amplis://people") return resourceContent(uri, await handlePeopleList());
510
+ const match = uri.match(/^amplis:\/\/people\/([^/]+)$/);
511
+ if (match) return resourceContent(uri, await handlePersonDetail(match[1]));
512
+ return null;
513
+ }
514
+
515
+ // src/resources/tasks.ts
516
+ var tasks_exports = {};
517
+ __export(tasks_exports, {
518
+ handleRead: () => handleRead8,
519
+ resourceTemplates: () => resourceTemplates8,
520
+ staticResources: () => staticResources8
521
+ });
522
+ var staticResources8 = [];
523
+ var resourceTemplates8 = [
524
+ { uriTemplate: "amplis://projects/{projectId}/tasks", name: "Project Tasks", description: "All tasks for a project", mimeType: "application/json" }
525
+ ];
526
+ async function handleProjectTasks(projectId) {
527
+ return await apiGet(`/resources/projects/${projectId}/tasks`);
528
+ }
529
+ async function handleRead8(uri) {
530
+ const match = uri.match(/^amplis:\/\/projects\/([^/]+)\/tasks$/);
531
+ if (match) return resourceContent(uri, await handleProjectTasks(match[1]));
532
+ return null;
533
+ }
534
+
535
+ // src/resources/deliverables.ts
536
+ var deliverables_exports = {};
537
+ __export(deliverables_exports, {
538
+ handleRead: () => handleRead9,
539
+ resourceTemplates: () => resourceTemplates9,
540
+ staticResources: () => staticResources9
541
+ });
542
+ var staticResources9 = [];
543
+ var resourceTemplates9 = [
544
+ {
545
+ uriTemplate: "amplis://projects/{projectId}/deliverables",
546
+ name: "Project Deliverables",
547
+ description: "All deliverables for a project with status and progress",
548
+ mimeType: "application/json"
549
+ }
550
+ ];
551
+ async function handleProjectDeliverables(projectId) {
552
+ const result = await apiGet(`/resources/projects/${projectId}/deliverables`);
553
+ return result;
554
+ }
555
+ async function handleRead9(uri) {
556
+ const match = uri.match(/^amplis:\/\/projects\/([^/]+)\/deliverables$/);
557
+ if (match) {
558
+ return resourceContent(uri, await handleProjectDeliverables(match[1]));
559
+ }
560
+ return null;
561
+ }
562
+
563
+ // src/resources/documents.ts
564
+ var documents_exports = {};
565
+ __export(documents_exports, {
566
+ handleRead: () => handleRead10,
567
+ resourceTemplates: () => resourceTemplates10,
568
+ staticResources: () => staticResources10
569
+ });
570
+ var staticResources10 = [
571
+ {
572
+ uri: "amplis://documents",
573
+ name: "Documents",
574
+ description: "List all documents (proposals, reports, resumes)",
575
+ mimeType: "application/json"
576
+ }
577
+ ];
578
+ var resourceTemplates10 = [
579
+ {
580
+ uriTemplate: "amplis://documents/{documentId}",
581
+ name: "Document Detail",
582
+ description: "Document metadata and version history",
583
+ mimeType: "application/json"
584
+ }
585
+ ];
586
+ async function handleDocumentsList() {
587
+ const result = await apiGet("/resources/documents");
588
+ return result.documents;
589
+ }
590
+ async function handleDocumentDetail(documentId) {
591
+ const document = await apiGet(`/resources/documents/${documentId}`);
592
+ return document;
593
+ }
594
+ async function handleRead10(uri) {
595
+ if (uri === "amplis://documents") {
596
+ return resourceContent(uri, await handleDocumentsList());
597
+ }
598
+ const match = uri.match(/^amplis:\/\/documents\/([^/]+)$/);
599
+ if (match) {
600
+ return resourceContent(uri, await handleDocumentDetail(match[1]));
601
+ }
602
+ return null;
603
+ }
604
+
605
+ // src/resources/knowledge.ts
606
+ var knowledge_exports = {};
607
+ __export(knowledge_exports, {
608
+ handleRead: () => handleRead11,
609
+ resourceTemplates: () => resourceTemplates11,
610
+ staticResources: () => staticResources11
611
+ });
612
+ var staticResources11 = [
613
+ {
614
+ uri: "amplis://knowledge/sources",
615
+ name: "Knowledge Base Sources",
616
+ description: "List all knowledge base sources with status and chunk counts",
617
+ mimeType: "application/json"
618
+ }
619
+ ];
620
+ var resourceTemplates11 = [
621
+ {
622
+ uriTemplate: "amplis://knowledge/search?q={query}",
623
+ name: "Knowledge Base Search",
624
+ description: "Semantic search across knowledge base chunks",
625
+ mimeType: "application/json"
626
+ }
627
+ ];
628
+ async function handleKBSourcesList() {
629
+ const result = await apiGet("/resources/knowledge/sources");
630
+ return result.sources;
631
+ }
632
+ async function handleKBSearch(query) {
633
+ const result = await apiGet("/resources/knowledge/search", { q: query });
634
+ return result;
635
+ }
636
+ async function handleRead11(uri) {
637
+ if (uri === "amplis://knowledge/sources") {
638
+ return resourceContent(uri, await handleKBSourcesList());
639
+ }
640
+ const match = uri.match(/^amplis:\/\/knowledge\/search\?q=(.+)$/);
641
+ if (match) {
642
+ const query = decodeURIComponent(match[1]);
643
+ return resourceContent(uri, await handleKBSearch(query));
644
+ }
645
+ return null;
646
+ }
647
+
648
+ // src/resources/credentials.ts
649
+ var credentials_exports = {};
650
+ __export(credentials_exports, {
651
+ handleRead: () => handleRead12,
652
+ resourceTemplates: () => resourceTemplates12,
653
+ staticResources: () => staticResources12
654
+ });
655
+ var staticResources12 = [
656
+ {
657
+ uri: "amplis://credentials",
658
+ name: "Credentials",
659
+ description: "List all credentials across the organization",
660
+ mimeType: "application/json"
661
+ }
662
+ ];
663
+ var resourceTemplates12 = [
664
+ {
665
+ uriTemplate: "amplis://people/{personId}/credentials",
666
+ name: "Person Credentials",
667
+ description: "All credentials for a specific person",
668
+ mimeType: "application/json"
669
+ },
670
+ {
671
+ uriTemplate: "amplis://credentials/expiring?days={days}",
672
+ name: "Expiring Credentials",
673
+ description: "Credentials expiring within specified days",
674
+ mimeType: "application/json"
675
+ }
676
+ ];
677
+ async function handleCredentialsList() {
678
+ const result = await apiGet("/resources/credentials");
679
+ return result.credentials;
680
+ }
681
+ async function handlePersonCredentials(personId) {
682
+ const result = await apiGet(`/resources/people/${personId}/credentials`);
683
+ return result;
684
+ }
685
+ async function handleExpiringCredentials(days) {
686
+ const result = await apiGet(`/resources/credentials/expiring`, { days });
687
+ return result;
688
+ }
689
+ async function handleRead12(uri) {
690
+ if (uri === "amplis://credentials") {
691
+ return resourceContent(uri, await handleCredentialsList());
692
+ }
693
+ const personMatch = uri.match(/^amplis:\/\/people\/([^/]+)\/credentials$/);
694
+ if (personMatch) {
695
+ return resourceContent(uri, await handlePersonCredentials(personMatch[1]));
696
+ }
697
+ const expiringMatch = uri.match(/^amplis:\/\/credentials\/expiring\?days=(\d+)$/);
698
+ if (expiringMatch) {
699
+ return resourceContent(uri, await handleExpiringCredentials(parseInt(expiringMatch[1], 10)));
700
+ }
701
+ return null;
702
+ }
703
+
704
+ // src/resources/capabilities.ts
705
+ var capabilities_exports = {};
706
+ __export(capabilities_exports, {
707
+ handleRead: () => handleRead13,
708
+ resourceTemplates: () => resourceTemplates13,
709
+ staticResources: () => staticResources13
710
+ });
711
+ var staticResources13 = [
712
+ { uri: "amplis://capabilities/matrix", name: "Capability Matrix", description: "Full capability matrix", mimeType: "application/json" }
713
+ ];
714
+ var resourceTemplates13 = [
715
+ { uriTemplate: "amplis://people/{personId}/capabilities", name: "Person Capabilities", description: "Capabilities for a specific person", mimeType: "application/json" }
716
+ ];
717
+ async function handleCapabilityMatrix() {
718
+ return await apiGet("/resources/capabilities/matrix");
719
+ }
720
+ async function handlePersonCapabilities(personId) {
721
+ return await apiGet(`/resources/people/${personId}/capabilities`);
722
+ }
723
+ async function handleRead13(uri) {
724
+ if (uri === "amplis://capabilities/matrix") return resourceContent(uri, await handleCapabilityMatrix());
725
+ const personMatch = uri.match(/^amplis:\/\/people\/([^/]+)\/capabilities$/);
726
+ if (personMatch) return resourceContent(uri, await handlePersonCapabilities(personMatch[1]));
727
+ return null;
728
+ }
729
+
730
+ // src/resources/availability.ts
731
+ var availability_exports = {};
732
+ __export(availability_exports, {
733
+ handleRead: () => handleRead14,
734
+ resourceTemplates: () => resourceTemplates14,
735
+ staticResources: () => staticResources14
736
+ });
737
+ var staticResources14 = [
738
+ { uri: "amplis://team/availability", name: "Team Availability", description: "Team availability overview for the next 4 weeks", mimeType: "application/json" }
739
+ ];
740
+ var resourceTemplates14 = [
741
+ { uriTemplate: "amplis://people/{personId}/availability", name: "Person Availability", description: "Day-by-day availability for a person", mimeType: "application/json" }
742
+ ];
743
+ async function handleTeamAvailability() {
744
+ const result = await apiGet("/resources/team/availability");
745
+ return result.team;
746
+ }
747
+ async function handlePersonAvailability(personId) {
748
+ return await apiGet(`/resources/people/${personId}/availability`);
749
+ }
750
+ async function handleRead14(uri) {
751
+ if (uri === "amplis://team/availability") return resourceContent(uri, await handleTeamAvailability());
752
+ const personMatch = uri.match(/^amplis:\/\/people\/([^/]+)\/availability$/);
753
+ if (personMatch) return resourceContent(uri, await handlePersonAvailability(personMatch[1]));
754
+ return null;
755
+ }
756
+
757
+ // src/resources/contacts.ts
758
+ var contacts_exports = {};
759
+ __export(contacts_exports, {
760
+ handleRead: () => handleRead15,
761
+ resourceTemplates: () => resourceTemplates15,
762
+ staticResources: () => staticResources15
763
+ });
764
+ var staticResources15 = [
765
+ { uri: "amplis://contacts", name: "Contacts", description: "List all global contacts", mimeType: "application/json" }
766
+ ];
767
+ var resourceTemplates15 = [
768
+ { uriTemplate: "amplis://contacts/{contactId}", name: "Contact Detail", description: "Detailed contact information", mimeType: "application/json" }
769
+ ];
770
+ async function handleContactsList() {
771
+ const result = await apiGet("/resources/contacts");
772
+ return result.contacts;
773
+ }
774
+ async function handleContactDetail(contactId) {
775
+ return await apiGet(`/resources/contacts/${contactId}`);
776
+ }
777
+ async function handleRead15(uri) {
778
+ if (uri === "amplis://contacts") return resourceContent(uri, await handleContactsList());
779
+ const match = uri.match(/^amplis:\/\/contacts\/([^/]+)$/);
780
+ if (match) return resourceContent(uri, await handleContactDetail(match[1]));
781
+ return null;
782
+ }
783
+
784
+ // src/resources/timesheets.ts
785
+ var timesheets_exports = {};
786
+ __export(timesheets_exports, {
787
+ handleRead: () => handleRead16,
788
+ resourceTemplates: () => resourceTemplates16,
789
+ staticResources: () => staticResources16
790
+ });
791
+ var staticResources16 = [
792
+ {
793
+ uri: "amplis://timesheets",
794
+ name: "Timesheets",
795
+ description: "Current timesheet periods across the team",
796
+ mimeType: "application/json"
797
+ }
798
+ ];
799
+ var resourceTemplates16 = [
800
+ {
801
+ uriTemplate: "amplis://timesheets/{periodId}/entries",
802
+ name: "Timesheet Entries",
803
+ description: "All entries for a timesheet period",
804
+ mimeType: "application/json"
805
+ }
806
+ ];
807
+ async function handleTimesheetsList() {
808
+ const result = await apiGet("/resources/timesheets");
809
+ return result.periods;
810
+ }
811
+ async function handleTimesheetEntries(periodId) {
812
+ const result = await apiGet(`/resources/timesheets/${periodId}/entries`);
813
+ return result;
814
+ }
815
+ async function handleRead16(uri) {
816
+ if (uri === "amplis://timesheets") {
817
+ return resourceContent(uri, await handleTimesheetsList());
818
+ }
819
+ const match = uri.match(/^amplis:\/\/timesheets\/([^/]+)\/entries$/);
820
+ if (match) {
821
+ return resourceContent(uri, await handleTimesheetEntries(match[1]));
822
+ }
823
+ return null;
824
+ }
825
+
826
+ // src/resources/invoices.ts
827
+ var invoices_exports = {};
828
+ __export(invoices_exports, {
829
+ handleRead: () => handleRead17,
830
+ resourceTemplates: () => resourceTemplates17,
831
+ staticResources: () => staticResources17
832
+ });
833
+ var staticResources17 = [
834
+ {
835
+ uri: "amplis://invoices",
836
+ name: "Invoices",
837
+ description: "List all invoices with status and amounts",
838
+ mimeType: "application/json"
839
+ }
840
+ ];
841
+ var resourceTemplates17 = [
842
+ {
843
+ uriTemplate: "amplis://invoices/{invoiceId}",
844
+ name: "Invoice Detail",
845
+ description: "Detailed invoice with line items",
846
+ mimeType: "application/json"
847
+ }
848
+ ];
849
+ async function handleInvoicesList() {
850
+ const result = await apiGet("/resources/invoices");
851
+ return result.invoices;
852
+ }
853
+ async function handleInvoiceDetail(invoiceId) {
854
+ const invoice = await apiGet(`/resources/invoices/${invoiceId}`);
855
+ return invoice;
856
+ }
857
+ async function handleRead17(uri) {
858
+ if (uri === "amplis://invoices") {
859
+ return resourceContent(uri, await handleInvoicesList());
860
+ }
861
+ const match = uri.match(/^amplis:\/\/invoices\/([^/]+)$/);
862
+ if (match) {
863
+ return resourceContent(uri, await handleInvoiceDetail(match[1]));
864
+ }
865
+ return null;
866
+ }
867
+
868
+ // src/resources/files.ts
869
+ var files_exports = {};
870
+ __export(files_exports, {
871
+ handleRead: () => handleRead18,
872
+ resourceTemplates: () => resourceTemplates18,
873
+ staticResources: () => staticResources18
874
+ });
875
+ var staticResources18 = [
876
+ {
877
+ uri: "amplis://files",
878
+ name: "Files",
879
+ description: "List all files and attachments",
880
+ mimeType: "application/json"
881
+ }
882
+ ];
883
+ var resourceTemplates18 = [
884
+ {
885
+ uriTemplate: "amplis://files/{entityType}/{entityId}",
886
+ name: "Entity Files",
887
+ description: "Files attached to a specific entity",
888
+ mimeType: "application/json"
889
+ }
890
+ ];
891
+ async function handleFilesList() {
892
+ const result = await apiGet("/resources/files");
893
+ return result.files;
894
+ }
895
+ async function handleEntityFiles(entityType, entityId) {
896
+ const result = await apiGet(`/resources/files/${entityType}/${entityId}`);
897
+ return result;
898
+ }
899
+ async function handleRead18(uri) {
900
+ if (uri === "amplis://files") {
901
+ return resourceContent(uri, await handleFilesList());
902
+ }
903
+ const match = uri.match(/^amplis:\/\/files\/([^/]+)\/([^/]+)$/);
904
+ if (match) {
905
+ return resourceContent(uri, await handleEntityFiles(match[1], match[2]));
906
+ }
907
+ return null;
908
+ }
909
+
910
+ // src/resources/index.ts
911
+ var modules = [
912
+ projects_exports,
913
+ pipeline_exports,
914
+ alerts_exports,
915
+ utilization_exports,
916
+ memory_exports,
917
+ clients_exports,
918
+ people_exports,
919
+ tasks_exports,
920
+ deliverables_exports,
921
+ documents_exports,
922
+ knowledge_exports,
923
+ credentials_exports,
924
+ capabilities_exports,
925
+ availability_exports,
926
+ contacts_exports,
927
+ timesheets_exports,
928
+ invoices_exports,
929
+ files_exports
930
+ ];
931
+ var resources = modules.flatMap((m) => [...m.staticResources]);
932
+ var resourceTemplates19 = modules.flatMap((m) => [...m.resourceTemplates]);
933
+ async function handleReadResource(uri) {
934
+ for (const mod of modules) {
935
+ const result = await mod.handleRead(uri);
936
+ if (result) return result;
937
+ }
938
+ throw new MCPNotFoundError("Resource", uri);
939
+ }
940
+
941
+ // src/lib/rate-limit.ts
942
+ var WINDOW_MS = 6e4;
943
+ var buckets = {
944
+ global: { timestamps: [], maxRequests: 120, windowMs: WINDOW_MS },
945
+ write: { timestamps: [], maxRequests: 30, windowMs: WINDOW_MS }
946
+ };
947
+ var WRITE_TOOLS = /* @__PURE__ */ new Set([
948
+ "task_create",
949
+ "task_update",
950
+ "time_log",
951
+ "timesheet_submit",
952
+ "note_add",
953
+ "pipeline_move",
954
+ "alert_acknowledge",
955
+ "site_visit_log",
956
+ "compliance_update",
957
+ "client_create",
958
+ "client_update",
959
+ "document_create",
960
+ "credential_create",
961
+ "credential_update",
962
+ "capability_add",
963
+ "capability_update",
964
+ "capability_evidence_add",
965
+ "availability_set",
966
+ "availability_bulk_set",
967
+ "contact_create",
968
+ "deliverable_create",
969
+ "deliverable_update",
970
+ "memory_store",
971
+ "decision_log",
972
+ "meeting_summarize"
973
+ ]);
974
+ function pruneAndCheck(bucket, now) {
975
+ const cutoff = now - bucket.windowMs;
976
+ bucket.timestamps = bucket.timestamps.filter((t) => t > cutoff);
977
+ if (bucket.timestamps.length >= bucket.maxRequests) {
978
+ return false;
979
+ }
980
+ bucket.timestamps.push(now);
981
+ return true;
982
+ }
983
+ var RateLimitError = class extends Error {
984
+ constructor(bucket) {
985
+ super(`Rate limit exceeded (${bucket}). Please wait before making more requests.`);
986
+ this.name = "RateLimitError";
987
+ }
988
+ };
989
+ function checkRateLimit(toolName) {
990
+ const now = Date.now();
991
+ if (!pruneAndCheck(buckets.global, now)) {
992
+ throw new RateLimitError("global: 120 calls/minute");
993
+ }
994
+ if (WRITE_TOOLS.has(toolName)) {
995
+ if (!pruneAndCheck(buckets.write, now)) {
996
+ throw new RateLimitError("write: 30 calls/minute");
997
+ }
998
+ }
999
+ }
1000
+
1001
+ // src/tools/task-tools.ts
1002
+ var task_tools_exports = {};
1003
+ __export(task_tools_exports, {
1004
+ handleToolCall: () => handleToolCall,
1005
+ tools: () => tools
1006
+ });
1007
+ import { z as z2 } from "zod";
1008
+
1009
+ // src/lib/schema-utils.ts
1010
+ import { z } from "zod";
1011
+ function caseInsensitiveEnum(values) {
1012
+ const lowercaseMap = /* @__PURE__ */ new Map();
1013
+ for (const v of values) {
1014
+ lowercaseMap.set(v.toLowerCase(), v);
1015
+ }
1016
+ return z.string().transform((val, ctx) => {
1017
+ const normalized = lowercaseMap.get(val.toLowerCase());
1018
+ if (normalized === void 0) {
1019
+ ctx.addIssue({
1020
+ code: z.ZodIssueCode.invalid_enum_value,
1021
+ options: [...values],
1022
+ received: val
1023
+ });
1024
+ return z.NEVER;
1025
+ }
1026
+ return normalized;
1027
+ });
1028
+ }
1029
+ function caseInsensitiveEnumOptional(values) {
1030
+ return caseInsensitiveEnum(values).optional();
1031
+ }
1032
+
1033
+ // src/tools/task-tools.ts
1034
+ var TaskCreateSchema = z2.object({
1035
+ projectId: z2.string(),
1036
+ title: z2.string(),
1037
+ description: z2.string().optional(),
1038
+ assigneeId: z2.string().optional(),
1039
+ dueDate: z2.string().optional(),
1040
+ priority: caseInsensitiveEnumOptional(["low", "medium", "high", "critical"])
1041
+ });
1042
+ var TaskUpdateSchema = z2.object({
1043
+ taskId: z2.string(),
1044
+ status: caseInsensitiveEnumOptional(["not_started", "in_progress", "completed", "blocked"]),
1045
+ assigneeId: z2.string().optional(),
1046
+ dueDate: z2.string().optional(),
1047
+ notes: z2.string().optional()
1048
+ });
1049
+ var TaskSearchSchema = z2.object({
1050
+ projectId: z2.string().optional(),
1051
+ status: caseInsensitiveEnumOptional(["not_started", "in_progress", "completed", "blocked"]),
1052
+ assigneeId: z2.string().optional(),
1053
+ overdue: z2.boolean().optional(),
1054
+ query: z2.string().optional(),
1055
+ limit: z2.number().optional()
1056
+ });
1057
+ var tools = [
1058
+ {
1059
+ name: "task_create",
1060
+ description: "Create a new task in a project",
1061
+ inputSchema: {
1062
+ type: "object",
1063
+ properties: {
1064
+ projectId: { type: "string", description: "Project ID" },
1065
+ title: { type: "string", description: "Task title" },
1066
+ description: { type: "string", description: "Task description" },
1067
+ assigneeId: { type: "string", description: "Assignee person ID (optional)" },
1068
+ dueDate: { type: "string", description: "Due date in ISO format (optional)" },
1069
+ priority: { type: "string", enum: ["low", "medium", "high", "critical"], description: "Task priority" }
1070
+ },
1071
+ required: ["projectId", "title"]
1072
+ }
1073
+ },
1074
+ {
1075
+ name: "task_update",
1076
+ description: "Update an existing task",
1077
+ inputSchema: {
1078
+ type: "object",
1079
+ properties: {
1080
+ taskId: { type: "string", description: "Task ID" },
1081
+ status: { type: "string", enum: ["not_started", "in_progress", "completed", "blocked"], description: "Task status" },
1082
+ assigneeId: { type: "string", description: "New assignee ID" },
1083
+ dueDate: { type: "string", description: "New due date" },
1084
+ notes: { type: "string", description: "Notes to add" }
1085
+ },
1086
+ required: ["taskId"]
1087
+ }
1088
+ },
1089
+ {
1090
+ name: "task_search",
1091
+ description: "Search tasks by status, assignee, project, or overdue",
1092
+ inputSchema: {
1093
+ type: "object",
1094
+ properties: {
1095
+ projectId: { type: "string", description: "Filter by project ID" },
1096
+ status: { type: "string", enum: ["not_started", "in_progress", "completed", "blocked"], description: "Filter by status" },
1097
+ assigneeId: { type: "string", description: "Filter by assignee person ID" },
1098
+ overdue: { type: "boolean", description: "Only show overdue tasks" },
1099
+ query: { type: "string", description: "Search task names" },
1100
+ limit: { type: "number", description: "Max results (default 50)" }
1101
+ }
1102
+ }
1103
+ }
1104
+ ];
1105
+ async function handleTaskCreate(args) {
1106
+ const input = TaskCreateSchema.parse(args);
1107
+ const task = await apiPost("/tasks", {
1108
+ projectId: input.projectId,
1109
+ title: input.title,
1110
+ description: input.description,
1111
+ assigneeId: input.assigneeId,
1112
+ dueDate: input.dueDate,
1113
+ priority: input.priority
1114
+ });
1115
+ return { id: task.id, name: task.name, status: task.status, projectId: task.projectId };
1116
+ }
1117
+ async function handleTaskUpdate(args) {
1118
+ const input = TaskUpdateSchema.parse(args);
1119
+ const task = await apiPatch(`/tasks/${input.taskId}`, {
1120
+ status: input.status,
1121
+ assigneeId: input.assigneeId,
1122
+ dueDate: input.dueDate,
1123
+ notes: input.notes
1124
+ });
1125
+ return { id: task.id, name: task.name, status: task.status };
1126
+ }
1127
+ async function handleTaskSearch(args) {
1128
+ const input = TaskSearchSchema.parse(args);
1129
+ const result = await apiGet("/tasks", {
1130
+ projectId: input.projectId,
1131
+ status: input.status,
1132
+ assigneeId: input.assigneeId,
1133
+ overdue: input.overdue,
1134
+ query: input.query,
1135
+ limit: input.limit ?? 50
1136
+ });
1137
+ return {
1138
+ count: result.count,
1139
+ tasks: result.tasks.map((t) => ({
1140
+ id: t.id,
1141
+ name: t.name,
1142
+ status: t.status,
1143
+ startDate: t.startDate,
1144
+ endDate: t.endDate,
1145
+ isOverdue: new Date(t.endDate) < /* @__PURE__ */ new Date() && t.status !== "COMPLETED",
1146
+ project: t.project,
1147
+ assignees: t.assignees || []
1148
+ }))
1149
+ };
1150
+ }
1151
+ async function handleToolCall(name, args) {
1152
+ try {
1153
+ switch (name) {
1154
+ case "task_create":
1155
+ return success(await handleTaskCreate(args));
1156
+ case "task_update":
1157
+ return success(await handleTaskUpdate(args));
1158
+ case "task_search":
1159
+ return success(await handleTaskSearch(args));
1160
+ default:
1161
+ return null;
1162
+ }
1163
+ } catch (err) {
1164
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1165
+ return safeError(err);
1166
+ }
1167
+ }
1168
+
1169
+ // src/tools/time-tools.ts
1170
+ var time_tools_exports = {};
1171
+ __export(time_tools_exports, {
1172
+ handleToolCall: () => handleToolCall2,
1173
+ tools: () => tools2
1174
+ });
1175
+ import { z as z3 } from "zod";
1176
+ var TimeLogSchema = z3.object({ projectId: z3.string(), taskId: z3.string().optional(), hours: z3.number().positive(), date: z3.string(), description: z3.string().optional(), category: caseInsensitiveEnumOptional(["billable", "internal", "leave"]) });
1177
+ var TimesheetSubmitSchema = z3.object({ periodId: z3.string() });
1178
+ var tools2 = [
1179
+ { name: "time_log", description: "Log time against a project or task", inputSchema: { type: "object", properties: { projectId: { type: "string" }, taskId: { type: "string" }, hours: { type: "number" }, date: { type: "string" }, description: { type: "string" }, category: { type: "string", enum: ["billable", "internal", "leave"] } }, required: ["projectId", "hours", "date"] } },
1180
+ { name: "timesheet_submit", description: "Submit a timesheet period for approval", inputSchema: { type: "object", properties: { periodId: { type: "string" } }, required: ["periodId"] } }
1181
+ ];
1182
+ async function handleTimeLog(args) {
1183
+ const input = TimeLogSchema.parse(args);
1184
+ return await apiPost("/timesheets/entries", { ...input, category: input.category || "billable" });
1185
+ }
1186
+ async function handleTimesheetSubmit(args) {
1187
+ const input = TimesheetSubmitSchema.parse(args);
1188
+ return await apiPatch(`/timesheets/periods/${input.periodId}/submit`, {});
1189
+ }
1190
+ async function handleToolCall2(name, args) {
1191
+ try {
1192
+ switch (name) {
1193
+ case "time_log":
1194
+ return success(await handleTimeLog(args));
1195
+ case "timesheet_submit":
1196
+ return success(await handleTimesheetSubmit(args));
1197
+ default:
1198
+ return null;
1199
+ }
1200
+ } catch (err) {
1201
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1202
+ return safeError(err);
1203
+ }
1204
+ }
1205
+
1206
+ // src/tools/note-tools.ts
1207
+ var note_tools_exports = {};
1208
+ __export(note_tools_exports, {
1209
+ handleToolCall: () => handleToolCall3,
1210
+ tools: () => tools3
1211
+ });
1212
+ import { z as z4 } from "zod";
1213
+ var NoteAddSchema = z4.object({ entityType: caseInsensitiveEnum(["project", "task", "client", "opportunity"]), entityId: z4.string(), content: z4.string(), category: caseInsensitiveEnumOptional(["general", "risk", "decision", "action"]) });
1214
+ var tools3 = [
1215
+ { name: "note_add", description: "Add a note to a project, task, or client", inputSchema: { type: "object", properties: { entityType: { type: "string", enum: ["project", "task", "client", "opportunity"] }, entityId: { type: "string" }, content: { type: "string" }, category: { type: "string", enum: ["general", "risk", "decision", "action"] } }, required: ["entityType", "entityId", "content"] } }
1216
+ ];
1217
+ async function handleNoteAdd(args) {
1218
+ const input = NoteAddSchema.parse(args);
1219
+ return await apiPost("/notes", { ...input, category: input.category || "general" });
1220
+ }
1221
+ async function handleToolCall3(name, args) {
1222
+ try {
1223
+ switch (name) {
1224
+ case "note_add":
1225
+ return success(await handleNoteAdd(args));
1226
+ default:
1227
+ return null;
1228
+ }
1229
+ } catch (err) {
1230
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1231
+ return safeError(err);
1232
+ }
1233
+ }
1234
+
1235
+ // src/tools/pipeline-tools.ts
1236
+ var pipeline_tools_exports = {};
1237
+ __export(pipeline_tools_exports, {
1238
+ handleToolCall: () => handleToolCall4,
1239
+ tools: () => tools4
1240
+ });
1241
+ import { z as z5 } from "zod";
1242
+ var PipelineMoveSchema = z5.object({ opportunityId: z5.string(), stage: caseInsensitiveEnum(["lead", "discovery", "qualified", "proposal_draft", "proposal_submitted", "negotiation", "won", "lost"]), notes: z5.string().optional() });
1243
+ var tools4 = [
1244
+ { name: "pipeline_move", description: "Move an opportunity to a different pipeline stage", inputSchema: { type: "object", properties: { opportunityId: { type: "string" }, stage: { type: "string", enum: ["lead", "discovery", "qualified", "proposal_draft", "proposal_submitted", "negotiation", "won", "lost"] }, notes: { type: "string" } }, required: ["opportunityId", "stage"] } }
1245
+ ];
1246
+ async function handlePipelineMove(args) {
1247
+ const input = PipelineMoveSchema.parse(args);
1248
+ return await apiPatch(`/pipeline/${input.opportunityId}/stage`, input);
1249
+ }
1250
+ async function handleToolCall4(name, args) {
1251
+ try {
1252
+ switch (name) {
1253
+ case "pipeline_move":
1254
+ return success(await handlePipelineMove(args));
1255
+ default:
1256
+ return null;
1257
+ }
1258
+ } catch (err) {
1259
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1260
+ return safeError(err);
1261
+ }
1262
+ }
1263
+
1264
+ // src/tools/alert-tools.ts
1265
+ var alert_tools_exports = {};
1266
+ __export(alert_tools_exports, {
1267
+ handleToolCall: () => handleToolCall5,
1268
+ tools: () => tools5
1269
+ });
1270
+ import { z as z6 } from "zod";
1271
+ var AlertAckSchema = z6.object({
1272
+ alertId: z6.string(),
1273
+ notes: z6.string().optional()
1274
+ });
1275
+ var tools5 = [
1276
+ {
1277
+ name: "alert_acknowledge",
1278
+ description: "Acknowledge an alert",
1279
+ inputSchema: {
1280
+ type: "object",
1281
+ properties: {
1282
+ alertId: { type: "string", description: "Alert ID" },
1283
+ notes: { type: "string", description: "Acknowledgment notes" }
1284
+ },
1285
+ required: ["alertId"]
1286
+ }
1287
+ }
1288
+ ];
1289
+ async function handleAlertAcknowledge(args) {
1290
+ const input = AlertAckSchema.parse(args);
1291
+ const result = await apiPatch(`/alerts/${input.alertId}/acknowledge`, { notes: input.notes });
1292
+ return { id: result.alert.id, title: result.alert.title, previousStatus: result.previousStatus, newStatus: result.alert.status };
1293
+ }
1294
+ async function handleToolCall5(name, args) {
1295
+ try {
1296
+ switch (name) {
1297
+ case "alert_acknowledge":
1298
+ return success(await handleAlertAcknowledge(args));
1299
+ default:
1300
+ return null;
1301
+ }
1302
+ } catch (err) {
1303
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1304
+ return safeError(err);
1305
+ }
1306
+ }
1307
+
1308
+ // src/tools/compliance-tools.ts
1309
+ var compliance_tools_exports = {};
1310
+ __export(compliance_tools_exports, {
1311
+ handleToolCall: () => handleToolCall6,
1312
+ tools: () => tools6
1313
+ });
1314
+ import { z as z7 } from "zod";
1315
+ var SiteVisitLogSchema = z7.object({
1316
+ projectId: z7.string(),
1317
+ visitDate: z7.string(),
1318
+ location: z7.string(),
1319
+ findings: z7.array(z7.object({ observation: z7.string(), category: z7.string().optional(), severity: z7.string().optional() })).optional(),
1320
+ risks: z7.array(z7.object({ description: z7.string(), severity: z7.string().optional(), mitigation: z7.string().optional() })).optional(),
1321
+ actionItems: z7.array(z7.object({ task: z7.string(), owner: z7.string().optional(), dueDate: z7.string().optional() })).optional()
1322
+ });
1323
+ var ComplianceUpdateSchema = z7.object({
1324
+ complianceId: z7.string(),
1325
+ status: caseInsensitiveEnumOptional(["valid", "expiring_soon", "expired", "pending"]),
1326
+ expiryDate: z7.string().optional(),
1327
+ notes: z7.string().optional()
1328
+ });
1329
+ var tools6 = [
1330
+ { name: "site_visit_log", description: "Log a site visit with findings and risks", inputSchema: { type: "object", properties: { projectId: { type: "string" }, visitDate: { type: "string" }, location: { type: "string" }, findings: { type: "array" }, risks: { type: "array" }, actionItems: { type: "array" } }, required: ["projectId", "visitDate", "location"] } },
1331
+ { name: "compliance_update", description: "Update a compliance item", inputSchema: { type: "object", properties: { complianceId: { type: "string" }, status: { type: "string" }, expiryDate: { type: "string" }, notes: { type: "string" } }, required: ["complianceId"] } }
1332
+ ];
1333
+ async function handleSiteVisitLog(args) {
1334
+ const input = SiteVisitLogSchema.parse(args);
1335
+ return await apiPost("/compliance/site-visits", input);
1336
+ }
1337
+ async function handleComplianceUpdate(args) {
1338
+ const input = ComplianceUpdateSchema.parse(args);
1339
+ const updated = await apiPatch(`/compliance/${input.complianceId}`, input);
1340
+ return { id: updated.id, name: updated.name, status: updated.status };
1341
+ }
1342
+ async function handleToolCall6(name, args) {
1343
+ try {
1344
+ switch (name) {
1345
+ case "site_visit_log":
1346
+ return success(await handleSiteVisitLog(args));
1347
+ case "compliance_update":
1348
+ return success(await handleComplianceUpdate(args));
1349
+ default:
1350
+ return null;
1351
+ }
1352
+ } catch (err) {
1353
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1354
+ return safeError(err);
1355
+ }
1356
+ }
1357
+
1358
+ // src/tools/client-tools.ts
1359
+ var client_tools_exports = {};
1360
+ __export(client_tools_exports, {
1361
+ handleToolCall: () => handleToolCall7,
1362
+ tools: () => tools7
1363
+ });
1364
+ import { z as z8 } from "zod";
1365
+ var ClientSearchSchema = z8.object({
1366
+ query: z8.string().optional(),
1367
+ industry: z8.string().optional(),
1368
+ limit: z8.number().optional()
1369
+ });
1370
+ var ClientCreateSchema = z8.object({
1371
+ name: z8.string(),
1372
+ industry: z8.string().optional(),
1373
+ website: z8.string().optional(),
1374
+ description: z8.string().optional(),
1375
+ primaryContactName: z8.string().optional(),
1376
+ primaryContactEmail: z8.string().optional(),
1377
+ primaryContactPhone: z8.string().optional()
1378
+ });
1379
+ var ClientUpdateSchema = z8.object({
1380
+ clientId: z8.string(),
1381
+ name: z8.string().optional(),
1382
+ industry: z8.string().optional(),
1383
+ website: z8.string().optional(),
1384
+ description: z8.string().optional(),
1385
+ healthScore: z8.number().optional(),
1386
+ primaryContactName: z8.string().optional(),
1387
+ primaryContactEmail: z8.string().optional(),
1388
+ primaryContactPhone: z8.string().optional()
1389
+ });
1390
+ var tools7 = [
1391
+ {
1392
+ name: "client_search",
1393
+ description: "Search clients by name or industry",
1394
+ inputSchema: {
1395
+ type: "object",
1396
+ properties: {
1397
+ query: { type: "string", description: "Search client names" },
1398
+ industry: { type: "string", description: "Filter by industry" },
1399
+ limit: { type: "number", description: "Max results (default 20)" }
1400
+ }
1401
+ }
1402
+ },
1403
+ {
1404
+ name: "client_create",
1405
+ description: "Create a new client",
1406
+ inputSchema: {
1407
+ type: "object",
1408
+ properties: {
1409
+ name: { type: "string", description: "Client name" },
1410
+ industry: { type: "string", description: "Industry" },
1411
+ website: { type: "string", description: "Website URL" },
1412
+ description: { type: "string", description: "Description" },
1413
+ primaryContactName: { type: "string", description: "Primary contact name" },
1414
+ primaryContactEmail: { type: "string", description: "Primary contact email" },
1415
+ primaryContactPhone: { type: "string", description: "Primary contact phone" }
1416
+ },
1417
+ required: ["name"]
1418
+ }
1419
+ },
1420
+ {
1421
+ name: "client_update",
1422
+ description: "Update an existing client",
1423
+ inputSchema: {
1424
+ type: "object",
1425
+ properties: {
1426
+ clientId: { type: "string", description: "Client ID" },
1427
+ name: { type: "string", description: "New name" },
1428
+ industry: { type: "string", description: "New industry" },
1429
+ website: { type: "string", description: "New website" },
1430
+ description: { type: "string", description: "New description" },
1431
+ healthScore: { type: "number", description: "Health score (1-100)" },
1432
+ primaryContactName: { type: "string", description: "Primary contact name" },
1433
+ primaryContactEmail: { type: "string", description: "Primary contact email" },
1434
+ primaryContactPhone: { type: "string", description: "Primary contact phone" }
1435
+ },
1436
+ required: ["clientId"]
1437
+ }
1438
+ }
1439
+ ];
1440
+ async function handleClientSearch(args) {
1441
+ const input = ClientSearchSchema.parse(args);
1442
+ const result = await apiGet("/clients", {
1443
+ query: input.query,
1444
+ industry: input.industry,
1445
+ limit: input.limit ?? 20
1446
+ });
1447
+ return { count: result.count, clients: result.clients };
1448
+ }
1449
+ async function handleClientCreate(args) {
1450
+ const input = ClientCreateSchema.parse(args);
1451
+ const client = await apiPost("/clients", input);
1452
+ return { id: client.id, name: client.name, industry: client.industry };
1453
+ }
1454
+ async function handleClientUpdate(args) {
1455
+ const input = ClientUpdateSchema.parse(args);
1456
+ const { clientId, ...data } = input;
1457
+ const updated = await apiPatch(`/clients/${clientId}`, data);
1458
+ return { id: updated.id, name: updated.name, industry: updated.industry, healthScore: updated.healthScore };
1459
+ }
1460
+ async function handleToolCall7(name, args) {
1461
+ try {
1462
+ switch (name) {
1463
+ case "client_search":
1464
+ return success(await handleClientSearch(args));
1465
+ case "client_create":
1466
+ return success(await handleClientCreate(args));
1467
+ case "client_update":
1468
+ return success(await handleClientUpdate(args));
1469
+ default:
1470
+ return null;
1471
+ }
1472
+ } catch (err) {
1473
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1474
+ return safeError(err);
1475
+ }
1476
+ }
1477
+
1478
+ // src/tools/people-tools.ts
1479
+ var people_tools_exports = {};
1480
+ __export(people_tools_exports, {
1481
+ handleToolCall: () => handleToolCall8,
1482
+ tools: () => tools8
1483
+ });
1484
+ import { z as z9 } from "zod";
1485
+ var PersonSearchSchema = z9.object({
1486
+ query: z9.string().optional(),
1487
+ role: z9.string().optional(),
1488
+ skill: z9.string().optional(),
1489
+ available: z9.boolean().optional(),
1490
+ limit: z9.number().optional()
1491
+ });
1492
+ var tools8 = [
1493
+ {
1494
+ name: "person_search",
1495
+ description: "Search people by name, role, skill, or availability",
1496
+ inputSchema: {
1497
+ type: "object",
1498
+ properties: {
1499
+ query: { type: "string", description: "Search person names" },
1500
+ role: { type: "string", description: "Filter by role/title" },
1501
+ skill: { type: "string", description: "Filter by capability name" },
1502
+ available: { type: "boolean", description: "Only show people with available capacity" },
1503
+ limit: { type: "number", description: "Max results (default 20)" }
1504
+ }
1505
+ }
1506
+ }
1507
+ ];
1508
+ async function handlePersonSearch(args) {
1509
+ const input = PersonSearchSchema.parse(args);
1510
+ const result = await apiGet("/people", {
1511
+ query: input.query,
1512
+ role: input.role,
1513
+ skill: input.skill,
1514
+ available: input.available,
1515
+ limit: input.limit ?? 20
1516
+ });
1517
+ return { count: result.count, people: result.people };
1518
+ }
1519
+ async function handleToolCall8(name, args) {
1520
+ try {
1521
+ switch (name) {
1522
+ case "person_search":
1523
+ return success(await handlePersonSearch(args));
1524
+ default:
1525
+ return null;
1526
+ }
1527
+ } catch (err) {
1528
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1529
+ return safeError(err);
1530
+ }
1531
+ }
1532
+
1533
+ // src/tools/document-tools.ts
1534
+ var document_tools_exports = {};
1535
+ __export(document_tools_exports, {
1536
+ handleToolCall: () => handleToolCall9,
1537
+ tools: () => tools9
1538
+ });
1539
+ import { z as z10 } from "zod";
1540
+ var DocumentSearchSchema = z10.object({ query: z10.string().optional(), type: z10.string().optional(), projectId: z10.string().optional(), personId: z10.string().optional(), limit: z10.number().optional() });
1541
+ var DocumentCreateSchema = z10.object({ name: z10.string(), type: caseInsensitiveEnum(["RESUME", "PROPOSAL", "REPORT", "CAPABILITY_STATEMENT", "LETTER", "OTHER"]), personId: z10.string().optional(), projectId: z10.string().optional(), notes: z10.string().optional() });
1542
+ var tools9 = [
1543
+ { name: "document_search", description: "Search documents", inputSchema: { type: "object", properties: { query: { type: "string" }, type: { type: "string" }, projectId: { type: "string" }, personId: { type: "string" }, limit: { type: "number" } } } },
1544
+ { name: "document_create", description: "Create a new document", inputSchema: { type: "object", properties: { name: { type: "string" }, type: { type: "string" }, personId: { type: "string" }, projectId: { type: "string" }, notes: { type: "string" } }, required: ["name", "type"] } }
1545
+ ];
1546
+ async function handleDocumentSearch(args) {
1547
+ const input = DocumentSearchSchema.parse(args);
1548
+ const result = await apiGet("/documents", input);
1549
+ return { count: result.count, documents: result.documents };
1550
+ }
1551
+ async function handleDocumentCreate(args) {
1552
+ const input = DocumentCreateSchema.parse(args);
1553
+ const doc = await apiPost("/documents", input);
1554
+ return { id: doc.id, name: doc.title, type: doc.type, status: doc.status };
1555
+ }
1556
+ async function handleToolCall9(name, args) {
1557
+ try {
1558
+ switch (name) {
1559
+ case "document_search":
1560
+ return success(await handleDocumentSearch(args));
1561
+ case "document_create":
1562
+ return success(await handleDocumentCreate(args));
1563
+ default:
1564
+ return null;
1565
+ }
1566
+ } catch (err) {
1567
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1568
+ return safeError(err);
1569
+ }
1570
+ }
1571
+
1572
+ // src/tools/knowledge-tools.ts
1573
+ var knowledge_tools_exports = {};
1574
+ __export(knowledge_tools_exports, {
1575
+ handleToolCall: () => handleToolCall10,
1576
+ tools: () => tools10
1577
+ });
1578
+ import { z as z11 } from "zod";
1579
+ var KBSearchSchema = z11.object({ query: z11.string().max(1e3), projectId: z11.string().optional(), limit: z11.number().optional() });
1580
+ var tools10 = [
1581
+ { name: "kb_search", description: "Search the knowledge base using semantic search", inputSchema: { type: "object", properties: { query: { type: "string" }, projectId: { type: "string" }, limit: { type: "number" } }, required: ["query"] } }
1582
+ ];
1583
+ async function handleKBSearch2(args) {
1584
+ const input = KBSearchSchema.parse(args);
1585
+ return await apiGet("/knowledge/search", input);
1586
+ }
1587
+ async function handleToolCall10(name, args) {
1588
+ try {
1589
+ switch (name) {
1590
+ case "kb_search":
1591
+ return success(await handleKBSearch2(args));
1592
+ default:
1593
+ return null;
1594
+ }
1595
+ } catch (err) {
1596
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1597
+ return safeError(err);
1598
+ }
1599
+ }
1600
+
1601
+ // src/tools/project-tools.ts
1602
+ var project_tools_exports = {};
1603
+ __export(project_tools_exports, {
1604
+ handleToolCall: () => handleToolCall11,
1605
+ tools: () => tools11
1606
+ });
1607
+ import { z as z12 } from "zod";
1608
+ var ProjectSearchSchema = z12.object({
1609
+ query: z12.string().optional(),
1610
+ status: z12.string().optional(),
1611
+ clientId: z12.string().optional(),
1612
+ lifecycleStage: z12.string().optional(),
1613
+ limit: z12.number().optional()
1614
+ });
1615
+ var tools11 = [
1616
+ {
1617
+ name: "project_search",
1618
+ description: "Search projects by name, status, client, or lifecycle stage",
1619
+ inputSchema: {
1620
+ type: "object",
1621
+ properties: {
1622
+ query: { type: "string", description: "Search project names" },
1623
+ status: { type: "string", enum: ["SCOPING", "ACTIVE", "ON_HOLD", "COMPLETED", "ARCHIVED"], description: "Filter by status" },
1624
+ clientId: { type: "string", description: "Filter by client ID" },
1625
+ lifecycleStage: { type: "string", description: "Filter by lifecycle stage" },
1626
+ limit: { type: "number", description: "Max results (default 20)" }
1627
+ }
1628
+ }
1629
+ }
1630
+ ];
1631
+ async function handleProjectSearch(args) {
1632
+ const input = ProjectSearchSchema.parse(args);
1633
+ const result = await apiGet("/projects", {
1634
+ query: input.query,
1635
+ status: input.status,
1636
+ clientId: input.clientId,
1637
+ lifecycleStage: input.lifecycleStage,
1638
+ limit: input.limit ?? 20
1639
+ });
1640
+ return { count: result.count, projects: result.projects };
1641
+ }
1642
+ async function handleToolCall11(name, args) {
1643
+ try {
1644
+ switch (name) {
1645
+ case "project_search":
1646
+ return success(await handleProjectSearch(args));
1647
+ default:
1648
+ return null;
1649
+ }
1650
+ } catch (err) {
1651
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1652
+ return safeError(err);
1653
+ }
1654
+ }
1655
+
1656
+ // src/tools/credential-tools.ts
1657
+ var credential_tools_exports = {};
1658
+ __export(credential_tools_exports, {
1659
+ handleToolCall: () => handleToolCall12,
1660
+ tools: () => tools12
1661
+ });
1662
+ import { z as z13 } from "zod";
1663
+ var credentialTypeValues = ["CERTIFICATION", "LICENSE", "CLEARANCE", "MEDICAL", "TRAINING", "TICKET", "MEMBERSHIP", "INSURANCE", "IDENTITY", "OTHER"];
1664
+ var credentialStatusValues = ["PENDING_UPLOAD", "PENDING_VERIFICATION", "VERIFIED", "EXPIRED", "EXPIRING_SOON", "REJECTED", "SUSPENDED"];
1665
+ var CredentialCreateSchema = z13.object({
1666
+ personId: z13.string(),
1667
+ type: caseInsensitiveEnum(credentialTypeValues),
1668
+ name: z13.string(),
1669
+ issuer: z13.string().optional(),
1670
+ credentialNumber: z13.string().optional(),
1671
+ category: z13.string().optional(),
1672
+ issuedAt: z13.string().optional(),
1673
+ expiresAt: z13.string().optional(),
1674
+ notes: z13.string().optional()
1675
+ });
1676
+ var CredentialUpdateSchema = z13.object({
1677
+ credentialId: z13.string(),
1678
+ status: caseInsensitiveEnumOptional(credentialStatusValues),
1679
+ expiresAt: z13.string().optional(),
1680
+ notes: z13.string().optional()
1681
+ });
1682
+ var CredentialSearchSchema = z13.object({
1683
+ personId: z13.string().optional(),
1684
+ type: z13.string().optional(),
1685
+ status: z13.string().optional(),
1686
+ expiringWithinDays: z13.number().optional(),
1687
+ query: z13.string().optional(),
1688
+ limit: z13.number().optional()
1689
+ });
1690
+ var tools12 = [
1691
+ { name: "credential_create", description: "Create a new credential for a person", inputSchema: { type: "object", properties: { personId: { type: "string" }, type: { type: "string" }, name: { type: "string" }, issuer: { type: "string" }, credentialNumber: { type: "string" }, category: { type: "string" }, issuedAt: { type: "string" }, expiresAt: { type: "string" }, notes: { type: "string" } }, required: ["personId", "type", "name"] } },
1692
+ { name: "credential_update", description: "Update an existing credential", inputSchema: { type: "object", properties: { credentialId: { type: "string" }, status: { type: "string" }, expiresAt: { type: "string" }, notes: { type: "string" } }, required: ["credentialId"] } },
1693
+ { name: "credential_search", description: "Search credentials", inputSchema: { type: "object", properties: { personId: { type: "string" }, type: { type: "string" }, status: { type: "string" }, expiringWithinDays: { type: "number" }, query: { type: "string" }, limit: { type: "number" } } } }
1694
+ ];
1695
+ async function handleCredentialCreate(args) {
1696
+ const input = CredentialCreateSchema.parse(args);
1697
+ const cred = await apiPost("/credentials", input);
1698
+ return { id: cred.id, name: cred.name, type: cred.type, status: cred.status };
1699
+ }
1700
+ async function handleCredentialUpdate(args) {
1701
+ const input = CredentialUpdateSchema.parse(args);
1702
+ const updated = await apiPatch(`/credentials/${input.credentialId}`, input);
1703
+ return { id: updated.id, name: updated.name, status: updated.status, expiresAt: updated.expiresAt };
1704
+ }
1705
+ async function handleCredentialSearch(args) {
1706
+ const input = CredentialSearchSchema.parse(args);
1707
+ const result = await apiGet("/credentials", input);
1708
+ return { count: result.count, credentials: result.credentials };
1709
+ }
1710
+ async function handleToolCall12(name, args) {
1711
+ try {
1712
+ switch (name) {
1713
+ case "credential_create":
1714
+ return success(await handleCredentialCreate(args));
1715
+ case "credential_update":
1716
+ return success(await handleCredentialUpdate(args));
1717
+ case "credential_search":
1718
+ return success(await handleCredentialSearch(args));
1719
+ default:
1720
+ return null;
1721
+ }
1722
+ } catch (err) {
1723
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1724
+ return safeError(err);
1725
+ }
1726
+ }
1727
+
1728
+ // src/tools/capability-tools.ts
1729
+ var capability_tools_exports = {};
1730
+ __export(capability_tools_exports, {
1731
+ handleToolCall: () => handleToolCall13,
1732
+ tools: () => tools13
1733
+ });
1734
+ import { z as z14 } from "zod";
1735
+ var CapabilityAddSchema = z14.object({
1736
+ personId: z14.string(),
1737
+ category: caseInsensitiveEnum(["DOMAIN", "INDUSTRY", "TOOL", "METHODOLOGY", "SOFT_SKILL"]),
1738
+ name: z14.string(),
1739
+ proficiency: caseInsensitiveEnum(["NOVICE", "COMPETENT", "PROFICIENT", "EXPERT"]),
1740
+ yearsExperience: z14.number().optional()
1741
+ });
1742
+ var CapabilityUpdateSchema = z14.object({
1743
+ capabilityId: z14.string(),
1744
+ proficiency: caseInsensitiveEnumOptional(["NOVICE", "COMPETENT", "PROFICIENT", "EXPERT"]),
1745
+ yearsExperience: z14.number().optional()
1746
+ });
1747
+ var CapabilityEvidenceAddSchema = z14.object({
1748
+ capabilityId: z14.string(),
1749
+ type: caseInsensitiveEnum(["PROJECT_DELIVERABLE", "PRESENTATION", "PUBLICATION", "REFERENCE", "METRIC", "CERTIFICATION"]),
1750
+ title: z14.string(),
1751
+ description: z14.string().optional(),
1752
+ projectId: z14.string().optional(),
1753
+ documentId: z14.string().optional()
1754
+ });
1755
+ var tools13 = [
1756
+ { name: "capability_add", description: "Add a capability/skill to a person", inputSchema: { type: "object", properties: { personId: { type: "string" }, category: { type: "string", enum: ["DOMAIN", "INDUSTRY", "TOOL", "METHODOLOGY", "SOFT_SKILL"] }, name: { type: "string" }, proficiency: { type: "string", enum: ["NOVICE", "COMPETENT", "PROFICIENT", "EXPERT"] }, yearsExperience: { type: "number" } }, required: ["personId", "category", "name", "proficiency"] } },
1757
+ { name: "capability_update", description: "Update an existing capability", inputSchema: { type: "object", properties: { capabilityId: { type: "string" }, proficiency: { type: "string" }, yearsExperience: { type: "number" } }, required: ["capabilityId"] } },
1758
+ { name: "capability_evidence_add", description: "Add evidence supporting a capability", inputSchema: { type: "object", properties: { capabilityId: { type: "string" }, type: { type: "string" }, title: { type: "string" }, description: { type: "string" }, projectId: { type: "string" }, documentId: { type: "string" } }, required: ["capabilityId", "type", "title"] } }
1759
+ ];
1760
+ async function handleCapabilityAdd(args) {
1761
+ const input = CapabilityAddSchema.parse(args);
1762
+ const cap = await apiPost("/capabilities", input);
1763
+ return { id: cap.id, name: cap.name, category: cap.category, proficiency: cap.proficiency };
1764
+ }
1765
+ async function handleCapabilityUpdate(args) {
1766
+ const input = CapabilityUpdateSchema.parse(args);
1767
+ const updated = await apiPatch(`/capabilities/${input.capabilityId}`, input);
1768
+ return { id: updated.id, name: updated.name, proficiency: updated.proficiency };
1769
+ }
1770
+ async function handleCapabilityEvidenceAdd(args) {
1771
+ const input = CapabilityEvidenceAddSchema.parse(args);
1772
+ const evidence = await apiPost(`/capabilities/${input.capabilityId}/evidence`, input);
1773
+ return evidence;
1774
+ }
1775
+ async function handleToolCall13(name, args) {
1776
+ try {
1777
+ switch (name) {
1778
+ case "capability_add":
1779
+ return success(await handleCapabilityAdd(args));
1780
+ case "capability_update":
1781
+ return success(await handleCapabilityUpdate(args));
1782
+ case "capability_evidence_add":
1783
+ return success(await handleCapabilityEvidenceAdd(args));
1784
+ default:
1785
+ return null;
1786
+ }
1787
+ } catch (err) {
1788
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1789
+ return safeError(err);
1790
+ }
1791
+ }
1792
+
1793
+ // src/tools/availability-tools.ts
1794
+ var availability_tools_exports = {};
1795
+ __export(availability_tools_exports, {
1796
+ handleToolCall: () => handleToolCall14,
1797
+ tools: () => tools14
1798
+ });
1799
+ import { z as z15 } from "zod";
1800
+ var availabilityStatusValues = ["AVAILABLE", "UNAVAILABLE", "LEAVE", "PUBLIC_HOLIDAY", "PARTIALLY_AVAILABLE"];
1801
+ var AvailabilitySetSchema = z15.object({
1802
+ personId: z15.string(),
1803
+ date: z15.string(),
1804
+ status: caseInsensitiveEnum(availabilityStatusValues),
1805
+ hours: z15.number().optional(),
1806
+ note: z15.string().optional()
1807
+ });
1808
+ var AvailabilityBulkSetSchema = z15.object({
1809
+ personId: z15.string(),
1810
+ entries: z15.array(z15.object({
1811
+ date: z15.string(),
1812
+ status: caseInsensitiveEnum(availabilityStatusValues),
1813
+ hours: z15.number().optional(),
1814
+ note: z15.string().optional()
1815
+ })).max(366)
1816
+ });
1817
+ var AvailabilityQuerySchema = z15.object({
1818
+ personId: z15.string().optional(),
1819
+ startDate: z15.string(),
1820
+ endDate: z15.string(),
1821
+ status: caseInsensitiveEnumOptional(availabilityStatusValues)
1822
+ });
1823
+ var tools14 = [
1824
+ { name: "availability_set", description: "Set availability for a person on a specific date", inputSchema: { type: "object", properties: { personId: { type: "string" }, date: { type: "string" }, status: { type: "string", enum: ["AVAILABLE", "UNAVAILABLE", "LEAVE", "PUBLIC_HOLIDAY", "PARTIALLY_AVAILABLE"] }, hours: { type: "number" }, note: { type: "string" } }, required: ["personId", "date", "status"] } },
1825
+ { name: "availability_bulk_set", description: "Set availability for multiple dates at once", inputSchema: { type: "object", properties: { personId: { type: "string" }, entries: { type: "array", items: { type: "object" } } }, required: ["personId", "entries"] } },
1826
+ { name: "availability_query", description: "Query availability for a person or team", inputSchema: { type: "object", properties: { personId: { type: "string" }, startDate: { type: "string" }, endDate: { type: "string" }, status: { type: "string" } }, required: ["startDate", "endDate"] } }
1827
+ ];
1828
+ async function handleAvailabilitySet(args) {
1829
+ const input = AvailabilitySetSchema.parse(args);
1830
+ const day = await apiPost("/availability", input);
1831
+ return { id: day.id, date: day.date, status: day.status, hours: day.availableHours };
1832
+ }
1833
+ async function handleAvailabilityBulkSet(args) {
1834
+ const input = AvailabilityBulkSetSchema.parse(args);
1835
+ return await apiPost("/availability/bulk", input);
1836
+ }
1837
+ async function handleAvailabilityQuery(args) {
1838
+ const input = AvailabilityQuerySchema.parse(args);
1839
+ const result = await apiGet("/availability", input);
1840
+ return { count: result.count, days: result.days };
1841
+ }
1842
+ async function handleToolCall14(name, args) {
1843
+ try {
1844
+ switch (name) {
1845
+ case "availability_set":
1846
+ return success(await handleAvailabilitySet(args));
1847
+ case "availability_bulk_set":
1848
+ return success(await handleAvailabilityBulkSet(args));
1849
+ case "availability_query":
1850
+ return success(await handleAvailabilityQuery(args));
1851
+ default:
1852
+ return null;
1853
+ }
1854
+ } catch (err) {
1855
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1856
+ return safeError(err);
1857
+ }
1858
+ }
1859
+
1860
+ // src/tools/contact-tools.ts
1861
+ var contact_tools_exports = {};
1862
+ __export(contact_tools_exports, {
1863
+ handleToolCall: () => handleToolCall15,
1864
+ tools: () => tools15
1865
+ });
1866
+ import { z as z16 } from "zod";
1867
+ var ContactCreateSchema = z16.object({
1868
+ firstName: z16.string(),
1869
+ lastName: z16.string(),
1870
+ email: z16.string().optional(),
1871
+ phone: z16.string().optional(),
1872
+ title: z16.string().optional(),
1873
+ department: z16.string().optional(),
1874
+ role: caseInsensitiveEnumOptional(["CHAMPION", "DECISION_MAKER", "INFLUENCER", "USER", "BLOCKER"]),
1875
+ clientId: z16.string().optional(),
1876
+ notes: z16.string().optional(),
1877
+ linkedInUrl: z16.string().optional()
1878
+ });
1879
+ var tools15 = [
1880
+ {
1881
+ name: "contact_create",
1882
+ description: "Create a new global contact",
1883
+ inputSchema: {
1884
+ type: "object",
1885
+ properties: {
1886
+ firstName: { type: "string", description: "First name" },
1887
+ lastName: { type: "string", description: "Last name" },
1888
+ email: { type: "string", description: "Email" },
1889
+ phone: { type: "string", description: "Phone number" },
1890
+ title: { type: "string", description: "Job title" },
1891
+ department: { type: "string", description: "Department" },
1892
+ role: { type: "string", enum: ["CHAMPION", "DECISION_MAKER", "INFLUENCER", "USER", "BLOCKER"], description: "Contact role" },
1893
+ clientId: { type: "string", description: "Associated client ID" },
1894
+ notes: { type: "string", description: "Notes" },
1895
+ linkedInUrl: { type: "string", description: "LinkedIn URL" }
1896
+ },
1897
+ required: ["firstName"]
1898
+ }
1899
+ }
1900
+ ];
1901
+ async function handleContactCreate(args) {
1902
+ const input = ContactCreateSchema.parse(args);
1903
+ const contact = await apiPost("/contacts", input);
1904
+ return { id: contact.id, name: `${contact.firstName ?? ""} ${contact.lastName ?? ""}`.trim(), email: contact.email, role: contact.role };
1905
+ }
1906
+ async function handleToolCall15(name, args) {
1907
+ try {
1908
+ switch (name) {
1909
+ case "contact_create":
1910
+ return success(await handleContactCreate(args));
1911
+ default:
1912
+ return null;
1913
+ }
1914
+ } catch (err) {
1915
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1916
+ return safeError(err);
1917
+ }
1918
+ }
1919
+
1920
+ // src/tools/deliverable-tools.ts
1921
+ var deliverable_tools_exports = {};
1922
+ __export(deliverable_tools_exports, {
1923
+ handleToolCall: () => handleToolCall16,
1924
+ tools: () => tools16
1925
+ });
1926
+ import { z as z17 } from "zod";
1927
+ var DeliverableCreateSchema = z17.object({
1928
+ projectId: z17.string(),
1929
+ name: z17.string(),
1930
+ description: z17.string().optional(),
1931
+ type: caseInsensitiveEnumOptional(["REPORT", "MODEL", "WORKSHOP", "DASHBOARD", "OTHER"]),
1932
+ billingModel: caseInsensitiveEnumOptional(["FIXED_FEE", "TIME_AND_MATERIALS", "MILESTONE", "UNIT_PRICE"]),
1933
+ priceTotal: z17.number().optional(),
1934
+ budgetHours: z17.number().optional()
1935
+ });
1936
+ var DeliverableUpdateSchema = z17.object({
1937
+ deliverableId: z17.string(),
1938
+ status: caseInsensitiveEnumOptional(["NOT_STARTED", "IN_PROGRESS", "UNDER_REVIEW", "COMPLETED"]),
1939
+ percentComplete: z17.number().optional(),
1940
+ name: z17.string().optional(),
1941
+ description: z17.string().optional()
1942
+ });
1943
+ var tools16 = [
1944
+ { name: "deliverable_create", description: "Create a new deliverable in a project", inputSchema: { type: "object", properties: { projectId: { type: "string" }, name: { type: "string" }, description: { type: "string" }, type: { type: "string" }, billingModel: { type: "string" }, priceTotal: { type: "number" }, budgetHours: { type: "number" } }, required: ["projectId", "name"] } },
1945
+ { name: "deliverable_update", description: "Update an existing deliverable", inputSchema: { type: "object", properties: { deliverableId: { type: "string" }, status: { type: "string" }, percentComplete: { type: "number" }, name: { type: "string" }, description: { type: "string" } }, required: ["deliverableId"] } }
1946
+ ];
1947
+ async function handleDeliverableCreate(args) {
1948
+ const input = DeliverableCreateSchema.parse(args);
1949
+ const d = await apiPost("/deliverables", input);
1950
+ return { id: d.id, name: d.name, type: d.type, status: d.status };
1951
+ }
1952
+ async function handleDeliverableUpdate(args) {
1953
+ const input = DeliverableUpdateSchema.parse(args);
1954
+ const updated = await apiPatch(`/deliverables/${input.deliverableId}`, input);
1955
+ return { id: updated.id, name: updated.name, status: updated.status, percentComplete: updated.percentComplete };
1956
+ }
1957
+ async function handleToolCall16(name, args) {
1958
+ try {
1959
+ switch (name) {
1960
+ case "deliverable_create":
1961
+ return success(await handleDeliverableCreate(args));
1962
+ case "deliverable_update":
1963
+ return success(await handleDeliverableUpdate(args));
1964
+ default:
1965
+ return null;
1966
+ }
1967
+ } catch (err) {
1968
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
1969
+ return safeError(err);
1970
+ }
1971
+ }
1972
+
1973
+ // src/tools/memory-tools.ts
1974
+ var memory_tools_exports = {};
1975
+ __export(memory_tools_exports, {
1976
+ handleToolCall: () => handleToolCall17,
1977
+ tools: () => tools17
1978
+ });
1979
+ import { z as z18 } from "zod";
1980
+ var entityTypeValues = ["project", "client", "person", "org"];
1981
+ var memoryCategoryValues = ["preference", "decision", "risk", "relationship", "process", "context", "lesson_learned"];
1982
+ var MemoryStoreSchema = z18.object({ entityType: caseInsensitiveEnum(entityTypeValues), entityId: z18.string().optional(), category: caseInsensitiveEnum(memoryCategoryValues), content: z18.string().max(1e4), source: z18.string().optional() });
1983
+ var MemoryRecallSchema = z18.object({ entityType: caseInsensitiveEnum(entityTypeValues), entityId: z18.string(), limit: z18.number().optional() });
1984
+ var MemorySearchSchema = z18.object({ query: z18.string().max(1e3), entityType: caseInsensitiveEnumOptional(entityTypeValues), limit: z18.number().optional() });
1985
+ var DecisionLogSchema = z18.object({ projectId: z18.string().optional(), title: z18.string(), decision: z18.string(), rationale: z18.string(), alternatives: z18.string().optional(), decidedBy: z18.string().optional() });
1986
+ var MeetingSummarizeSchema = z18.object({ projectId: z18.string().optional(), clientId: z18.string().optional(), title: z18.string(), date: z18.string(), attendees: z18.array(z18.string()).max(100), rawNotes: z18.string().max(5e4) });
1987
+ var tools17 = [
1988
+ { name: "memory_store", description: "Store a memory about an entity", inputSchema: { type: "object", properties: { entityType: { type: "string" }, entityId: { type: "string" }, category: { type: "string" }, content: { type: "string" }, source: { type: "string" } }, required: ["entityType", "category", "content"] } },
1989
+ { name: "memory_recall", description: "Recall memories about an entity", inputSchema: { type: "object", properties: { entityType: { type: "string" }, entityId: { type: "string" }, limit: { type: "number" } }, required: ["entityType", "entityId"] } },
1990
+ { name: "memory_search", description: "Search memories semantically", inputSchema: { type: "object", properties: { query: { type: "string" }, entityType: { type: "string" }, limit: { type: "number" } }, required: ["query"] } },
1991
+ { name: "decision_log", description: "Log a decision with rationale", inputSchema: { type: "object", properties: { projectId: { type: "string" }, title: { type: "string" }, decision: { type: "string" }, rationale: { type: "string" }, alternatives: { type: "string" }, decidedBy: { type: "string" } }, required: ["title", "decision", "rationale"] } },
1992
+ { name: "meeting_summarize", description: "Create a meeting note with auto-extracted items", inputSchema: { type: "object", properties: { projectId: { type: "string" }, clientId: { type: "string" }, title: { type: "string" }, date: { type: "string" }, attendees: { type: "array" }, rawNotes: { type: "string" } }, required: ["title", "date", "attendees", "rawNotes"] } }
1993
+ ];
1994
+ async function handleMemoryStore(args) {
1995
+ const input = MemoryStoreSchema.parse(args);
1996
+ return await apiPost("/memory", input);
1997
+ }
1998
+ async function handleMemoryRecall(args) {
1999
+ const input = MemoryRecallSchema.parse(args);
2000
+ return await apiGet("/memory/recall", input);
2001
+ }
2002
+ async function handleMemorySearch2(args) {
2003
+ const input = MemorySearchSchema.parse(args);
2004
+ return await apiGet("/memory/search", input);
2005
+ }
2006
+ async function handleDecisionLog(args) {
2007
+ const input = DecisionLogSchema.parse(args);
2008
+ return await apiPost("/memory/decisions", input);
2009
+ }
2010
+ async function handleMeetingSummarize(args) {
2011
+ const input = MeetingSummarizeSchema.parse(args);
2012
+ return await apiPost("/memory/meetings", input);
2013
+ }
2014
+ async function handleToolCall17(name, args) {
2015
+ try {
2016
+ switch (name) {
2017
+ case "memory_store":
2018
+ return success(await handleMemoryStore(args));
2019
+ case "memory_recall":
2020
+ return success(await handleMemoryRecall(args));
2021
+ case "memory_search":
2022
+ return success(await handleMemorySearch2(args));
2023
+ case "decision_log":
2024
+ return success(await handleDecisionLog(args));
2025
+ case "meeting_summarize":
2026
+ return success(await handleMeetingSummarize(args));
2027
+ default:
2028
+ return null;
2029
+ }
2030
+ } catch (err) {
2031
+ console.error(`Tool ${name} failed:`, err instanceof Error ? err.message : err);
2032
+ return safeError(err);
2033
+ }
2034
+ }
2035
+
2036
+ // src/tools/index.ts
2037
+ var modules2 = [
2038
+ task_tools_exports,
2039
+ time_tools_exports,
2040
+ note_tools_exports,
2041
+ pipeline_tools_exports,
2042
+ alert_tools_exports,
2043
+ compliance_tools_exports,
2044
+ client_tools_exports,
2045
+ people_tools_exports,
2046
+ document_tools_exports,
2047
+ knowledge_tools_exports,
2048
+ project_tools_exports,
2049
+ credential_tools_exports,
2050
+ capability_tools_exports,
2051
+ availability_tools_exports,
2052
+ contact_tools_exports,
2053
+ deliverable_tools_exports,
2054
+ memory_tools_exports
2055
+ ];
2056
+ var tools18 = modules2.flatMap((m) => [...m.tools]);
2057
+ async function handleToolCall18(name, args) {
2058
+ try {
2059
+ checkRateLimit(name);
2060
+ } catch (err) {
2061
+ if (err instanceof RateLimitError) {
2062
+ return error(err.message);
2063
+ }
2064
+ return error(sanitizeError(err));
2065
+ }
2066
+ const a = args ?? {};
2067
+ for (const mod of modules2) {
2068
+ const result = await mod.handleToolCall(name, a);
2069
+ if (result) return result;
2070
+ }
2071
+ return error(`Unknown tool: ${name}`);
2072
+ }
2073
+
2074
+ // src/prompts/weekly-status.ts
2075
+ var weekly_status_exports = {};
2076
+ __export(weekly_status_exports, {
2077
+ generate: () => generate
2078
+ });
2079
+
2080
+ // src/prompts/_data-fetchers.ts
2081
+ async function fetchProjectWithDetails(projectId) {
2082
+ const result = await apiGet(`/prompts/project/${projectId}/details`);
2083
+ return result;
2084
+ }
2085
+ async function fetchTimeEntries(filters) {
2086
+ const result = await apiGet("/prompts/time-entries", {
2087
+ projectId: filters.projectId,
2088
+ personId: filters.personId,
2089
+ fromDate: filters.fromDate?.toISOString(),
2090
+ toDate: filters.toDate?.toISOString()
2091
+ });
2092
+ return result;
2093
+ }
2094
+ async function fetchPersonTasks(personId, options) {
2095
+ const result = await apiGet(`/prompts/people/${personId}/tasks`, {
2096
+ status: options?.status?.join(","),
2097
+ fromDate: options?.fromDate?.toISOString(),
2098
+ toDate: options?.toDate?.toISOString()
2099
+ });
2100
+ return result.tasks;
2101
+ }
2102
+ async function fetchClientActivity(clientId, days = 14) {
2103
+ const result = await apiGet(`/prompts/clients/${clientId}/activity`, { days });
2104
+ return result;
2105
+ }
2106
+ async function fetchExpiringCredentials(withinDays = 30) {
2107
+ const result = await apiGet("/prompts/credentials/expiring", { days: withinDays });
2108
+ return result.credentials;
2109
+ }
2110
+ async function fetchTeamAvailability(startDate, endDate) {
2111
+ const result = await apiGet("/prompts/team/availability", {
2112
+ startDate: startDate.toISOString(),
2113
+ endDate: endDate.toISOString()
2114
+ });
2115
+ return result;
2116
+ }
2117
+ async function fetchPipelineData(stageFilter) {
2118
+ const result = await apiGet("/prompts/pipeline", {
2119
+ stage: stageFilter
2120
+ });
2121
+ return result;
2122
+ }
2123
+ async function fetchRiskMemories(projectId) {
2124
+ const result = await apiGet("/prompts/memories/risks", {
2125
+ projectId
2126
+ });
2127
+ return result.memories;
2128
+ }
2129
+ async function fetchPersonByUserId(userId) {
2130
+ const result = await apiGet(`/prompts/people/by-user/${userId}`);
2131
+ return result.person;
2132
+ }
2133
+ async function fetchUnbilledTime(clientId, options) {
2134
+ const result = await apiGet(`/prompts/clients/${clientId}/unbilled-time`, {
2135
+ projectId: options?.projectId,
2136
+ fromDate: options?.fromDate?.toISOString(),
2137
+ toDate: options?.toDate?.toISOString()
2138
+ });
2139
+ return result;
2140
+ }
2141
+ async function fetchProjectDecisions(projectId) {
2142
+ const result = await apiGet(`/prompts/projects/${projectId}/decisions`);
2143
+ return result.decisions;
2144
+ }
2145
+
2146
+ // src/prompts/weekly-status.ts
2147
+ async function generate(args) {
2148
+ const data = await fetchProjectWithDetails(args.projectId);
2149
+ if (!data) return `Project ${args.projectId} not found.`;
2150
+ const { project, alerts, taskStats, allTasks } = data;
2151
+ const now = /* @__PURE__ */ new Date();
2152
+ const weekStart = new Date(now);
2153
+ weekStart.setDate(now.getDate() - now.getDay() + 1);
2154
+ weekStart.setHours(0, 0, 0, 0);
2155
+ const weekEnd = new Date(weekStart);
2156
+ weekEnd.setDate(weekStart.getDate() + 6);
2157
+ const timeData = await fetchTimeEntries({
2158
+ projectId: args.projectId,
2159
+ fromDate: weekStart,
2160
+ toDate: weekEnd
2161
+ });
2162
+ const tasks = allTasks || [];
2163
+ const completedTasks = tasks.filter((t) => t.status === "COMPLETED");
2164
+ const inProgressTasks = tasks.filter((t) => t.status === "IN_PROGRESS");
2165
+ const blockedTasks = tasks.filter((t) => t.status === "BLOCKED");
2166
+ const overdueTasks = tasks.filter((t) => t.endDate && new Date(t.endDate) < now && t.status !== "COMPLETED");
2167
+ const completedList = completedTasks.length > 0 ? completedTasks.map((t) => `- ${t.name}`).join("\n") : "- None this period";
2168
+ const inProgressList = inProgressTasks.length > 0 ? inProgressTasks.map((t) => {
2169
+ const dueStr = t.endDate ? ` (due ${typeof t.endDate === "string" ? t.endDate.split("T")[0] : ""})` : "";
2170
+ return `- ${t.name}${dueStr}`;
2171
+ }).join("\n") : "- None";
2172
+ const blockedList = blockedTasks.length > 0 ? blockedTasks.map((t) => `- ${t.name}`).join("\n") : "- None";
2173
+ const overdueList = overdueTasks.length > 0 ? overdueTasks.map((t) => `- ${t.name} (due ${typeof t.endDate === "string" ? t.endDate.split("T")[0] : ""})`).join("\n") : "- None";
2174
+ const alertList = alerts || [];
2175
+ const alertsList = alertList.length > 0 ? alertList.map((a) => `- [${a.priority}] ${a.title} (${a.status})`).join("\n") : "- No active alerts";
2176
+ const deliverables = project.deliverables || [];
2177
+ const deliverableList = deliverables.map((d) => {
2178
+ const dAny = d;
2179
+ const pct = Number(dAny.percentComplete ?? 0);
2180
+ const tasksArr = dAny.tasks;
2181
+ const taskCount = tasksArr ? tasksArr.length : dAny.taskCount ?? 0;
2182
+ return `- ${d.name}: ${d.status} (${pct}% complete, ${taskCount} tasks)`;
2183
+ }).join("\n");
2184
+ const stats = taskStats || { total: 0, completed: 0, inProgress: 0, blocked: 0, overdue: 0 };
2185
+ const allocations = project.allocations || [];
2186
+ return `# Weekly Status Report
2187
+ Project: ${project.name}
2188
+ Client: ${project.client?.name || "N/A"}
2189
+ Week of: ${weekStart.toISOString().split("T")[0]}
2190
+
2191
+ ## Task Summary
2192
+ | Metric | Count |
2193
+ |--------|-------|
2194
+ | Total tasks | ${stats.total} |
2195
+ | Completed | ${stats.completed} |
2196
+ | In Progress | ${stats.inProgress} |
2197
+ | Blocked | ${stats.blocked} |
2198
+ | Overdue | ${stats.overdue} |
2199
+
2200
+ ## Completed This Week
2201
+ ${completedList}
2202
+
2203
+ ## In Progress
2204
+ ${inProgressList}
2205
+
2206
+ ## Blocked
2207
+ ${blockedList}
2208
+
2209
+ ## Overdue
2210
+ ${overdueList}
2211
+
2212
+ ## Deliverables
2213
+ ${deliverableList}
2214
+
2215
+ ## Time Logged This Week
2216
+ - Total hours: ${timeData.totalHours.toFixed(1)}h
2217
+ - Billable hours: ${timeData.billableHours.toFixed(1)}h
2218
+ - Entries: ${timeData.entries.length}
2219
+
2220
+ ## Active Alerts & Risks
2221
+ ${alertsList}
2222
+
2223
+ ## Team Allocation
2224
+ ${allocations.map((a) => `- ${a.person?.name || "Unknown"}: ${Number(a.hoursPerWeek || 0)}h/week`).join("\n") || "- No allocations recorded"}
2225
+
2226
+ Based on the data above, please generate:
2227
+ 1. An executive summary (2-3 sentences)
2228
+ 2. Key risks and recommended mitigations
2229
+ 3. Planned focus for next week`;
2230
+ }
2231
+
2232
+ // src/prompts/daily-standup.ts
2233
+ var daily_standup_exports = {};
2234
+ __export(daily_standup_exports, {
2235
+ generate: () => generate2
2236
+ });
2237
+ async function generate2(args) {
2238
+ const person = await fetchPersonByUserId(args.userId);
2239
+ if (!person) return `No person record found for user ${args.userId}.`;
2240
+ const today = args.date ? new Date(args.date) : /* @__PURE__ */ new Date();
2241
+ today.setHours(0, 0, 0, 0);
2242
+ const yesterday = new Date(today);
2243
+ yesterday.setDate(today.getDate() - (today.getDay() === 1 ? 3 : 1));
2244
+ const tomorrow = new Date(today);
2245
+ tomorrow.setDate(today.getDate() + 1);
2246
+ const weekStart = new Date(today);
2247
+ weekStart.setDate(today.getDate() - today.getDay() + 1);
2248
+ weekStart.setHours(0, 0, 0, 0);
2249
+ const weekEnd = new Date(weekStart);
2250
+ weekEnd.setDate(weekStart.getDate() + 6);
2251
+ const yesterdayTime = await fetchTimeEntries({
2252
+ personId: person.id,
2253
+ fromDate: yesterday,
2254
+ toDate: yesterday,
2255
+ ...args.projectId && { projectId: args.projectId }
2256
+ });
2257
+ const weekTime = await fetchTimeEntries({
2258
+ personId: person.id,
2259
+ fromDate: weekStart,
2260
+ toDate: weekEnd,
2261
+ ...args.projectId && { projectId: args.projectId }
2262
+ });
2263
+ const inProgressTasks = await fetchPersonTasks(person.id, {
2264
+ status: ["IN_PROGRESS"]
2265
+ });
2266
+ const blockedTasks = await fetchPersonTasks(person.id, {
2267
+ status: ["BLOCKED"]
2268
+ });
2269
+ const allAssigned = await fetchPersonTasks(person.id, {
2270
+ status: ["NOT_STARTED", "IN_PROGRESS"]
2271
+ });
2272
+ const overdueTasks = allAssigned.filter((t) => t.endDate && new Date(t.endDate) < today);
2273
+ const yesterdayEntries = yesterdayTime.entries.length > 0 ? yesterdayTime.entries.map((e) => {
2274
+ const projName = e.project?.name || "Unknown";
2275
+ const taskName = e.task?.name || "";
2276
+ return `- ${projName}${taskName ? ` / ${taskName}` : ""}: ${e.description || "No description"} (${Number(e.hours)}h)`;
2277
+ }).join("\n") : "- No time logged yesterday";
2278
+ const todayTasks = inProgressTasks.length > 0 ? inProgressTasks.map((t) => {
2279
+ const due = t.endDate ? ` \u2014 due ${typeof t.endDate === "string" ? t.endDate.split("T")[0] : ""}` : "";
2280
+ return `- ${t.project?.name || "Unknown"} / ${t.name}${due}`;
2281
+ }).join("\n") : "- No tasks currently in progress";
2282
+ const blockersList = blockedTasks.length > 0 ? blockedTasks.map((t) => `- ${t.project?.name || "Unknown"} / ${t.name}`).join("\n") : "- No blockers";
2283
+ const overdueList = overdueTasks.length > 0 ? overdueTasks.map((t) => `- ${t.project?.name || "Unknown"} / ${t.name} (due ${typeof t.endDate === "string" ? t.endDate.split("T")[0] : ""})`).join("\n") : "";
2284
+ return `# Daily Standup
2285
+ Person: ${person.name}
2286
+ Date: ${today.toISOString().split("T")[0]}
2287
+ ${args.projectId ? `Project filter: ${args.projectId}` : ""}
2288
+
2289
+ ## What I completed yesterday
2290
+ ${yesterdayEntries}
2291
+ - Total: ${yesterdayTime.totalHours.toFixed(1)}h
2292
+
2293
+ ## What I'm working on today
2294
+ ${todayTasks}
2295
+
2296
+ ## Blockers & Risks
2297
+ ${blockersList}
2298
+ ${overdueList ? `
2299
+ ### Overdue Tasks
2300
+ ${overdueList}` : ""}
2301
+
2302
+ ## Hours Summary
2303
+ - Yesterday: ${yesterdayTime.totalHours.toFixed(1)}h
2304
+ - This week so far: ${weekTime.totalHours.toFixed(1)}h (billable: ${weekTime.billableHours.toFixed(1)}h)
2305
+
2306
+ Based on this data, please summarize for standup in 2-3 concise bullet points per section.`;
2307
+ }
2308
+
2309
+ // src/prompts/project-health.ts
2310
+ var project_health_exports = {};
2311
+ __export(project_health_exports, {
2312
+ generate: () => generate3
2313
+ });
2314
+ async function generate3(args) {
2315
+ const data = await fetchProjectWithDetails(args.projectId);
2316
+ if (!data) return `Project ${args.projectId} not found.`;
2317
+ const { project, alerts, taskStats } = data;
2318
+ const now = /* @__PURE__ */ new Date();
2319
+ const timeData = await fetchTimeEntries({ projectId: args.projectId });
2320
+ const deliverables = project.deliverables || [];
2321
+ const totalBudget = deliverables.reduce((sum, d) => sum + Number(d.priceTotal || 0), 0);
2322
+ const totalBudgetHours = deliverables.reduce((sum, d) => sum + Number(d.budgetHours || 0), 0);
2323
+ const hoursUsed = timeData.totalHours;
2324
+ const budgetBurnPct = totalBudgetHours > 0 ? (hoursUsed / totalBudgetHours * 100).toFixed(1) : "N/A";
2325
+ const startDateStr = project.startDate;
2326
+ const endDateStr = project.endDate;
2327
+ const startDate = startDateStr ? new Date(startDateStr) : null;
2328
+ const endDate = endDateStr ? new Date(endDateStr) : null;
2329
+ let timelineElapsedPct = "N/A";
2330
+ if (startDate && endDate) {
2331
+ const total = endDate.getTime() - startDate.getTime();
2332
+ const elapsed = now.getTime() - startDate.getTime();
2333
+ timelineElapsedPct = total > 0 ? (elapsed / total * 100).toFixed(1) : "0";
2334
+ }
2335
+ const allocations = project.allocations || [];
2336
+ const teamMembers = allocations.map((a) => ({
2337
+ name: a.person?.name || "Unknown",
2338
+ hoursPerWeek: Number(a.hoursPerWeek || 0)
2339
+ }));
2340
+ const deliverableDetails = deliverables.map((d) => {
2341
+ const pct = Number(d.percentComplete ?? 0);
2342
+ const dAny = d;
2343
+ const tasksArr = dAny.tasks;
2344
+ const taskInfo = tasksArr ? `${tasksArr.filter((t) => t.status === "COMPLETED").length}/${tasksArr.length} tasks` : `${dAny.taskCount ?? 0} tasks`;
2345
+ return `| ${d.name} | ${d.status} | ${pct}% | ${taskInfo} |`;
2346
+ }).join("\n");
2347
+ const alertList = alerts || [];
2348
+ const alertRows = alertList.map(
2349
+ (a) => `| ${a.title} | ${a.priority} | ${a.status} | ${typeof a.createdAt === "string" ? a.createdAt.split("T")[0] : ""} |`
2350
+ ).join("\n");
2351
+ const stats = taskStats || { total: 0, completed: 0, inProgress: 0, blocked: 0, overdue: 0 };
2352
+ const healthIndicators = [];
2353
+ if (stats.overdue > 0) healthIndicators.push(`${stats.overdue} overdue tasks`);
2354
+ if (stats.blocked > 0) healthIndicators.push(`${stats.blocked} blocked tasks`);
2355
+ if (alertList.length > 0) healthIndicators.push(`${alertList.length} active alerts`);
2356
+ if (totalBudgetHours > 0 && hoursUsed > totalBudgetHours * 0.9) healthIndicators.push("Budget burn > 90%");
2357
+ return `# Project Health Report
2358
+ Project: ${project.name}
2359
+ Client: ${project.client?.name || "N/A"}
2360
+ Status: ${project.status}
2361
+ Phase: ${project.lifecycleStage || "N/A"}
2362
+ ${args.includeForecasts === "true" ? "Forecasting: Enabled" : ""}
2363
+
2364
+ ## Health Indicators
2365
+ ${healthIndicators.length > 0 ? healthIndicators.map((h) => `- WARNING: ${h}`).join("\n") : "- No issues detected"}
2366
+
2367
+ ## Budget
2368
+ - Total budget: $${totalBudget.toLocaleString()} / ${totalBudgetHours}h
2369
+ - Hours used: ${hoursUsed.toFixed(1)}h (${budgetBurnPct}%)
2370
+ - Billable hours: ${timeData.billableHours.toFixed(1)}h
2371
+ - Time entries: ${timeData.entries.length}
2372
+
2373
+ ## Timeline
2374
+ - Start: ${startDate ? startDate.toISOString().split("T")[0] : "Not set"}
2375
+ - End: ${endDate ? endDate.toISOString().split("T")[0] : "Not set"}
2376
+ - Elapsed: ${timelineElapsedPct}%
2377
+
2378
+ ## Tasks
2379
+ | Metric | Count |
2380
+ |--------|-------|
2381
+ | Total | ${stats.total} |
2382
+ | Completed | ${stats.completed} |
2383
+ | In Progress | ${stats.inProgress} |
2384
+ | Blocked | ${stats.blocked} |
2385
+ | Overdue | ${stats.overdue} |
2386
+
2387
+ ## Deliverables
2388
+ | Name | Status | Progress | Tasks |
2389
+ |------|--------|----------|-------|
2390
+ ${deliverableDetails || "| No deliverables | - | - | - |"}
2391
+
2392
+ ## Team (${teamMembers.length} members)
2393
+ ${teamMembers.map((m) => `- ${m.name} (${m.hoursPerWeek}h/week)`).join("\n") || "- No team allocations"}
2394
+
2395
+ ## Alerts & Risks
2396
+ | Title | Priority | Status | Created |
2397
+ |-------|----------|--------|---------|
2398
+ ${alertRows || "| No active alerts | - | - | - |"}
2399
+
2400
+ Based on this data, please:
2401
+ 1. Assign an overall health rating (GREEN / AMBER / RED) with justification
2402
+ 2. Identify the top 3 risks
2403
+ 3. Provide specific actionable recommendations
2404
+ ${args.includeForecasts === "true" ? "4. Forecast budget at completion and projected end date" : ""}`;
2405
+ }
2406
+
2407
+ // src/prompts/meeting-prep.ts
2408
+ var meeting_prep_exports = {};
2409
+ __export(meeting_prep_exports, {
2410
+ generate: () => generate4
2411
+ });
2412
+ async function generate4(args) {
2413
+ const data = await fetchClientActivity(args.clientId, 14);
2414
+ if (!data) return `Client ${args.clientId} not found.`;
2415
+ const { client, recentEntries, openTasks, invoices } = data;
2416
+ const meetingType = args.meetingType || "status";
2417
+ const globalContacts = client.globalContacts || [];
2418
+ const contactsList = globalContacts.length > 0 ? globalContacts.map((c) => `- ${c.firstName || ""} ${c.lastName || ""} \u2014 ${c.title || "N/A"} (${c.role || "N/A"}) \u2014 ${c.email || "No email"}`).join("\n") : "- No contacts on file";
2419
+ const projects = client.projects || [];
2420
+ const projectsList = projects.length > 0 ? projects.map((p) => {
2421
+ const deliverables = p.deliverables || [];
2422
+ const delStatus = deliverables.map((d) => `${d.name}: ${d.status} (${Number(d.percentComplete || 0)}%)`).join(", ");
2423
+ return `- ${p.name} [${p.status}/${p.lifecycleStage || "N/A"}] \u2014 Value: $${Number(p.proposalValue || 0).toLocaleString()}
2424
+ Deliverables: ${delStatus || "None"}`;
2425
+ }).join("\n") : "- No active projects";
2426
+ const entries = recentEntries || [];
2427
+ const recentHours = entries.reduce((sum, e) => sum + Number(e.hours), 0);
2428
+ const activityList = entries.length > 0 ? entries.slice(0, 20).map((e) => {
2429
+ const date = typeof e.entryDate === "string" ? e.entryDate.split("T")[0] : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2430
+ const who = e.person?.name || "Unknown";
2431
+ return `- ${date}: ${who} \u2014 ${e.project?.name || ""} / ${e.task?.name || "General"} (${Number(e.hours)}h) ${e.description || ""}`;
2432
+ }).join("\n") : "- No recent activity";
2433
+ const tasks = openTasks || [];
2434
+ const openItemsList = tasks.length > 0 ? tasks.slice(0, 20).map((t) => {
2435
+ const due = t.endDate ? typeof t.endDate === "string" ? t.endDate.split("T")[0] : "No date" : "No date";
2436
+ const assignees = t.assignees || [];
2437
+ const owner = assignees.map((a) => a.name).filter(Boolean).join(", ") || "Unassigned";
2438
+ return `| ${t.name} | ${owner} | ${due} | ${t.status} |`;
2439
+ }).join("\n") : "| No open items | - | - | - |";
2440
+ const invoiceList = invoices || [];
2441
+ const totalOutstanding = invoiceList.reduce((sum, i) => sum + Number(i.total || 0), 0);
2442
+ const invoiceListStr = invoiceList.length > 0 ? invoiceList.map((i) => `- ${i.invoiceNumber || i.id}: $${Number(i.total || 0).toLocaleString()} (${i.status}) due ${i.dueDate ? typeof i.dueDate === "string" ? i.dueDate.split("T")[0] : "N/A" : "N/A"}`).join("\n") : "- No outstanding invoices";
2443
+ const talkingPoints = {
2444
+ kickoff: `- Project scope and objectives
2445
+ - Team introductions and roles
2446
+ - Communication cadence and tools
2447
+ - Success criteria and KPIs
2448
+ - Timeline and key milestones
2449
+ - Risk management approach`,
2450
+ status: `- Progress since last meeting
2451
+ - Upcoming milestones and deadlines
2452
+ - Resource needs or changes
2453
+ - Risks and mitigation actions
2454
+ - Client decisions needed`,
2455
+ review: `- Deliverables walkthrough
2456
+ - Quality and acceptance criteria
2457
+ - Lessons learned
2458
+ - Scope changes or CR discussion
2459
+ - Next phase planning`,
2460
+ escalation: `- Issue summary and timeline
2461
+ - Impact assessment (schedule, budget, quality)
2462
+ - Root cause analysis
2463
+ - Proposed resolution options
2464
+ - Prevention measures for future`
2465
+ };
2466
+ return `# Meeting Preparation Brief
2467
+ Client: ${client.name}
2468
+ ${args.projectId ? `Project filter: ${args.projectId}` : "All Projects"}
2469
+ Meeting Type: ${meetingType}
2470
+
2471
+ ## Client Context
2472
+ - Industry: ${client.industry || "N/A"}
2473
+ - Health Score: ${client.healthScore || "N/A"}
2474
+ - Active projects: ${projects.length}
2475
+
2476
+ ## Key Contacts
2477
+ ${contactsList}
2478
+
2479
+ ## Projects Overview
2480
+ ${projectsList}
2481
+
2482
+ ## Recent Activity (Last 2 Weeks)
2483
+ Total hours logged: ${recentHours.toFixed(1)}h
2484
+ ${activityList}
2485
+
2486
+ ## Open Items & Action Items
2487
+ | Item | Owner | Due | Status |
2488
+ |------|-------|-----|--------|
2489
+ ${openItemsList}
2490
+
2491
+ ## Financial Summary
2492
+ - Outstanding invoices: $${totalOutstanding.toLocaleString()}
2493
+ ${invoiceListStr}
2494
+
2495
+ ## Suggested Talking Points (${meetingType})
2496
+ ${talkingPoints[meetingType] || talkingPoints.status}
2497
+
2498
+ Based on this data, please:
2499
+ 1. Draft a concise meeting agenda (5-7 items)
2500
+ 2. Highlight the top 3 things to discuss with the client
2501
+ 3. Note any risks or sensitive topics to be aware of
2502
+ 4. Suggest any documents or materials to bring`;
2503
+ }
2504
+
2505
+ // src/prompts/compliance-check.ts
2506
+ var compliance_check_exports = {};
2507
+ __export(compliance_check_exports, {
2508
+ generate: () => generate5
2509
+ });
2510
+ async function generate5(args) {
2511
+ const daysAhead = parseInt(args.daysAhead || "30", 10);
2512
+ const expiring = await fetchExpiringCredentials(daysAhead);
2513
+ const result = await apiGet("/prompts/credentials/all");
2514
+ const allCredentials = result.credentials;
2515
+ const statusCounts = {
2516
+ verified: allCredentials.filter((c) => c.status === "VERIFIED").length,
2517
+ expiringSoon: allCredentials.filter((c) => c.status === "EXPIRING_SOON").length,
2518
+ expired: allCredentials.filter((c) => c.status === "EXPIRED").length,
2519
+ pendingUpload: allCredentials.filter((c) => c.status === "PENDING_UPLOAD").length,
2520
+ pendingVerification: allCredentials.filter((c) => c.status === "PENDING_VERIFICATION").length,
2521
+ rejected: allCredentials.filter((c) => c.status === "REJECTED").length,
2522
+ suspended: allCredentials.filter((c) => c.status === "SUSPENDED").length
2523
+ };
2524
+ const total = allCredentials.length;
2525
+ const complianceScore = total > 0 ? (statusCounts.verified / total * 100).toFixed(1) : "N/A";
2526
+ let complianceItems = "- No project filter applied; showing org-wide credentials";
2527
+ if (args.projectId) {
2528
+ const itemsResult = await apiGet(`/prompts/projects/${args.projectId}/compliance`);
2529
+ complianceItems = itemsResult.items.length > 0 ? itemsResult.items.map((i) => `| ${i.name} | ${i.type || "N/A"} | ${i.status} | ${i.dueDate || "N/A"} |`).join("\n") : "- No compliance items for this project";
2530
+ }
2531
+ const expiringList = expiring.length > 0 ? expiring.map((c) => `| ${c.person?.name || "Unknown"} | ${c.name} | ${c.type} | ${c.expiresAt || "N/A"} | ${c.daysUntilExpiry ?? "N/A"} days |`).join("\n") : "| No credentials expiring within window | - | - | - | - |";
2532
+ return `# Compliance Status Review
2533
+ ${args.projectId ? `Project: ${args.projectId}` : "Scope: Organization-wide"}
2534
+ Look-ahead window: ${daysAhead} days
2535
+
2536
+ ## Credential Summary
2537
+ | Status | Count |
2538
+ |--------|-------|
2539
+ | Verified | ${statusCounts.verified} |
2540
+ | Expiring Soon | ${statusCounts.expiringSoon} |
2541
+ | Expired | ${statusCounts.expired} |
2542
+ | Pending Upload | ${statusCounts.pendingUpload} |
2543
+ | Pending Verification | ${statusCounts.pendingVerification} |
2544
+ | Rejected | ${statusCounts.rejected} |
2545
+ | Suspended | ${statusCounts.suspended} |
2546
+ | **Total** | **${total}** |
2547
+
2548
+ ## Compliance Score: ${complianceScore}%
2549
+
2550
+ ## Expiring Credentials (within ${daysAhead} days)
2551
+ | Person | Credential | Type | Expires | Time Left |
2552
+ |--------|------------|------|---------|-----------|
2553
+ ${expiringList}
2554
+
2555
+ ${args.projectId ? `## Project Compliance Items
2556
+ | Name | Type | Status | Expiry |
2557
+ |------|------|--------|--------|
2558
+ ${complianceItems}` : ""}
2559
+
2560
+ Based on this data, please:
2561
+ 1. Identify the most urgent compliance actions needed
2562
+ 2. Flag any team members who may become non-compliant
2563
+ 3. Recommend a prioritized remediation plan
2564
+ 4. Note any patterns (e.g., multiple credentials expiring at once)`;
2565
+ }
2566
+
2567
+ // src/prompts/resource-plan.ts
2568
+ var resource_plan_exports = {};
2569
+ __export(resource_plan_exports, {
2570
+ generate: () => generate6
2571
+ });
2572
+ async function generate6(args) {
2573
+ const timeframeWeeks = parseInt(args.timeframe || "4", 10);
2574
+ const data = await fetchProjectWithDetails(args.projectId);
2575
+ if (!data) return `Project ${args.projectId} not found.`;
2576
+ const { project, taskStats } = data;
2577
+ const startDate = /* @__PURE__ */ new Date();
2578
+ const endDate = /* @__PURE__ */ new Date();
2579
+ endDate.setDate(endDate.getDate() + timeframeWeeks * 7);
2580
+ const availability = await fetchTeamAvailability(startDate, endDate);
2581
+ const teamResult = await apiGet("/prompts/team");
2582
+ const team = teamResult.team;
2583
+ const personAllocation = team.map((p) => {
2584
+ const totalAllocated = p.allocations.reduce((sum, a) => sum + Number(a.hoursPerWeek || 0), 0);
2585
+ const projectAlloc = p.allocations.find((a) => a.project.name === project.name);
2586
+ return {
2587
+ name: p.name,
2588
+ role: p.role,
2589
+ totalAllocatedHours: totalAllocated,
2590
+ thisProjectHours: Number(projectAlloc?.hoursPerWeek || 0),
2591
+ otherProjects: p.allocations.filter((a) => a.project.name !== project.name).map((a) => `${a.project.name} (${Number(a.hoursPerWeek || 0)}h)`),
2592
+ topSkills: p.capabilities.slice(0, 5).map((c) => `${c.name} (${c.proficiency})`)
2593
+ };
2594
+ });
2595
+ const availSummary = {};
2596
+ for (const day of availability.days) {
2597
+ const name = day.person.name;
2598
+ if (!availSummary[name]) availSummary[name] = { available: 0, leave: 0, partial: 0 };
2599
+ if (day.status === "AVAILABLE") availSummary[name].available++;
2600
+ if (day.status === "LEAVE") availSummary[name].leave++;
2601
+ if (day.status === "PARTIALLY_AVAILABLE") availSummary[name].partial++;
2602
+ }
2603
+ const utilRows = availability.utilization.length > 0 ? availability.utilization.map((u) => {
2604
+ return `| ${u.person.name} | ${u.weekStart.split("T")[0]} | ${Number(u.billableHours || 0)}h | ${Number(u.availableHours || 0)}h | ${Number(u.utilizationRate || 0).toFixed(0)}% |`;
2605
+ }).join("\n") : "| No utilization data | - | - | - | - |";
2606
+ const allocationTable = personAllocation.filter((p) => p.totalAllocatedHours > 0 || p.thisProjectHours > 0).map((p) => `| ${p.name} | ${p.role || "N/A"} | ${p.thisProjectHours}h | ${p.totalAllocatedHours}h | ${p.otherProjects.join(", ") || "None"} |`).join("\n") || "| No allocations | - | - | - | - |";
2607
+ const typedProject = project;
2608
+ return `# Resource Allocation Plan
2609
+ Project: ${project.name}
2610
+ Client: ${typedProject.client?.name || typedProject.clientName || "N/A"}
2611
+ Timeframe: ${timeframeWeeks} weeks (${startDate.toISOString().split("T")[0]} to ${endDate.toISOString().split("T")[0]})
2612
+
2613
+ ## Project Status
2614
+ - Tasks: ${taskStats?.total || 0} total (${taskStats?.inProgress || 0} in progress, ${taskStats?.blocked || 0} blocked)
2615
+ - Overdue tasks: ${taskStats?.overdue || 0}
2616
+ - Team size: ${typedProject.allocations?.length || 0} allocated
2617
+
2618
+ ## Current Team Allocation
2619
+ | Person | Role | This Project | Total Allocated | Other Projects |
2620
+ |--------|------|-------------|-----------------|----------------|
2621
+ ${allocationTable}
2622
+
2623
+ ## Availability (next ${timeframeWeeks} weeks)
2624
+ ${Object.entries(availSummary).map(
2625
+ ([name, s]) => `- ${name}: ${s.available} available days, ${s.leave} leave days, ${s.partial} partial days`
2626
+ ).join("\n") || "- No availability data recorded"}
2627
+
2628
+ ## Utilization Records
2629
+ | Person | Week | Billable | Available | Rate |
2630
+ |--------|------|----------|-----------|------|
2631
+ ${utilRows}
2632
+
2633
+ ## Team Capabilities
2634
+ ${team.filter((p) => p.capabilities.length > 0).map(
2635
+ (p) => `- ${p.name}: ${p.capabilities.map((c) => `${c.name} (${c.proficiency})`).join(", ")}`
2636
+ ).join("\n") || "- No capability data recorded"}
2637
+
2638
+ Based on this data, please:
2639
+ 1. Assess whether the project is adequately resourced
2640
+ 2. Identify capacity gaps or over-allocation risks
2641
+ 3. Recommend specific staffing adjustments
2642
+ 4. Flag any upcoming availability conflicts (leave, competing projects)`;
2643
+ }
2644
+
2645
+ // src/prompts/risk-assessment.ts
2646
+ var risk_assessment_exports = {};
2647
+ __export(risk_assessment_exports, {
2648
+ generate: () => generate7
2649
+ });
2650
+ async function generate7(args) {
2651
+ const data = await fetchProjectWithDetails(args.projectId);
2652
+ if (!data) return `Project ${args.projectId} not found.`;
2653
+ const { project, alerts, taskStats, allTasks } = data;
2654
+ const now = /* @__PURE__ */ new Date();
2655
+ const riskMemories = await fetchRiskMemories(args.projectId);
2656
+ const tasks = allTasks || [];
2657
+ const overdueTasks = tasks.filter((t) => t.endDate && new Date(t.endDate) < now && t.status !== "COMPLETED").map((t) => ({
2658
+ name: t.name,
2659
+ dueDate: typeof t.endDate === "string" ? t.endDate.split("T")[0] : "",
2660
+ daysOverdue: Math.ceil((now.getTime() - new Date(t.endDate).getTime()) / (1e3 * 60 * 60 * 24)),
2661
+ status: t.status
2662
+ }));
2663
+ const complianceResult = await apiGet(`/prompts/projects/${args.projectId}/compliance/issues`);
2664
+ const complianceIssues = complianceResult.items || [];
2665
+ const completedTasks = tasks.filter((t) => t.status === "COMPLETED");
2666
+ const sortedCompleted = completedTasks.sort((a, b) => {
2667
+ const aTime = a.endDate ? new Date(a.endDate).getTime() : 0;
2668
+ const bTime = b.endDate ? new Date(b.endDate).getTime() : 0;
2669
+ return bTime - aTime;
2670
+ });
2671
+ const latestTask = sortedCompleted[0];
2672
+ const daysSinceActivity = latestTask?.endDate ? Math.ceil((now.getTime() - new Date(latestTask.endDate).getTime()) / (1e3 * 60 * 60 * 24)) : null;
2673
+ const stats = taskStats || { total: 0, completed: 0, inProgress: 0, blocked: 0, overdue: 0 };
2674
+ const alertList = alerts || [];
2675
+ const riskFactors = [];
2676
+ if (stats.overdue > 0) riskFactors.push(`${stats.overdue} overdue tasks`);
2677
+ if (stats.blocked > 0) riskFactors.push(`${stats.blocked} blocked tasks`);
2678
+ if (alertList.length > 0) riskFactors.push(`${alertList.length} active alerts`);
2679
+ if (complianceIssues.length > 0) riskFactors.push(`${complianceIssues.length} compliance issues`);
2680
+ if (daysSinceActivity && daysSinceActivity > 14) riskFactors.push(`${daysSinceActivity} days since last completed task`);
2681
+ const alertsList = alertList.length > 0 ? alertList.map((a) => `| ${a.title} | ${a.priority} | ${a.status} | ${typeof a.createdAt === "string" ? a.createdAt.split("T")[0] : ""} |`).join("\n") : "| No active alerts | - | - | - |";
2682
+ const overdueList = overdueTasks.length > 0 ? overdueTasks.map((t) => `| ${t.name} | ${t.dueDate} | ${t.daysOverdue} days | ${t.status} |`).join("\n") : "| No overdue tasks | - | - | - |";
2683
+ const complianceList = complianceIssues.length > 0 ? complianceIssues.map((c) => `| ${c.name} | ${c.status} | ${c.dueDate ? typeof c.dueDate === "string" ? c.dueDate.split("T")[0] : "N/A" : "N/A"} |`).join("\n") : "| No compliance issues | - | - |";
2684
+ const memoriesList = riskMemories.length > 0 ? riskMemories.map((m) => `- [${m.category}] ${m.content} (confidence: ${m.confidence})`).join("\n") : "- No risk memories recorded";
2685
+ const typedProject = project;
2686
+ return `# Risk Assessment
2687
+ Project: ${project.name}
2688
+ Client: ${typedProject.client?.name || "N/A"}
2689
+ Date: ${now.toISOString().split("T")[0]}
2690
+
2691
+ ## Risk Summary
2692
+ Risk factors identified: ${riskFactors.length}
2693
+ ${riskFactors.length > 0 ? riskFactors.map((r) => `- WARNING: ${r}`).join("\n") : "- No immediate risk factors"}
2694
+
2695
+ ## Active Alerts
2696
+ | Title | Priority | Status | Created |
2697
+ |-------|----------|--------|---------|
2698
+ ${alertsList}
2699
+
2700
+ ## Overdue Tasks
2701
+ | Task | Due Date | Days Overdue | Status |
2702
+ |------|----------|-------------|--------|
2703
+ ${overdueList}
2704
+
2705
+ ## Compliance Issues
2706
+ | Item | Status | Expiry |
2707
+ |------|--------|--------|
2708
+ ${complianceList}
2709
+
2710
+ ## Project Activity
2711
+ - Last completed task: ${latestTask ? `${latestTask.name} (${latestTask.endDate ? typeof latestTask.endDate === "string" ? latestTask.endDate.split("T")[0] : "" : "N/A"})` : "None"}
2712
+ - Days since activity: ${daysSinceActivity ?? "N/A"}
2713
+ ${daysSinceActivity && daysSinceActivity > 14 ? "- WARNING: Project may be stale" : ""}
2714
+
2715
+ ## AI Agent Risk Memories
2716
+ ${memoriesList}
2717
+
2718
+ ## Task Health
2719
+ - Total: ${stats.total} | Completed: ${stats.completed} | Blocked: ${stats.blocked} | Overdue: ${stats.overdue}
2720
+
2721
+ Based on this data, please:
2722
+ 1. Rate overall risk level (LOW / MEDIUM / HIGH / CRITICAL)
2723
+ 2. Create a prioritized risk register with likelihood, impact, and owner
2724
+ 3. Recommend specific mitigation actions for each risk
2725
+ 4. Identify any emerging risk patterns`;
2726
+ }
2727
+
2728
+ // src/prompts/pipeline-review.ts
2729
+ var pipeline_review_exports = {};
2730
+ __export(pipeline_review_exports, {
2731
+ generate: () => generate8
2732
+ });
2733
+ async function generate8(args) {
2734
+ const data = await fetchPipelineData(args.stage);
2735
+ const projects = data.projects || [];
2736
+ const totalValue = projects.reduce((sum, p) => sum + Number(p.proposalValue || 0), 0);
2737
+ const stageTable = (data.stageStats || []).length > 0 ? data.stageStats.map((s) => `| ${s.stage} | ${s.count} | $${s.totalValue.toLocaleString()} | ${s.avgAgeDays} |`).join("\n") : "| No pipeline data | - | - | - |";
2738
+ const now = /* @__PURE__ */ new Date();
2739
+ const staleOpps = projects.filter((p) => {
2740
+ const updatedAt = p.updatedAt ? new Date(p.updatedAt) : now;
2741
+ const ageDays = (now.getTime() - updatedAt.getTime()) / (1e3 * 60 * 60 * 24);
2742
+ return ageDays > 30;
2743
+ });
2744
+ const staleList = staleOpps.length > 0 ? staleOpps.map((p) => {
2745
+ const updatedAt = p.updatedAt ? new Date(p.updatedAt) : now;
2746
+ const staleDays = Math.ceil((now.getTime() - updatedAt.getTime()) / (1e3 * 60 * 60 * 24));
2747
+ return `| ${p.name} | ${p.client?.name || p.clientName || "N/A"} | ${p.lifecycleStage} | $${Number(p.proposalValue || 0).toLocaleString()} | ${staleDays} days |`;
2748
+ }).join("\n") : "| No stale opportunities | - | - | - | - |";
2749
+ const oppList = projects.map((p) => {
2750
+ const createdAt = p.createdAt ? new Date(p.createdAt) : now;
2751
+ const age = Math.ceil((now.getTime() - createdAt.getTime()) / (1e3 * 60 * 60 * 24));
2752
+ return `| ${p.name} | ${p.client?.name || p.clientName || "N/A"} | ${p.lifecycleStage} | $${Number(p.proposalValue || 0).toLocaleString()} | ${p.status} | ${age}d |`;
2753
+ }).join("\n") || "| No opportunities | - | - | - | - | - |";
2754
+ return `# Pipeline Health Review
2755
+ ${args.stage ? `Stage Filter: ${args.stage}` : "All Stages"}
2756
+ Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
2757
+
2758
+ ## Pipeline Summary
2759
+ - Total opportunities: ${data.totalCount}
2760
+ - Total value: $${totalValue.toLocaleString()}
2761
+ - Stale opportunities (>30 days): ${staleOpps.length}
2762
+
2763
+ ## Stage Breakdown
2764
+ | Stage | Count | Value | Avg Age (days) |
2765
+ |-------|-------|-------|----------------|
2766
+ ${stageTable}
2767
+
2768
+ ## All Opportunities
2769
+ | Name | Client | Stage | Value | Status | Age |
2770
+ |------|--------|-------|-------|--------|-----|
2771
+ ${oppList}
2772
+
2773
+ ## At-Risk Opportunities (Stale >30 days)
2774
+ | Name | Client | Stage | Value | Stale Days |
2775
+ |------|--------|-------|-------|------------|
2776
+ ${staleList}
2777
+
2778
+ Based on this data, please:
2779
+ 1. Assess overall pipeline health and velocity
2780
+ 2. Identify opportunities that need immediate attention
2781
+ 3. Recommend next actions for stale or at-risk opportunities
2782
+ 4. Suggest pipeline optimization strategies`;
2783
+ }
2784
+
2785
+ // src/prompts/invoice-draft.ts
2786
+ var invoice_draft_exports = {};
2787
+ __export(invoice_draft_exports, {
2788
+ generate: () => generate9
2789
+ });
2790
+ async function generate9(args) {
2791
+ const clientResult = await apiGet(`/prompts/clients/${args.clientId}`);
2792
+ const client = clientResult.client;
2793
+ if (!client) return `Client ${args.clientId} not found.`;
2794
+ const options = {};
2795
+ if (args.projectId) options.projectId = args.projectId;
2796
+ if (args.fromDate) options.fromDate = new Date(args.fromDate);
2797
+ if (args.toDate) options.toDate = new Date(args.toDate);
2798
+ const data = await fetchUnbilledTime(args.clientId, options);
2799
+ const byProject = {};
2800
+ for (const entry of data.entries) {
2801
+ const pid = entry.project?.id || "unknown";
2802
+ if (!byProject[pid]) byProject[pid] = { projectName: entry.project?.name || "Unknown", entries: [] };
2803
+ byProject[pid].entries.push(entry);
2804
+ }
2805
+ const entryRows = data.entries.length > 0 ? data.entries.map((e) => {
2806
+ const date = e.entryDate.split("T")[0];
2807
+ const person = e.person?.name || "Unknown";
2808
+ const proj = e.project?.name || "Unknown";
2809
+ const task = e.task?.name || "General";
2810
+ return `| ${date} | ${person} | ${proj} | ${task} | ${Number(e.hours)} | - | - |`;
2811
+ }).join("\n") : "| No unbilled entries found | - | - | - | - | - | - |";
2812
+ const projectSummary = Object.entries(byProject).map(([_pid, { projectName, entries }]) => {
2813
+ const hours = entries.reduce((sum, e) => sum + Number(e.hours), 0);
2814
+ return `### ${projectName}
2815
+ - Hours: ${hours.toFixed(1)}h
2816
+ - Entries: ${entries.length}`;
2817
+ }).join("\n\n");
2818
+ return `# Invoice Draft
2819
+ Client: ${client.name}
2820
+ ${args.projectId ? `Project: ${args.projectId}` : "All Projects"}
2821
+ Billing Period: ${args.fromDate || "Start of unbilled"} to ${args.toDate || "Today"}
2822
+
2823
+ ## Unbilled Time Entries
2824
+ | Date | Team Member | Project | Task | Hours | Rate | Amount |
2825
+ |------|-------------|---------|------|-------|------|--------|
2826
+ ${entryRows}
2827
+
2828
+ ## Summary
2829
+ - Total unbilled hours: ${data.totalHours.toFixed(1)}h
2830
+ - Number of entries: ${data.entries.length}
2831
+ - Projects: ${data.projects.length}
2832
+
2833
+ ## Line Items (Grouped by Project)
2834
+ ${projectSummary || "- No entries to group"}
2835
+
2836
+ ## Notes
2837
+ - Client: ${client.name}
2838
+ - Industry: ${client.industry || "N/A"}
2839
+
2840
+ Based on this data, please:
2841
+ 1. Calculate estimated amounts using standard billing rates (or note that rates need to be provided)
2842
+ 2. Draft professional invoice line items grouped by project
2843
+ 3. Include a summary table with subtotals per project
2844
+ 4. Note any entries that may need review (e.g., unusually high hours, missing descriptions)
2845
+ 5. Suggest payment terms and any special billing notes`;
2846
+ }
2847
+
2848
+ // src/prompts/client-update.ts
2849
+ var client_update_exports = {};
2850
+ __export(client_update_exports, {
2851
+ generate: () => generate10
2852
+ });
2853
+ async function generate10(args) {
2854
+ const data = await fetchClientActivity(args.clientId, 14);
2855
+ if (!data) return `Client ${args.clientId} not found.`;
2856
+ const { client, recentEntries, openTasks } = data;
2857
+ const topic = args.topic || "General Update";
2858
+ const projects = client.projects || [];
2859
+ const deliverables = projects.flatMap(
2860
+ (p) => (p.deliverables || []).map((d) => ({
2861
+ projectName: p.name,
2862
+ name: d.name,
2863
+ status: d.status,
2864
+ progress: Number(d.percentComplete || 0)
2865
+ }))
2866
+ );
2867
+ const recentActivity = (recentEntries || []).length > 0 ? (recentEntries || []).slice(0, 15).map((e) => {
2868
+ const date = typeof e.entryDate === "string" ? e.entryDate.split("T")[0] : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2869
+ return `- ${date}: ${e.project?.name || ""} \u2014 ${e.description || e.task?.name || "Work performed"} (${Number(e.hours)}h)`;
2870
+ }).join("\n") : "- No recent activity to report";
2871
+ const deliverablesList = deliverables.length > 0 ? deliverables.map((d) => `- ${d.projectName} / ${d.name}: ${d.status} (${d.progress}%)`).join("\n") : "- No deliverables to report";
2872
+ const now = /* @__PURE__ */ new Date();
2873
+ const upcomingItems = (openTasks || []).filter((t) => t.endDate && new Date(t.endDate) > now).slice(0, 10).map((t) => `- ${t.name} \u2014 due ${typeof t.endDate === "string" ? t.endDate.split("T")[0] : ""}`).join("\n") || "- No upcoming items";
2874
+ const globalContacts = client.globalContacts || [];
2875
+ const primaryContact = globalContacts[0];
2876
+ return `# Client Update Draft
2877
+ Client: ${client.name}
2878
+ Topic: ${topic}
2879
+ ${primaryContact ? `Primary Contact: ${primaryContact.firstName || ""} ${primaryContact.lastName || ""} (${primaryContact.title || "N/A"})` : ""}
2880
+
2881
+ ## Context Data
2882
+
2883
+ ### Recent Activity (Last 2 Weeks)
2884
+ ${recentActivity}
2885
+
2886
+ ### Deliverable Status
2887
+ ${deliverablesList}
2888
+
2889
+ ### Upcoming Milestones
2890
+ ${upcomingItems}
2891
+
2892
+ ### Active Projects
2893
+ ${projects.map((p) => `- ${p.name}: ${p.status} (${p.lifecycleStage || "N/A"})`).join("\n") || "- None"}
2894
+
2895
+ Based on this data, please draft a professional client update email that:
2896
+ 1. Opens with a brief, warm greeting${primaryContact ? ` to ${primaryContact.firstName || ""}` : ""}
2897
+ 2. Summarizes progress on the topic: "${topic}"
2898
+ 3. Highlights key deliverables and their status
2899
+ 4. Notes upcoming milestones or deadlines
2900
+ 5. Calls out any items needing client input or decisions
2901
+ 6. Closes professionally with next steps`;
2902
+ }
2903
+
2904
+ // src/prompts/handoff-notes.ts
2905
+ var handoff_notes_exports = {};
2906
+ __export(handoff_notes_exports, {
2907
+ generate: () => generate11
2908
+ });
2909
+ async function generate11(args) {
2910
+ const data = await fetchProjectWithDetails(args.projectId);
2911
+ if (!data) return `Project ${args.projectId} not found.`;
2912
+ const { project, taskStats } = data;
2913
+ const fromPerson = await fetchPersonByUserId(args.fromUserId);
2914
+ const toPerson = args.toUserId ? await fetchPersonByUserId(args.toUserId) : null;
2915
+ const scope = args.scope || "full";
2916
+ const fromTasks = fromPerson ? await fetchPersonTasks(fromPerson.id, {
2917
+ status: ["NOT_STARTED", "IN_PROGRESS", "BLOCKED"]
2918
+ }) : [];
2919
+ const projectTasks = fromTasks.filter((t) => t.project?.id === args.projectId);
2920
+ const decisions = await fetchProjectDecisions(args.projectId);
2921
+ const clientId = project.clientId;
2922
+ let contacts = [];
2923
+ if (clientId) {
2924
+ const contactsResult = await apiGet(`/prompts/clients/${clientId}/contacts`);
2925
+ contacts = contactsResult.contacts;
2926
+ }
2927
+ const documentsResult = await apiGet(`/prompts/projects/${args.projectId}/documents`);
2928
+ const documents = documentsResult.documents;
2929
+ const memoriesResult = await apiGet(`/prompts/projects/${args.projectId}/memories`);
2930
+ const memories = memoriesResult.memories;
2931
+ const taskTable = projectTasks.length > 0 ? projectTasks.map((t) => {
2932
+ const due = t.endDate ? t.endDate.split("T")[0] : "No date";
2933
+ return `| ${t.name} | ${t.status} | ${due} | ${(t.description || "").slice(0, 80) || "N/A"} |`;
2934
+ }).join("\n") : "| No assigned tasks | - | - | - |";
2935
+ const decisionList = decisions.length > 0 ? decisions.map((d) => `- ${d.createdAt.split("T")[0]}: **${d.title}** \u2014 ${d.decision}
2936
+ Rationale: ${d.rationale || "N/A"}`).join("\n") : "- No decisions recorded";
2937
+ const contactList = contacts.length > 0 ? contacts.map((c) => `- ${c.firstName || ""} ${c.lastName || ""} (${c.title || "N/A"}, ${c.role || "N/A"}) \u2014 ${c.email || "No email"}`).join("\n") : "- No contacts on file";
2938
+ const docList = documents.length > 0 ? documents.map((d) => `- ${d.name || d.title} [${d.type || "N/A"}] \u2014 ${d.status} (updated ${d.updatedAt?.split("T")[0] || "N/A"})`).join("\n") : "- No documents";
2939
+ const tribalKnowledge = memories.length > 0 ? memories.map((m) => `- [${m.category}] ${m.content}`).join("\n") : "- No agent memories recorded for this project";
2940
+ const typedProject = project;
2941
+ return `# Handoff Notes
2942
+ Project: ${project.name}
2943
+ Client: ${typedProject.client?.name || typedProject.clientName || "N/A"}
2944
+ From: ${fromPerson?.name || args.fromUserId}
2945
+ To: ${toPerson?.name || args.toUserId || "TBD"}
2946
+ Scope: ${scope}
2947
+
2948
+ ## Project Overview
2949
+ - Status: ${project.status}
2950
+ - Phase: ${project.lifecycleStage || "N/A"}
2951
+ - Start: ${project.startDate?.split("T")[0] || "Not set"}
2952
+ - End: ${project.endDate?.split("T")[0] || "Not set"}
2953
+ - Tasks: ${taskStats?.total || 0} total (${taskStats?.completed || 0} completed, ${taskStats?.inProgress || 0} in progress, ${taskStats?.blocked || 0} blocked)
2954
+
2955
+ ## In-Progress Tasks (assigned to ${fromPerson?.name || "outgoing person"})
2956
+ | Task | Status | Due | Notes |
2957
+ |------|--------|-----|-------|
2958
+ ${taskTable}
2959
+
2960
+ ## Key Decisions Made
2961
+ ${decisionList}
2962
+
2963
+ ## Key Contacts
2964
+ ${contactList}
2965
+
2966
+ ## Important Documents
2967
+ ${docList}
2968
+
2969
+ ## Tribal Knowledge (Agent Memories)
2970
+ ${tribalKnowledge}
2971
+
2972
+ Based on this data, please:
2973
+ 1. Write a concise handoff summary (what the new person needs to know most urgently)
2974
+ 2. List the top 3 priority items the incoming person should address first
2975
+ 3. Highlight any open risks, blockers, or sensitive topics
2976
+ 4. Suggest a recommended first-week action plan`;
2977
+ }
2978
+
2979
+ // src/prompts/capability-gap.ts
2980
+ var capability_gap_exports = {};
2981
+ __export(capability_gap_exports, {
2982
+ generate: () => generate12
2983
+ });
2984
+ async function generate12(args) {
2985
+ const result = await apiGet("/prompts/capabilities");
2986
+ const capabilities = result.capabilities;
2987
+ const matrix = {};
2988
+ for (const cap of capabilities) {
2989
+ const cat = cap.category;
2990
+ const name = cap.name;
2991
+ if (!matrix[cat]) matrix[cat] = {};
2992
+ if (!matrix[cat][name]) matrix[cat][name] = [];
2993
+ matrix[cat][name].push({
2994
+ person: cap.person.name,
2995
+ proficiency: cap.proficiency,
2996
+ evidenced: cap.evidenced
2997
+ });
2998
+ }
2999
+ const singlePoints = [];
3000
+ for (const [cat, caps] of Object.entries(matrix)) {
3001
+ for (const [name, people] of Object.entries(caps)) {
3002
+ if (people.length === 1) {
3003
+ singlePoints.push({ category: cat, capability: name, person: people[0].person });
3004
+ }
3005
+ }
3006
+ }
3007
+ const projectsResult = await apiGet("/prompts/projects/active");
3008
+ const activeProjects = projectsResult.projects;
3009
+ const proficiencyCounts = { NOVICE: 0, COMPETENT: 0, PROFICIENT: 0, EXPERT: 0 };
3010
+ for (const cap of capabilities) {
3011
+ if (proficiencyCounts[cap.proficiency] !== void 0) {
3012
+ proficiencyCounts[cap.proficiency]++;
3013
+ }
3014
+ }
3015
+ const matrixOutput = Object.entries(matrix).map(([cat, caps]) => {
3016
+ const rows = Object.entries(caps).map(([name, people]) => {
3017
+ const peopleStr = people.map((p) => `${p.person} (${p.proficiency}${p.evidenced ? ", evidenced" : ""})`).join(", ");
3018
+ return `| ${name} | ${people.length} | ${peopleStr} |`;
3019
+ }).join("\n");
3020
+ return `### ${cat}
3021
+ | Capability | Coverage | People |
3022
+ |-----------|----------|--------|
3023
+ ${rows}`;
3024
+ }).join("\n\n");
3025
+ const spofList = singlePoints.length > 0 ? singlePoints.map((s) => `| ${s.capability} | ${s.category} | ${s.person} |`).join("\n") : "| No single points of failure | - | - |";
3026
+ const uniqueCapabilities = new Set(capabilities.map((c) => c.name)).size;
3027
+ const uniquePeople = new Set(capabilities.map((c) => c.person.id)).size;
3028
+ return `# Capability Gap Analysis
3029
+ Organization Scope
3030
+ Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
3031
+
3032
+ ## Overview
3033
+ - Total capability entries: ${capabilities.length}
3034
+ - Unique capabilities: ${uniqueCapabilities}
3035
+ - People with capabilities: ${uniquePeople}
3036
+ - Categories: ${Object.keys(matrix).length}
3037
+
3038
+ ## Proficiency Distribution
3039
+ | Level | Count |
3040
+ |-------|-------|
3041
+ | Expert | ${proficiencyCounts.EXPERT} |
3042
+ | Proficient | ${proficiencyCounts.PROFICIENT} |
3043
+ | Competent | ${proficiencyCounts.COMPETENT} |
3044
+ | Novice | ${proficiencyCounts.NOVICE} |
3045
+
3046
+ ## Capability Matrix
3047
+ ${matrixOutput || "- No capabilities recorded"}
3048
+
3049
+ ## Single Points of Failure (only 1 person)
3050
+ | Capability | Category | Person |
3051
+ |-----------|----------|--------|
3052
+ ${spofList}
3053
+
3054
+ ## Active Project Types
3055
+ ${activeProjects.map((p) => {
3056
+ const types = [...new Set((p.deliverables || []).map((d) => d.type))];
3057
+ return `- ${p.name}: ${types.join(", ") || "No deliverables"}`;
3058
+ }).join("\n") || "- No active projects"}
3059
+
3060
+ Based on this data, please:
3061
+ 1. Identify the most critical capability gaps (considering project needs)
3062
+ 2. Highlight single points of failure that pose business risk
3063
+ 3. Recommend training or hiring priorities
3064
+ 4. Suggest cross-training opportunities to improve coverage`;
3065
+ }
3066
+
3067
+ // src/prompts/team-availability.ts
3068
+ var team_availability_exports = {};
3069
+ __export(team_availability_exports, {
3070
+ generate: () => generate13
3071
+ });
3072
+ async function generate13(args) {
3073
+ const weeksAhead = parseInt(args.weeks || "2", 10);
3074
+ const startDate = /* @__PURE__ */ new Date();
3075
+ startDate.setHours(0, 0, 0, 0);
3076
+ const endDate = /* @__PURE__ */ new Date();
3077
+ endDate.setDate(endDate.getDate() + weeksAhead * 7);
3078
+ const { days, utilization } = await fetchTeamAvailability(startDate, endDate);
3079
+ const peopleResult = await apiGet("/prompts/people");
3080
+ const people = peopleResult.people;
3081
+ const personSummary = people.map((p) => {
3082
+ const personDays = days.filter((d) => d.person.id === p.id);
3083
+ const available = personDays.filter((d) => d.status === "AVAILABLE").length;
3084
+ const leave = personDays.filter((d) => d.status === "LEAVE").length;
3085
+ const partial = personDays.filter((d) => d.status === "PARTIALLY_AVAILABLE").length;
3086
+ const unavailable = personDays.filter((d) => d.status === "UNAVAILABLE").length;
3087
+ const holiday = personDays.filter((d) => d.status === "PUBLIC_HOLIDAY").length;
3088
+ const totalAllocated = p.allocations.reduce((sum, a) => sum + Number(a.hoursPerWeek || 0), 0);
3089
+ return {
3090
+ name: p.name,
3091
+ available,
3092
+ leave,
3093
+ partial,
3094
+ unavailable,
3095
+ holiday,
3096
+ totalDays: personDays.length,
3097
+ allocatedHoursPerWeek: totalAllocated,
3098
+ projects: p.allocations.map((a) => `${a.project.name} (${Number(a.hoursPerWeek || 0)}h)`)
3099
+ };
3100
+ });
3101
+ const leaveDays = days.filter((d) => d.status === "LEAVE" || d.status === "UNAVAILABLE").map((d) => `| ${d.person.name} | ${d.date.split("T")[0]} | ${d.status} | ${d.notes || ""} |`);
3102
+ const utilRows = utilization.map((u) => {
3103
+ return `| ${u.person.name} | ${u.weekStart.split("T")[0]} | ${Number(u.billableHours || 0)}h | ${Number(u.nonBillableHours || 0)}h | ${Number(u.leaveHours || 0)}h | ${Number(u.utilizationRate || 0).toFixed(0)}% |`;
3104
+ });
3105
+ const summaryTable = personSummary.map(
3106
+ (p) => `| ${p.name} | ${p.available} | ${p.leave} | ${p.partial} | ${p.unavailable} | ${p.allocatedHoursPerWeek}h | ${p.projects.join(", ") || "None"} |`
3107
+ ).join("\n");
3108
+ return `# Team Availability Overview
3109
+ Period: ${startDate.toISOString().split("T")[0]} to ${endDate.toISOString().split("T")[0]} (${weeksAhead} weeks)
3110
+ Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
3111
+
3112
+ ## Team Summary
3113
+ | Person | Available | Leave | Partial | Unavailable | Allocated/week | Projects |
3114
+ |--------|-----------|-------|---------|-------------|----------------|----------|
3115
+ ${summaryTable || "| No data | - | - | - | - | - | - |"}
3116
+
3117
+ ## Upcoming Leave / Unavailability
3118
+ | Person | Date | Status | Note |
3119
+ |--------|------|--------|------|
3120
+ ${leaveDays.length > 0 ? leaveDays.join("\n") : "| No leave scheduled | - | - | - |"}
3121
+
3122
+ ## Utilization Records
3123
+ | Person | Week | Billable | Non-Billable | Leave | Rate |
3124
+ |--------|------|----------|-------------|-------|------|
3125
+ ${utilRows.length > 0 ? utilRows.join("\n") : "| No utilization data | - | - | - | - | - |"}
3126
+
3127
+ ## Key Stats
3128
+ - Total team members: ${people.length}
3129
+ - People on leave this period: ${personSummary.filter((p) => p.leave > 0).length}
3130
+ - Avg allocated hours/week: ${people.length > 0 ? (personSummary.reduce((sum, p) => sum + p.allocatedHoursPerWeek, 0) / people.length).toFixed(1) : 0}h
3131
+
3132
+ Based on this data, please:
3133
+ 1. Identify team members with capacity for additional work
3134
+ 2. Flag any upcoming coverage gaps or leave conflicts
3135
+ 3. Highlight anyone who appears over-allocated
3136
+ 4. Recommend any scheduling adjustments needed`;
3137
+ }
3138
+
3139
+ // src/prompts/index.ts
3140
+ var prompts = [
3141
+ {
3142
+ name: "weekly_status",
3143
+ description: "Generate a weekly status report for a project with real task, time, and deliverable data",
3144
+ arguments: [
3145
+ { name: "projectId", description: "Project ID", required: true },
3146
+ { name: "week", description: "Week number or date range", required: false }
3147
+ ]
3148
+ },
3149
+ {
3150
+ name: "daily_standup",
3151
+ description: "Generate daily standup summary with completed work, planned tasks, and blockers",
3152
+ arguments: [
3153
+ { name: "userId", description: "User ID for the standup", required: true },
3154
+ { name: "date", description: "Date for the standup (defaults to today)", required: false },
3155
+ { name: "projectId", description: "Filter to a specific project", required: false }
3156
+ ]
3157
+ },
3158
+ {
3159
+ name: "project_health",
3160
+ description: "Analyze project health including budget burn, timeline adherence, and risk exposure",
3161
+ arguments: [
3162
+ { name: "projectId", description: "Project ID to analyze", required: true },
3163
+ { name: "includeForecasts", description: "Include budget and timeline forecasts (true/false)", required: false }
3164
+ ]
3165
+ },
3166
+ {
3167
+ name: "meeting_prep",
3168
+ description: "Prepare for a client meeting with recent activity, open items, and talking points",
3169
+ arguments: [
3170
+ { name: "clientId", description: "Client ID for the meeting", required: true },
3171
+ { name: "projectId", description: "Filter to a specific project", required: false },
3172
+ { name: "meetingType", description: "Type of meeting (kickoff, status, review, escalation)", required: false }
3173
+ ]
3174
+ },
3175
+ {
3176
+ name: "compliance_check",
3177
+ description: "Review compliance status and upcoming credential expirations",
3178
+ arguments: [
3179
+ { name: "projectId", description: "Project ID (optional, all if omitted)", required: false },
3180
+ { name: "daysAhead", description: "Look ahead days for expiries (default 30)", required: false }
3181
+ ]
3182
+ },
3183
+ {
3184
+ name: "resource_plan",
3185
+ description: "Generate resource allocation recommendation with availability and capability data",
3186
+ arguments: [
3187
+ { name: "projectId", description: "Project ID", required: true },
3188
+ { name: "timeframe", description: "Planning timeframe in weeks (default 4)", required: false }
3189
+ ]
3190
+ },
3191
+ {
3192
+ name: "risk_assessment",
3193
+ description: "Assess and summarize risks for a project using alerts, tasks, compliance, and agent memories",
3194
+ arguments: [
3195
+ { name: "projectId", description: "Project ID", required: true }
3196
+ ]
3197
+ },
3198
+ {
3199
+ name: "pipeline_review",
3200
+ description: "Analyze pipeline health, stage distribution, and stale opportunities",
3201
+ arguments: [
3202
+ { name: "stage", description: "Filter by lifecycle stage (optional)", required: false }
3203
+ ]
3204
+ },
3205
+ {
3206
+ name: "invoice_draft",
3207
+ description: "Draft an invoice based on unbilled time entries for a client",
3208
+ arguments: [
3209
+ { name: "clientId", description: "Client ID to invoice", required: true },
3210
+ { name: "projectId", description: "Filter to a specific project", required: false },
3211
+ { name: "fromDate", description: "Start date for billable period (ISO format)", required: false },
3212
+ { name: "toDate", description: "End date for billable period (ISO format)", required: false }
3213
+ ]
3214
+ },
3215
+ {
3216
+ name: "client_update",
3217
+ description: "Draft a client update email with recent activity and deliverable progress",
3218
+ arguments: [
3219
+ { name: "clientId", description: "Client ID", required: true },
3220
+ { name: "topic", description: "Update topic", required: false }
3221
+ ]
3222
+ },
3223
+ {
3224
+ name: "handoff_notes",
3225
+ description: "Generate handoff notes when reassigning work with tasks, decisions, and tribal knowledge",
3226
+ arguments: [
3227
+ { name: "projectId", description: "Project ID being handed off", required: true },
3228
+ { name: "fromUserId", description: "Current assignee user ID", required: true },
3229
+ { name: "toUserId", description: "New assignee user ID", required: false },
3230
+ { name: "scope", description: "Scope of handoff (full, partial)", required: false }
3231
+ ]
3232
+ },
3233
+ {
3234
+ name: "capability_gap",
3235
+ description: "Analyze capability matrix to identify gaps, single points of failure, and training needs",
3236
+ arguments: []
3237
+ },
3238
+ {
3239
+ name: "team_availability",
3240
+ description: "Team calendar overview with availability, upcoming leave, and capacity analysis",
3241
+ arguments: [
3242
+ { name: "weeks", description: "Number of weeks to look ahead (default 2)", required: false }
3243
+ ]
3244
+ }
3245
+ ];
3246
+ var generators = {
3247
+ weekly_status: weekly_status_exports,
3248
+ daily_standup: daily_standup_exports,
3249
+ project_health: project_health_exports,
3250
+ meeting_prep: meeting_prep_exports,
3251
+ compliance_check: compliance_check_exports,
3252
+ resource_plan: resource_plan_exports,
3253
+ risk_assessment: risk_assessment_exports,
3254
+ pipeline_review: pipeline_review_exports,
3255
+ invoice_draft: invoice_draft_exports,
3256
+ client_update: client_update_exports,
3257
+ handoff_notes: handoff_notes_exports,
3258
+ capability_gap: capability_gap_exports,
3259
+ team_availability: team_availability_exports
3260
+ };
3261
+ async function generatePromptContent(name, args) {
3262
+ const generator = generators[name];
3263
+ if (!generator) {
3264
+ throw new Error(`Unknown prompt: ${name}`);
3265
+ }
3266
+ return generator.generate(args);
3267
+ }
3268
+
3269
+ // src/auth/api-key.ts
3270
+ import { z as z19 } from "zod";
3271
+ var apiKeyPayloadSchema = z19.object({
3272
+ orgId: z19.string(),
3273
+ permissions: z19.array(z19.enum(["read", "write", "admin"])),
3274
+ expiresAt: z19.date().optional()
3275
+ });
3276
+ function isValidApiKeyFormat(key) {
3277
+ return /^amplis_[a-f0-9]{64}$/.test(key);
3278
+ }
3279
+ async function validateApiKey(apiKey) {
3280
+ const baseUrl = process.env.AMPLIS_API_URL || "https://app.amplis.com.au";
3281
+ try {
3282
+ const response = await fetch(`${baseUrl}/api/mcp/auth/validate`, {
3283
+ method: "POST",
3284
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }
3285
+ });
3286
+ if (!response.ok) return null;
3287
+ const data = await response.json();
3288
+ return { orgId: data.orgId, permissions: data.permissions.map((p) => p.toLowerCase()), expiresAt: data.expiresAt ? new Date(data.expiresAt) : void 0 };
3289
+ } catch {
3290
+ return null;
3291
+ }
3292
+ }
3293
+
3294
+ // src/lib/context.ts
3295
+ var cachedContext = null;
3296
+ async function initializeContext() {
3297
+ if (process.env.AMPLIS_ORG_ID) {
3298
+ cachedContext = {
3299
+ orgId: process.env.AMPLIS_ORG_ID,
3300
+ userId: process.env.AMPLIS_USER_ID
3301
+ };
3302
+ return cachedContext;
3303
+ }
3304
+ const creds = loadCredentials();
3305
+ if (!creds) {
3306
+ throw new Error(
3307
+ "No credentials found. Run `amplis login` or set AMPLIS_ORG_ID environment variable."
3308
+ );
3309
+ }
3310
+ if (!creds.apiKey || !isValidApiKeyFormat(creds.apiKey)) {
3311
+ throw new Error(
3312
+ "Invalid API key format in credentials. Run `amplis login` to re-authenticate."
3313
+ );
3314
+ }
3315
+ const payload = await validateApiKey(creds.apiKey);
3316
+ if (!payload) {
3317
+ throw new Error(
3318
+ "API key is invalid, revoked, or expired. Run `amplis login` to re-authenticate."
3319
+ );
3320
+ }
3321
+ if (payload.orgId !== creds.orgId) {
3322
+ throw new Error(
3323
+ "API key org does not match stored credentials. Run `amplis login` to re-authenticate."
3324
+ );
3325
+ }
3326
+ cachedContext = {
3327
+ orgId: payload.orgId,
3328
+ userId: creds.userId
3329
+ };
3330
+ process.env.AMPLIS_ORG_ID = payload.orgId;
3331
+ if (creds.userId) {
3332
+ process.env.AMPLIS_USER_ID = creds.userId;
3333
+ }
3334
+ return cachedContext;
3335
+ }
3336
+
3337
+ // src/cli/login.ts
3338
+ import readline from "readline";
3339
+ var DEFAULT_API_URL = "https://app.amplis.com.au";
3340
+ function prompt(question, masked = false) {
3341
+ return new Promise((resolve) => {
3342
+ const rl = readline.createInterface({
3343
+ input: process.stdin,
3344
+ output: process.stdout
3345
+ });
3346
+ if (masked && process.stdin.isTTY) {
3347
+ process.stdout.write(question);
3348
+ let input = "";
3349
+ process.stdin.setRawMode(true);
3350
+ process.stdin.resume();
3351
+ process.stdin.setEncoding("utf-8");
3352
+ const onData = (char) => {
3353
+ if (char === "\n" || char === "\r" || char === "") {
3354
+ process.stdin.setRawMode(false);
3355
+ process.stdin.pause();
3356
+ process.stdin.removeListener("data", onData);
3357
+ process.stdout.write("\n");
3358
+ rl.close();
3359
+ resolve(input);
3360
+ } else if (char === "") {
3361
+ process.stdout.write("\n");
3362
+ process.exit(1);
3363
+ } else if (char === "\x7F" || char === "\b") {
3364
+ if (input.length > 0) {
3365
+ input = input.slice(0, -1);
3366
+ process.stdout.write("\b \b");
3367
+ }
3368
+ } else {
3369
+ input += char;
3370
+ process.stdout.write("*");
3371
+ }
3372
+ };
3373
+ process.stdin.on("data", onData);
3374
+ } else {
3375
+ rl.question(question, (answer) => {
3376
+ rl.close();
3377
+ resolve(answer.trim());
3378
+ });
3379
+ }
3380
+ });
3381
+ }
3382
+ async function loginCommand() {
3383
+ console.log("\n AMPLIS MCP Server \u2014 Authentication\n");
3384
+ console.log(
3385
+ " Generate an API key in the AMPLIS web app:\n Settings \u2192 API Keys \u2192 Create New Key\n"
3386
+ );
3387
+ const apiKey = await prompt(" Enter your API key: ", true);
3388
+ if (!apiKey) {
3389
+ console.error("\n Error: No API key provided.");
3390
+ process.exit(1);
3391
+ }
3392
+ if (!isValidApiKeyFormat(apiKey)) {
3393
+ console.error(
3394
+ "\n Error: Invalid API key format.\n Keys should look like: amplis_a1b2c3d4e5f6..."
3395
+ );
3396
+ process.exit(1);
3397
+ }
3398
+ const apiUrl = process.env.AMPLIS_API_URL || DEFAULT_API_URL;
3399
+ console.log("\n Validating API key...");
3400
+ let response;
3401
+ try {
3402
+ response = await fetch(`${apiUrl}/api/mcp/auth/validate`, {
3403
+ method: "POST",
3404
+ headers: {
3405
+ Authorization: `Bearer ${apiKey}`,
3406
+ "Content-Type": "application/json"
3407
+ }
3408
+ });
3409
+ } catch (err) {
3410
+ console.error(
3411
+ `
3412
+ Error: Could not connect to ${apiUrl}
3413
+ Check your internet connection or set AMPLIS_API_URL.
3414
+ `
3415
+ );
3416
+ process.exit(1);
3417
+ }
3418
+ if (!response.ok) {
3419
+ const body = await response.json().catch(() => ({}));
3420
+ const msg = body.error || `HTTP ${response.status}`;
3421
+ console.error(`
3422
+ Authentication failed: ${msg}
3423
+ `);
3424
+ process.exit(1);
3425
+ }
3426
+ const data = await response.json();
3427
+ saveCredentials({
3428
+ apiKey,
3429
+ orgId: data.orgId,
3430
+ orgName: data.orgName,
3431
+ userId: data.userId,
3432
+ permissions: data.permissions
3433
+ });
3434
+ console.log("\n Authenticated successfully!\n");
3435
+ console.log(` Organization : ${data.orgName}`);
3436
+ console.log(` Permissions : ${data.permissions.join(", ")}`);
3437
+ console.log(` Key prefix : ${apiKey.substring(0, 16)}...`);
3438
+ console.log("\n Credentials saved to ~/.amplis/credentials.json\n");
3439
+ }
3440
+
3441
+ // src/cli/browser-login.ts
3442
+ import http from "http";
3443
+ import { URL } from "url";
3444
+ import crypto from "crypto";
3445
+ import open from "open";
3446
+ var DEFAULT_API_URL2 = "https://app.amplis.com.au";
3447
+ var CALLBACK_PORT = 19284;
3448
+ var TIMEOUT_MS = 12e4;
3449
+ async function browserLoginCommand() {
3450
+ console.log("\n \u{1F510} AMPLIS MCP Server \u2014 Browser Authentication\n");
3451
+ const apiUrl = process.env.AMPLIS_API_URL || DEFAULT_API_URL2;
3452
+ const state = crypto.randomBytes(32).toString("hex");
3453
+ const callbackUrl = `http://localhost:${CALLBACK_PORT}/callback`;
3454
+ const authPromise = new Promise((resolve, reject) => {
3455
+ const server = http.createServer(async (req, res) => {
3456
+ const url = new URL(req.url || "/", `http://localhost:${CALLBACK_PORT}`);
3457
+ if (url.pathname === "/callback") {
3458
+ const code = url.searchParams.get("code");
3459
+ const returnedState = url.searchParams.get("state");
3460
+ const error2 = url.searchParams.get("error");
3461
+ if (error2) {
3462
+ res.writeHead(400, { "Content-Type": "text/html" });
3463
+ res.end(`
3464
+ <html><body style="font-family: system-ui; padding: 40px; text-align: center;">
3465
+ <h1 style="color: #dc2626;">\u274C Authentication Failed</h1>
3466
+ <p>${error2}</p>
3467
+ <p>You can close this window.</p>
3468
+ </body></html>
3469
+ `);
3470
+ server.close();
3471
+ reject(new Error(error2));
3472
+ return;
3473
+ }
3474
+ if (returnedState !== state) {
3475
+ res.writeHead(400, { "Content-Type": "text/html" });
3476
+ res.end(`
3477
+ <html><body style="font-family: system-ui; padding: 40px; text-align: center;">
3478
+ <h1 style="color: #dc2626;">\u274C Invalid State</h1>
3479
+ <p>Security validation failed. Please try again.</p>
3480
+ <p>You can close this window.</p>
3481
+ </body></html>
3482
+ `);
3483
+ server.close();
3484
+ reject(new Error("Invalid state parameter"));
3485
+ return;
3486
+ }
3487
+ if (!code) {
3488
+ res.writeHead(400, { "Content-Type": "text/html" });
3489
+ res.end(`
3490
+ <html><body style="font-family: system-ui; padding: 40px; text-align: center;">
3491
+ <h1 style="color: #dc2626;">\u274C Missing Code</h1>
3492
+ <p>No authorization code received.</p>
3493
+ <p>You can close this window.</p>
3494
+ </body></html>
3495
+ `);
3496
+ server.close();
3497
+ reject(new Error("Missing authorization code"));
3498
+ return;
3499
+ }
3500
+ try {
3501
+ const exchangeResponse = await fetch(`${apiUrl}/api/mcp/auth/exchange`, {
3502
+ method: "POST",
3503
+ headers: { "Content-Type": "application/json" },
3504
+ body: JSON.stringify({ code, state })
3505
+ });
3506
+ if (!exchangeResponse.ok) {
3507
+ const errorBody = await exchangeResponse.json().catch(() => ({}));
3508
+ throw new Error(errorBody.error || `HTTP ${exchangeResponse.status}`);
3509
+ }
3510
+ const result = await exchangeResponse.json();
3511
+ res.writeHead(200, { "Content-Type": "text/html" });
3512
+ res.end(`
3513
+ <html><body style="font-family: system-ui; padding: 40px; text-align: center;">
3514
+ <h1 style="color: #16a34a;">\u2705 Authentication Successful!</h1>
3515
+ <p>Logged in to <strong>${result.orgName}</strong></p>
3516
+ <p>You can close this window and return to your terminal.</p>
3517
+ <script>setTimeout(() => window.close(), 3000);</script>
3518
+ </body></html>
3519
+ `);
3520
+ server.close();
3521
+ resolve(result);
3522
+ } catch (err) {
3523
+ res.writeHead(500, { "Content-Type": "text/html" });
3524
+ res.end(`
3525
+ <html><body style="font-family: system-ui; padding: 40px; text-align: center;">
3526
+ <h1 style="color: #dc2626;">\u274C Exchange Failed</h1>
3527
+ <p>${err instanceof Error ? err.message : "Unknown error"}</p>
3528
+ <p>You can close this window.</p>
3529
+ </body></html>
3530
+ `);
3531
+ server.close();
3532
+ reject(err);
3533
+ }
3534
+ } else {
3535
+ res.writeHead(404);
3536
+ res.end("Not found");
3537
+ }
3538
+ });
3539
+ server.listen(CALLBACK_PORT, "127.0.0.1", () => {
3540
+ console.log(` \u{1F4E1} Callback server listening on port ${CALLBACK_PORT}`);
3541
+ });
3542
+ server.on("error", (err) => {
3543
+ reject(new Error(`Failed to start callback server: ${err.message}`));
3544
+ });
3545
+ setTimeout(() => {
3546
+ server.close();
3547
+ reject(new Error("Authentication timed out. Please try again."));
3548
+ }, TIMEOUT_MS);
3549
+ });
3550
+ const authUrl = new URL(`${apiUrl}/auth/mcp`);
3551
+ authUrl.searchParams.set("callback", callbackUrl);
3552
+ authUrl.searchParams.set("state", state);
3553
+ console.log(` \u{1F310} Opening browser to authenticate...
3554
+ `);
3555
+ console.log(` If the browser doesn't open, visit:`);
3556
+ console.log(` ${authUrl.toString()}
3557
+ `);
3558
+ try {
3559
+ await open(authUrl.toString());
3560
+ } catch {
3561
+ }
3562
+ console.log(" \u23F3 Waiting for authentication...\n");
3563
+ try {
3564
+ const result = await authPromise;
3565
+ saveCredentials({
3566
+ apiKey: result.apiKey,
3567
+ orgId: result.orgId,
3568
+ orgName: result.orgName,
3569
+ userId: result.userId,
3570
+ permissions: result.permissions
3571
+ });
3572
+ console.log(" \u2705 Authentication successful!\n");
3573
+ console.log(` Organization : ${result.orgName}`);
3574
+ console.log(` Permissions : ${result.permissions.join(", ")}`);
3575
+ console.log(` Key prefix : ${result.apiKey.substring(0, 16)}...`);
3576
+ console.log("\n Credentials saved to ~/.amplis/credentials.json\n");
3577
+ } catch (err) {
3578
+ console.error(`
3579
+ \u274C Authentication failed: ${err instanceof Error ? err.message : "Unknown error"}
3580
+ `);
3581
+ process.exit(1);
3582
+ }
3583
+ }
3584
+
3585
+ // src/cli/logout.ts
3586
+ async function logoutCommand() {
3587
+ const removed = clearCredentials();
3588
+ if (removed) {
3589
+ console.log("\n Credentials removed from", getCredentialsPath());
3590
+ console.log(" You are now logged out.\n");
3591
+ } else {
3592
+ console.log("\n No credentials found \u2014 already logged out.\n");
3593
+ }
3594
+ }
3595
+
3596
+ // src/cli/status.ts
3597
+ async function statusCommand() {
3598
+ console.log("\n AMPLIS MCP Server \u2014 Status\n");
3599
+ const envOrgId = process.env.AMPLIS_ORG_ID;
3600
+ if (envOrgId) {
3601
+ console.log(" Auth source : Environment variables");
3602
+ console.log(` Org ID : ${envOrgId}`);
3603
+ console.log(` User ID : ${process.env.AMPLIS_USER_ID || "(not set)"}`);
3604
+ console.log("");
3605
+ return;
3606
+ }
3607
+ const creds = loadCredentials();
3608
+ if (!creds) {
3609
+ console.log(" Status : Not authenticated");
3610
+ console.log(` Config file : ${getCredentialsPath()} (not found)`);
3611
+ console.log("\n Run `amplis login` to authenticate.\n");
3612
+ return;
3613
+ }
3614
+ const maskedKey = creds.apiKey ? `${creds.apiKey.substring(0, 16)}...` : "(unknown)";
3615
+ console.log(" Auth source : ~/.amplis/credentials.json");
3616
+ console.log(` Organization : ${creds.orgName || creds.orgId}`);
3617
+ console.log(` Org ID : ${creds.orgId}`);
3618
+ console.log(` API key : ${maskedKey}`);
3619
+ console.log(
3620
+ ` Permissions : ${creds.permissions?.join(", ") || "(unknown)"}`
3621
+ );
3622
+ console.log("");
3623
+ }
3624
+
3625
+ // src/cli/index.ts
3626
+ var HELP = `
3627
+ AMPLIS MCP Server
3628
+
3629
+ Usage:
3630
+ amplis [command]
3631
+
3632
+ Commands:
3633
+ serve Start the MCP server (default)
3634
+ login Authenticate via browser (recommended)
3635
+ login-key Authenticate with API key (manual)
3636
+ logout Remove stored credentials
3637
+ status Show current authentication status
3638
+ help Show this help message
3639
+
3640
+ Environment Variables:
3641
+ AMPLIS_ORG_ID Override org ID (skips credentials file)
3642
+ AMPLIS_USER_ID Override user ID
3643
+ AMPLIS_API_URL API base URL (default: https://app.amplis.com.au)
3644
+ `;
3645
+ function parseCLICommand(argv) {
3646
+ const cmd = argv[2]?.toLowerCase();
3647
+ switch (cmd) {
3648
+ case "login":
3649
+ return "login";
3650
+ case "login-key":
3651
+ return "login-key";
3652
+ case "logout":
3653
+ return "logout";
3654
+ case "status":
3655
+ return "status";
3656
+ case "help":
3657
+ case "--help":
3658
+ case "-h":
3659
+ return "help";
3660
+ default:
3661
+ return "serve";
3662
+ }
3663
+ }
3664
+ async function runCLI(command2) {
3665
+ switch (command2) {
3666
+ case "login":
3667
+ await browserLoginCommand();
3668
+ return true;
3669
+ case "login-key":
3670
+ await loginCommand();
3671
+ return true;
3672
+ case "logout":
3673
+ await logoutCommand();
3674
+ return true;
3675
+ case "status":
3676
+ await statusCommand();
3677
+ return true;
3678
+ case "help":
3679
+ console.log(HELP);
3680
+ return true;
3681
+ case "serve":
3682
+ return false;
3683
+ }
3684
+ }
3685
+
3686
+ // src/index.ts
3687
+ var command = parseCLICommand(process.argv);
3688
+ if (command !== "serve") {
3689
+ runCLI(command).then(() => process.exit(0)).catch((err) => {
3690
+ console.error(err instanceof Error ? err.message : String(err));
3691
+ process.exit(1);
3692
+ });
3693
+ } else {
3694
+ const server = new Server(
3695
+ {
3696
+ name: "amplis-mcp-server",
3697
+ version: "0.2.0"
3698
+ },
3699
+ {
3700
+ capabilities: {
3701
+ resources: { listChanged: true },
3702
+ tools: {},
3703
+ prompts: {}
3704
+ }
3705
+ }
3706
+ );
3707
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
3708
+ return { resources };
3709
+ });
3710
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
3711
+ return { resourceTemplates: resourceTemplates19 };
3712
+ });
3713
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
3714
+ return handleReadResource(request.params.uri);
3715
+ });
3716
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
3717
+ return { tools: tools18 };
3718
+ });
3719
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3720
+ return handleToolCall18(request.params.name, request.params.arguments);
3721
+ });
3722
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
3723
+ return { prompts };
3724
+ });
3725
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
3726
+ const { name, arguments: args } = request.params;
3727
+ const prompt2 = prompts.find((p) => p.name === name);
3728
+ if (!prompt2) {
3729
+ throw new Error(`Prompt not found: ${name}`);
3730
+ }
3731
+ const content = await generatePromptContent(name, args ?? {});
3732
+ return {
3733
+ description: prompt2.description,
3734
+ messages: [
3735
+ {
3736
+ role: "user",
3737
+ content: { type: "text", text: content }
3738
+ }
3739
+ ]
3740
+ };
3741
+ });
3742
+ async function main() {
3743
+ try {
3744
+ const ctx = await initializeContext();
3745
+ console.error(`AMPLIS MCP Server: authenticated for org ${ctx.orgId}`);
3746
+ } catch (err) {
3747
+ console.error("Startup authentication failed:", err instanceof Error ? err.message : String(err));
3748
+ process.exit(1);
3749
+ }
3750
+ const transport = new StdioServerTransport();
3751
+ await server.connect(transport);
3752
+ console.error("AMPLIS MCP Server running on stdio");
3753
+ }
3754
+ main().catch((error2) => {
3755
+ console.error("Fatal error:", error2);
3756
+ process.exit(1);
3757
+ });
3758
+ }