@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.d.ts +1 -0
- package/dist/index.js +3758 -0
- package/package.json +34 -0
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
|
+
}
|