@mukulaggarwal/pacman 0.1.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.
Files changed (53) hide show
  1. package/README.md +39 -0
  2. package/dist/chunk-3QNXXON5.js +330 -0
  3. package/dist/chunk-3QNXXON5.js.map +1 -0
  4. package/dist/chunk-43PUZDIZ.js +148 -0
  5. package/dist/chunk-43PUZDIZ.js.map +1 -0
  6. package/dist/chunk-7D4SUZUM.js +38 -0
  7. package/dist/chunk-7D4SUZUM.js.map +1 -0
  8. package/dist/chunk-AYFIQNZ5.js +807 -0
  9. package/dist/chunk-AYFIQNZ5.js.map +1 -0
  10. package/dist/chunk-FH6ZHWGR.js +37 -0
  11. package/dist/chunk-FH6ZHWGR.js.map +1 -0
  12. package/dist/chunk-O6T35A4O.js +137 -0
  13. package/dist/chunk-O6T35A4O.js.map +1 -0
  14. package/dist/chunk-TRQIZP6Z.js +451 -0
  15. package/dist/chunk-TRQIZP6Z.js.map +1 -0
  16. package/dist/chunk-UWT6AFJB.js +471 -0
  17. package/dist/chunk-UWT6AFJB.js.map +1 -0
  18. package/dist/chunk-ZKKMIDRK.js +3923 -0
  19. package/dist/chunk-ZKKMIDRK.js.map +1 -0
  20. package/dist/daemon.d.ts +3 -0
  21. package/dist/daemon.js +141 -0
  22. package/dist/daemon.js.map +1 -0
  23. package/dist/dist-3PIJOFZ4.js +91 -0
  24. package/dist/dist-3PIJOFZ4.js.map +1 -0
  25. package/dist/dist-L76NGFFH.js +102 -0
  26. package/dist/dist-L76NGFFH.js.map +1 -0
  27. package/dist/dist-NV2YVVHI.js +178 -0
  28. package/dist/dist-NV2YVVHI.js.map +1 -0
  29. package/dist/dist-RMYCRZIU.js +41 -0
  30. package/dist/dist-RMYCRZIU.js.map +1 -0
  31. package/dist/dist-THLCZNOZ.js +14 -0
  32. package/dist/dist-THLCZNOZ.js.map +1 -0
  33. package/dist/dist-TWNHTXYH.js +95 -0
  34. package/dist/dist-TWNHTXYH.js.map +1 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +452 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/mcp-compat.d.ts +1 -0
  39. package/dist/mcp-compat.js +78 -0
  40. package/dist/mcp-compat.js.map +1 -0
  41. package/dist/onboarding-server.d.ts +3 -0
  42. package/dist/onboarding-server.js +1172 -0
  43. package/dist/onboarding-server.js.map +1 -0
  44. package/dist/provider-runtime.d.ts +11 -0
  45. package/dist/provider-runtime.js +10 -0
  46. package/dist/provider-runtime.js.map +1 -0
  47. package/dist/slack-listener.d.ts +49 -0
  48. package/dist/slack-listener.js +888 -0
  49. package/dist/slack-listener.js.map +1 -0
  50. package/dist/storage.d.ts +8 -0
  51. package/dist/storage.js +9 -0
  52. package/dist/storage.js.map +1 -0
  53. package/package.json +75 -0
@@ -0,0 +1,807 @@
1
+ import {
2
+ createIndexer
3
+ } from "./chunk-3QNXXON5.js";
4
+ import {
5
+ createNoopEventClient,
6
+ validateIntegrationConfig
7
+ } from "./chunk-43PUZDIZ.js";
8
+ import {
9
+ createContextManager
10
+ } from "./chunk-UWT6AFJB.js";
11
+ import {
12
+ createConfigManager,
13
+ createGDriveStorage,
14
+ createLocalStorage
15
+ } from "./chunk-TRQIZP6Z.js";
16
+
17
+ // src/mcp-installers.ts
18
+ import * as fs from "fs/promises";
19
+ import * as os from "os";
20
+ import * as path from "path";
21
+ import { execFile } from "child_process";
22
+ import { promisify } from "util";
23
+ var execFileAsync = promisify(execFile);
24
+ var MCP_SERVER_NAME = "personal_assistant";
25
+ var DEFAULT_WORKSPACE = path.join(os.homedir(), ".personal-assistant");
26
+ async function runCommand(command, args) {
27
+ const result = await execFileAsync(command, args);
28
+ return {
29
+ stdout: result.stdout ?? "",
30
+ stderr: result.stderr ?? ""
31
+ };
32
+ }
33
+ async function resolveWorkspacePath(dirOpt) {
34
+ if (dirOpt) {
35
+ return path.resolve(dirOpt);
36
+ }
37
+ const rcPath = path.join(os.homedir(), ".personal-assistant-rc.json");
38
+ try {
39
+ const rc = JSON.parse(await fs.readFile(rcPath, "utf-8"));
40
+ if (rc.workspacePath) {
41
+ return rc.workspacePath;
42
+ }
43
+ } catch {
44
+ }
45
+ return DEFAULT_WORKSPACE;
46
+ }
47
+ async function resolvePacmanServerCommand() {
48
+ const scriptPath = path.resolve(process.argv[1]);
49
+ return { command: process.execPath, args: [scriptPath, "mcp", "serve"] };
50
+ }
51
+ async function resolveCompatServerCommand() {
52
+ const scriptPath = path.resolve(process.argv[1]);
53
+ return { command: process.execPath, args: [scriptPath] };
54
+ }
55
+ async function installClaudeMcp(workspacePath, resolveCommand = resolvePacmanServerCommand, runner = runCommand) {
56
+ const serverCommand = await resolveCommand();
57
+ const fallbackCommand = formatClaudeManualInstallCommand(workspacePath, serverCommand);
58
+ try {
59
+ try {
60
+ await runner("claude", ["mcp", "remove", "-s", "user", MCP_SERVER_NAME]);
61
+ } catch (err) {
62
+ const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
63
+ if (code === "ENOENT") {
64
+ throw err;
65
+ }
66
+ }
67
+ await runner("claude", [
68
+ "mcp",
69
+ "add",
70
+ "-s",
71
+ "user",
72
+ `--env=PA_WORKSPACE=${workspacePath}`,
73
+ MCP_SERVER_NAME,
74
+ "--",
75
+ serverCommand.command,
76
+ ...serverCommand.args
77
+ ]);
78
+ } catch (err) {
79
+ throw normalizeClaudeInstallError(err, fallbackCommand);
80
+ }
81
+ }
82
+ function formatClaudeManualInstallCommand(workspacePath, serverCommand) {
83
+ return [
84
+ "claude",
85
+ "mcp",
86
+ "add",
87
+ "-s",
88
+ "user",
89
+ `--env=${shellQuote(`PA_WORKSPACE=${workspacePath}`)}`,
90
+ MCP_SERVER_NAME,
91
+ "--",
92
+ shellQuote(serverCommand.command),
93
+ ...serverCommand.args.map(shellQuote)
94
+ ].join(" ");
95
+ }
96
+ function formatCodexManualInstallCommand(workspacePath, serverCommand) {
97
+ return [
98
+ "codex",
99
+ "mcp",
100
+ "add",
101
+ MCP_SERVER_NAME,
102
+ "--env",
103
+ shellQuote(`PA_WORKSPACE=${workspacePath}`),
104
+ "--",
105
+ shellQuote(serverCommand.command),
106
+ ...serverCommand.args.map(shellQuote)
107
+ ].join(" ");
108
+ }
109
+ function parseCodexServerList(stdout) {
110
+ const trimmed = stdout.trim();
111
+ if (trimmed.length === 0) {
112
+ return [];
113
+ }
114
+ const jsonStart = trimmed.indexOf("[");
115
+ const json = jsonStart >= 0 ? trimmed.slice(jsonStart) : trimmed;
116
+ return JSON.parse(json);
117
+ }
118
+ async function installCodexMcp(workspacePath, resolveCommand = resolvePacmanServerCommand, runner = runCommand) {
119
+ const serverCommand = await resolveCommand();
120
+ const fallbackCommand = formatCodexManualInstallCommand(workspacePath, serverCommand);
121
+ try {
122
+ const { stdout } = await runner("codex", ["mcp", "list", "--json"]);
123
+ const servers = parseCodexServerList(stdout);
124
+ if (servers.some((server) => server.name === MCP_SERVER_NAME)) {
125
+ await runner("codex", ["mcp", "remove", MCP_SERVER_NAME]);
126
+ }
127
+ await runner("codex", [
128
+ "mcp",
129
+ "add",
130
+ MCP_SERVER_NAME,
131
+ "--env",
132
+ `PA_WORKSPACE=${workspacePath}`,
133
+ "--",
134
+ serverCommand.command,
135
+ ...serverCommand.args
136
+ ]);
137
+ } catch (err) {
138
+ throw normalizeCodexInstallError(err, fallbackCommand);
139
+ }
140
+ }
141
+ function shellQuote(value) {
142
+ if (/^[A-Za-z0-9_./:=+-]+$/.test(value)) {
143
+ return value;
144
+ }
145
+ return `'${value.replace(/'/g, `'\\''`)}'`;
146
+ }
147
+ function normalizeCodexInstallError(err, fallbackCommand) {
148
+ const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
149
+ const stderr = typeof err === "object" && err !== null && "stderr" in err ? String(err.stderr ?? "") : "";
150
+ const message = err instanceof Error ? err.message : String(err);
151
+ if (code === "ENOENT") {
152
+ return new Error(
153
+ `Codex CLI was not found on PATH. Install Codex, then rerun \`pacman mcp codex install\`. Manual registration once Codex is available: \`${fallbackCommand}\``
154
+ );
155
+ }
156
+ const details = stderr.trim() || message;
157
+ return new Error(
158
+ `Unable to register the Personal Assistant MCP server with Codex. ${details} Manual fallback: \`${fallbackCommand}\``
159
+ );
160
+ }
161
+ function normalizeClaudeInstallError(err, fallbackCommand) {
162
+ const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
163
+ const stderr = typeof err === "object" && err !== null && "stderr" in err ? String(err.stderr ?? "") : "";
164
+ const message = err instanceof Error ? err.message : String(err);
165
+ if (code === "ENOENT") {
166
+ return new Error(
167
+ `Claude Code CLI was not found on PATH. Install Claude Code, then rerun \`pacman mcp claude install\`. Manual registration once Claude Code is available: \`${fallbackCommand}\``
168
+ );
169
+ }
170
+ const details = stderr.trim() || message;
171
+ return new Error(
172
+ `Unable to register the Personal Assistant MCP server with Claude Code. ${details} Manual fallback: \`${fallbackCommand}\``
173
+ );
174
+ }
175
+
176
+ // src/mcp-server.ts
177
+ import * as fs2 from "fs/promises";
178
+ import * as path2 from "path";
179
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
180
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
181
+ import { z } from "zod";
182
+ var DEFAULT_RESTART_STEPS = [
183
+ "Restart your MCP client after the configuration has been updated.",
184
+ "If the registered workspace path changed, rerun `pacman mcp claude install` for Claude Code or `pacman mcp codex install` for Codex before restarting."
185
+ ];
186
+ async function startMcpServer(workspacePath) {
187
+ const localStore = createLocalStorage(workspacePath);
188
+ let storage = localStore;
189
+ let configManager = createConfigManager(localStore);
190
+ let contextManager = createContextManager(localStore);
191
+ let indexer = createIndexer(localStore);
192
+ const eventClient = createNoopEventClient();
193
+ let bootstrapConfig = null;
194
+ let storageReadyResolve;
195
+ const storageReady = new Promise((resolve3) => {
196
+ storageReadyResolve = resolve3;
197
+ });
198
+ const runtimeHealth = {
199
+ storage: {
200
+ status: "healthy",
201
+ mode: "local",
202
+ summary: `Using local workspace at ${workspacePath}`,
203
+ fix: [],
204
+ restart: DEFAULT_RESTART_STEPS
205
+ },
206
+ integrations: [],
207
+ alerts: []
208
+ };
209
+ const server = new McpServer({
210
+ name: "personal-assistant",
211
+ version: "0.1.0"
212
+ });
213
+ function textResult(text, isError = false) {
214
+ return {
215
+ content: [{ type: "text", text }],
216
+ ...isError ? { isError: true } : {}
217
+ };
218
+ }
219
+ function getStatusConfig() {
220
+ return bootstrapConfig ?? {
221
+ user: { name: "", assistantName: "Jarvis", profileType: "software-engineer", responsibilities: [] },
222
+ storage: { mode: "local", workspacePath },
223
+ integrations: [],
224
+ sync: {
225
+ dailySyncTime: "09:00",
226
+ timezone: "UTC",
227
+ manualSyncEnabled: false,
228
+ asyncUpdateEnabled: false
229
+ }
230
+ };
231
+ }
232
+ function addAlert(alert) {
233
+ runtimeHealth.alerts.push(alert);
234
+ }
235
+ function setStorageFailure(mode, reason, fix) {
236
+ runtimeHealth.storage = {
237
+ status: "error",
238
+ mode,
239
+ summary: `Unable to use ${mode} storage`,
240
+ reason,
241
+ fix,
242
+ restart: DEFAULT_RESTART_STEPS
243
+ };
244
+ addAlert({
245
+ component: "storage",
246
+ name: mode,
247
+ reason,
248
+ fix,
249
+ restart: DEFAULT_RESTART_STEPS
250
+ });
251
+ }
252
+ function formatSteps(label, steps) {
253
+ if (steps.length === 0) return "";
254
+ return `${label}:
255
+ ${steps.map((step, index) => `${index + 1}. ${step}`).join("\n")}`;
256
+ }
257
+ function storageUnavailableMessage() {
258
+ const sections = [
259
+ `Personal Assistant MCP storage is unavailable: ${runtimeHealth.storage.reason ?? runtimeHealth.storage.summary}`,
260
+ formatSteps("Fix", runtimeHealth.storage.fix),
261
+ formatSteps("Restart", runtimeHealth.storage.restart)
262
+ ].filter(Boolean);
263
+ return sections.join("\n\n");
264
+ }
265
+ function logStartupHealth() {
266
+ if (runtimeHealth.alerts.length === 0) {
267
+ console.error(`Personal Assistant MCP ready. Storage: ${runtimeHealth.storage.mode}`);
268
+ return;
269
+ }
270
+ console.error("Personal Assistant MCP startup alerts:");
271
+ for (const alert of runtimeHealth.alerts) {
272
+ console.error(`- ${alert.component} ${alert.name}: ${alert.reason}`);
273
+ for (const step of alert.fix) {
274
+ console.error(` Fix: ${step}`);
275
+ }
276
+ for (const step of alert.restart) {
277
+ console.error(` Restart: ${step}`);
278
+ }
279
+ }
280
+ }
281
+ function getStorageFixSteps(mode, config) {
282
+ if (mode === "gdrive") {
283
+ return [
284
+ "Verify the Google Drive folder ID and OAuth credentials in profile/storage.json.",
285
+ "If the refresh token is no longer valid, reconnect Google Drive and save the updated configuration.",
286
+ ...config?.cachePath ? [`Ensure the local cache path exists and is writable: ${config.cachePath}`] : []
287
+ ];
288
+ }
289
+ return [
290
+ `Verify that PA_WORKSPACE points to the correct workspace directory: ${workspacePath}`,
291
+ "Ensure the workspace directory exists and is writable.",
292
+ "Run `pacman init` if the workspace has not been initialized yet."
293
+ ];
294
+ }
295
+ function getIntegrationHealth(type) {
296
+ return runtimeHealth.integrations.find((integration) => integration.type === type);
297
+ }
298
+ async function ensureStorageAvailable() {
299
+ await storageReady;
300
+ if (runtimeHealth.storage.status === "error") {
301
+ return textResult(storageUnavailableMessage(), true);
302
+ }
303
+ return null;
304
+ }
305
+ async function resolveStorageBackend() {
306
+ bootstrapConfig = await configManager.loadConfig();
307
+ runtimeHealth.storage.mode = bootstrapConfig.storage.mode;
308
+ if (bootstrapConfig.storage.mode === "gdrive") {
309
+ const gdriveConfig = bootstrapConfig.storage;
310
+ const resolvedCachePath = path2.isAbsolute(gdriveConfig.cachePath) ? gdriveConfig.cachePath : path2.resolve(path2.dirname(workspacePath), gdriveConfig.cachePath);
311
+ try {
312
+ const gdriveStorage = createGDriveStorage({
313
+ ...gdriveConfig,
314
+ cachePath: resolvedCachePath
315
+ });
316
+ await gdriveStorage.initialize();
317
+ storage = gdriveStorage;
318
+ configManager = createConfigManager(gdriveStorage);
319
+ contextManager = createContextManager(gdriveStorage);
320
+ indexer = createIndexer(gdriveStorage);
321
+ bootstrapConfig = await configManager.loadConfig();
322
+ runtimeHealth.storage = {
323
+ status: "healthy",
324
+ mode: "gdrive",
325
+ summary: `Connected to Google Drive storage${gdriveConfig.folderName ? ` (${gdriveConfig.folderName})` : ""}`,
326
+ fix: [],
327
+ restart: DEFAULT_RESTART_STEPS
328
+ };
329
+ } catch (err) {
330
+ setStorageFailure(
331
+ "gdrive",
332
+ err instanceof Error ? err.message : String(err),
333
+ getStorageFixSteps("gdrive", {
334
+ ...gdriveConfig,
335
+ cachePath: resolvedCachePath
336
+ })
337
+ );
338
+ }
339
+ } else {
340
+ try {
341
+ await fs2.mkdir(workspacePath, { recursive: true });
342
+ runtimeHealth.storage = {
343
+ status: "healthy",
344
+ mode: "local",
345
+ summary: `Using local workspace at ${workspacePath}`,
346
+ fix: [],
347
+ restart: DEFAULT_RESTART_STEPS
348
+ };
349
+ } catch (err) {
350
+ setStorageFailure(
351
+ "local",
352
+ err instanceof Error ? err.message : String(err),
353
+ getStorageFixSteps("local")
354
+ );
355
+ }
356
+ }
357
+ const configForIntegrations = getStatusConfig();
358
+ runtimeHealth.integrations = [];
359
+ for (const integration of configForIntegrations.integrations.filter((item) => item.enabled)) {
360
+ const result = await validateIntegrationConfig(integration);
361
+ if (result.ok) {
362
+ runtimeHealth.integrations.push({
363
+ type: integration.type,
364
+ status: "healthy",
365
+ summary: result.summary ?? `Connected to ${integration.type}`,
366
+ fix: [],
367
+ restart: DEFAULT_RESTART_STEPS
368
+ });
369
+ continue;
370
+ }
371
+ runtimeHealth.integrations.push({
372
+ type: integration.type,
373
+ status: "error",
374
+ summary: `Unable to connect to ${integration.type}`,
375
+ reason: result.reason ?? `Unable to connect to ${integration.type}`,
376
+ fix: result.fix ?? [],
377
+ restart: DEFAULT_RESTART_STEPS
378
+ });
379
+ addAlert({
380
+ component: "integration",
381
+ name: integration.type,
382
+ reason: result.reason ?? `Unable to connect to ${integration.type}`,
383
+ fix: result.fix ?? [],
384
+ restart: DEFAULT_RESTART_STEPS
385
+ });
386
+ }
387
+ logStartupHealth();
388
+ }
389
+ server.tool("config_get", "Get the full application configuration", {}, async () => {
390
+ const unavailable = await ensureStorageAvailable();
391
+ if (unavailable) return unavailable;
392
+ try {
393
+ const config = await configManager.loadConfig();
394
+ return textResult(JSON.stringify(config, null, 2));
395
+ } catch (err) {
396
+ return textResult(`Error loading config: ${err}`, true);
397
+ }
398
+ });
399
+ server.tool("config_get_storage_mode", "Get the current storage mode (local or gdrive)", {}, async () => {
400
+ const unavailable = await ensureStorageAvailable();
401
+ if (unavailable) return unavailable;
402
+ try {
403
+ const mode = await configManager.getStorageMode();
404
+ return textResult(mode);
405
+ } catch (err) {
406
+ return textResult(`Error: ${err}`, true);
407
+ }
408
+ });
409
+ server.tool("config_get_active_project", "Get the currently active project name", {}, async () => {
410
+ const unavailable = await ensureStorageAvailable();
411
+ if (unavailable) return unavailable;
412
+ try {
413
+ const project = await configManager.getActiveProject();
414
+ return textResult(project ?? "No active project set");
415
+ } catch (err) {
416
+ return textResult(`Error: ${err}`, true);
417
+ }
418
+ });
419
+ server.tool(
420
+ "config_set_active_project",
421
+ "Set the active project",
422
+ { project_name: z.string().describe("The project name to set as active") },
423
+ async ({ project_name }) => {
424
+ const unavailable = await ensureStorageAvailable();
425
+ if (unavailable) return unavailable;
426
+ try {
427
+ await configManager.setActiveProject(project_name);
428
+ await eventClient.emitWithProject("project_selected", project_name);
429
+ return textResult(`Active project set to: ${project_name}`);
430
+ } catch (err) {
431
+ return textResult(`Error: ${err}`, true);
432
+ }
433
+ }
434
+ );
435
+ server.tool("project_list", "List all known projects", {}, async () => {
436
+ const unavailable = await ensureStorageAvailable();
437
+ if (unavailable) return unavailable;
438
+ try {
439
+ const projects = await contextManager.listProjects();
440
+ return textResult(projects.length > 0 ? projects.join("\n") : "No projects found");
441
+ } catch (err) {
442
+ return textResult(`Error: ${err}`, true);
443
+ }
444
+ });
445
+ server.tool(
446
+ "project_suggest",
447
+ "Suggest a project based on context",
448
+ {
449
+ hint: z.string().optional().describe("Optional hint to narrow project suggestion")
450
+ },
451
+ async ({ hint }) => {
452
+ const unavailable = await ensureStorageAvailable();
453
+ if (unavailable) return unavailable;
454
+ try {
455
+ const suggestion = await contextManager.suggestProject(hint);
456
+ if (!suggestion) {
457
+ return textResult("No projects found to suggest");
458
+ }
459
+ return textResult(JSON.stringify(suggestion, null, 2));
460
+ } catch (err) {
461
+ return textResult(`Error: ${err}`, true);
462
+ }
463
+ }
464
+ );
465
+ server.tool(
466
+ "project_get_context",
467
+ "Get the full context for a project",
468
+ { project_name: z.string().describe("The project name") },
469
+ async ({ project_name }) => {
470
+ const unavailable = await ensureStorageAvailable();
471
+ if (unavailable) return unavailable;
472
+ try {
473
+ const context = await contextManager.getProjectContext(project_name);
474
+ await eventClient.emit("mcp_query_executed", { tool: "project_get_context" });
475
+ const parts = [];
476
+ parts.push(`# Project: ${context.name}
477
+ `);
478
+ for (const file of context.canonicalFiles) {
479
+ parts.push(`## ${file.path}
480
+ ${file.content}
481
+ `);
482
+ }
483
+ for (const file of context.derivedFiles) {
484
+ if (file.path.endsWith(".json")) {
485
+ parts.push(`## ${file.path}
486
+ \`\`\`json
487
+ ${file.content.slice(0, 2e3)}
488
+ \`\`\`
489
+ `);
490
+ } else {
491
+ parts.push(`## ${file.path}
492
+ ${file.content}
493
+ `);
494
+ }
495
+ }
496
+ return textResult(parts.join("\n"));
497
+ } catch (err) {
498
+ return textResult(`Error: ${err}`, true);
499
+ }
500
+ }
501
+ );
502
+ server.tool(
503
+ "context_search",
504
+ "Search across all context files",
505
+ { query: z.string().describe("Search query") },
506
+ async ({ query }) => {
507
+ const unavailable = await ensureStorageAvailable();
508
+ if (unavailable) return unavailable;
509
+ try {
510
+ const results = await indexer.search(query);
511
+ await eventClient.emit("mcp_query_executed", { tool: "context_search" });
512
+ if (results.length === 0) {
513
+ return textResult("No results found");
514
+ }
515
+ const text = results.map(
516
+ (result) => `[${result.matchType}] ${result.filePath}${result.section ? ` > ${result.section}` : ""} (score: ${result.score})
517
+ ${result.content}
518
+ `
519
+ ).join("\n---\n");
520
+ return textResult(text);
521
+ } catch (err) {
522
+ return textResult(`Error: ${err}`, true);
523
+ }
524
+ }
525
+ );
526
+ server.tool(
527
+ "context_read_file",
528
+ "Read a specific context file",
529
+ { file_path: z.string().describe("Path to the context file") },
530
+ async ({ file_path }) => {
531
+ const unavailable = await ensureStorageAvailable();
532
+ if (unavailable) return unavailable;
533
+ try {
534
+ const content = await contextManager.readFile(file_path);
535
+ return textResult(content);
536
+ } catch (err) {
537
+ return textResult(`Error reading file: ${err}`, true);
538
+ }
539
+ }
540
+ );
541
+ server.tool(
542
+ "context_read_section",
543
+ "Read a specific section from a context file",
544
+ {
545
+ file_path: z.string().describe("Path to the context file"),
546
+ section_name: z.string().describe("Name of the section to read")
547
+ },
548
+ async ({ file_path, section_name }) => {
549
+ const unavailable = await ensureStorageAvailable();
550
+ if (unavailable) return unavailable;
551
+ try {
552
+ const section = await contextManager.readSection(file_path, section_name);
553
+ if (!section) {
554
+ return textResult(`Section "${section_name}" not found in ${file_path}`);
555
+ }
556
+ return textResult(section);
557
+ } catch (err) {
558
+ return textResult(`Error: ${err}`, true);
559
+ }
560
+ }
561
+ );
562
+ server.tool(
563
+ "context_list_recent_updates",
564
+ "List recent context update proposals",
565
+ { limit: z.number().optional().describe("Maximum number of updates to return") },
566
+ async ({ limit }) => {
567
+ const unavailable = await ensureStorageAvailable();
568
+ if (unavailable) return unavailable;
569
+ try {
570
+ const updates = await contextManager.listRecentUpdates(limit ?? 10);
571
+ if (updates.length === 0) {
572
+ return textResult("No recent updates");
573
+ }
574
+ return textResult(JSON.stringify(updates, null, 2));
575
+ } catch (err) {
576
+ return textResult(`Error: ${err}`, true);
577
+ }
578
+ }
579
+ );
580
+ server.tool(
581
+ "context_get_daily_summary",
582
+ "Get the daily summary for a specific date",
583
+ { date: z.string().optional().describe("Date in YYYY-MM-DD format (defaults to today)") },
584
+ async ({ date }) => {
585
+ const unavailable = await ensureStorageAvailable();
586
+ if (unavailable) return unavailable;
587
+ try {
588
+ const summary = await contextManager.getDailySummary(date);
589
+ if (!summary) {
590
+ return textResult(`No daily summary found for ${date ?? "today"}`);
591
+ }
592
+ return textResult(summary);
593
+ } catch (err) {
594
+ return textResult(`Error: ${err}`, true);
595
+ }
596
+ }
597
+ );
598
+ server.tool(
599
+ "context_propose_update",
600
+ "Propose an update to a context file",
601
+ {
602
+ target_file: z.string().describe("Path to the target file"),
603
+ section: z.string().optional().describe("Section name to update (omit for full file)"),
604
+ proposed_content: z.string().describe("The proposed new content"),
605
+ reason: z.string().describe("Reason for the update")
606
+ },
607
+ async ({ target_file, section, proposed_content, reason }) => {
608
+ const unavailable = await ensureStorageAvailable();
609
+ if (unavailable) return unavailable;
610
+ try {
611
+ const proposal = await contextManager.proposeUpdate({
612
+ targetFile: target_file,
613
+ section,
614
+ proposedContent: proposed_content,
615
+ reason
616
+ });
617
+ await eventClient.emit("context_update_proposed");
618
+ return textResult(
619
+ `Update proposal created: ${proposal.id}
620
+ Target: ${proposal.targetFile}
621
+ Reason: ${proposal.reason}
622
+ Status: ${proposal.status}`
623
+ );
624
+ } catch (err) {
625
+ return textResult(`Error: ${err}`, true);
626
+ }
627
+ }
628
+ );
629
+ server.tool(
630
+ "context_apply_update",
631
+ "Apply a pending update proposal",
632
+ { proposal_id: z.string().describe("ID of the proposal to apply") },
633
+ async ({ proposal_id }) => {
634
+ const unavailable = await ensureStorageAvailable();
635
+ if (unavailable) return unavailable;
636
+ try {
637
+ const applied = await contextManager.applyUpdate(proposal_id);
638
+ await eventClient.emit("context_update_applied");
639
+ return textResult(
640
+ `Update applied: ${applied.id}
641
+ Target: ${applied.targetFile}
642
+ Applied at: ${applied.appliedAt}`
643
+ );
644
+ } catch (err) {
645
+ return textResult(`Error: ${err}`, true);
646
+ }
647
+ }
648
+ );
649
+ server.tool(
650
+ "context_append_note",
651
+ "Append a note to a project context file",
652
+ {
653
+ project_name: z.string().describe("The project name"),
654
+ note: z.string().describe("The note to append")
655
+ },
656
+ async ({ project_name, note }) => {
657
+ const unavailable = await ensureStorageAvailable();
658
+ if (unavailable) return unavailable;
659
+ try {
660
+ await contextManager.appendNote(project_name, note);
661
+ return textResult(`Note appended to project: ${project_name}`);
662
+ } catch (err) {
663
+ return textResult(`Error: ${err}`, true);
664
+ }
665
+ }
666
+ );
667
+ server.tool("context_rebuild_indexes", "Rebuild all context indexes", {}, async () => {
668
+ const unavailable = await ensureStorageAvailable();
669
+ if (unavailable) return unavailable;
670
+ try {
671
+ await indexer.buildIndexes();
672
+ return textResult("Indexes rebuilt successfully");
673
+ } catch (err) {
674
+ return textResult(`Error: ${err}`, true);
675
+ }
676
+ });
677
+ server.tool(
678
+ "session_end",
679
+ "End the current session: force-apply all pending proposals, save session notes, rebuild indexes, and clear the active project",
680
+ {
681
+ project_name: z.string().optional().describe("The active project name"),
682
+ session_notes: z.string().optional().describe("Summary of what was learned or decided during this session")
683
+ },
684
+ async ({ project_name, session_notes }) => {
685
+ const unavailable = await ensureStorageAvailable();
686
+ if (unavailable) return unavailable;
687
+ try {
688
+ const results = [];
689
+ const appliedIds = await contextManager.applyAllPending();
690
+ if (appliedIds.length > 0) {
691
+ results.push(`Applied ${appliedIds.length} pending proposal(s): ${appliedIds.join(", ")}`);
692
+ } else {
693
+ results.push("No pending proposals to apply.");
694
+ }
695
+ if (session_notes && project_name) {
696
+ await contextManager.appendNote(project_name, session_notes);
697
+ results.push(`Session notes saved to project: ${project_name}`);
698
+ }
699
+ await indexer.buildIndexes();
700
+ results.push("Indexes rebuilt.");
701
+ await configManager.setActiveProject("");
702
+ results.push("Active project cleared.");
703
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
704
+ const logEntry = `[${timestamp}] Session ended for project "${project_name ?? "none"}". Applied ${appliedIds.length} proposals.
705
+ `;
706
+ await storage.append("logs/audit.log", logEntry);
707
+ return textResult(`Session ended successfully.
708
+
709
+ ${results.join("\n")}`);
710
+ } catch (err) {
711
+ return textResult(`Error ending session: ${err}`, true);
712
+ }
713
+ }
714
+ );
715
+ server.tool("sync_run_now", "Trigger an immediate sync", {}, async () => {
716
+ const unavailable = await ensureStorageAvailable();
717
+ if (unavailable) return unavailable;
718
+ try {
719
+ await indexer.buildIndexes();
720
+ return textResult("Sync completed. Indexes rebuilt.");
721
+ } catch (err) {
722
+ return textResult(`Error: ${err}`, true);
723
+ }
724
+ });
725
+ server.tool("sync_status", "Get the current sync status", {}, async () => {
726
+ await storageReady;
727
+ try {
728
+ const config = getStatusConfig();
729
+ const integrations = config.integrations.filter((integration) => integration.enabled).map((integration) => {
730
+ const health = getIntegrationHealth(integration.type);
731
+ return {
732
+ type: integration.type,
733
+ lastSyncAt: integration.lastSyncAt ?? "never",
734
+ cursor: integration.cursor ?? "none",
735
+ connectionStatus: health?.status ?? "healthy",
736
+ connectionSummary: health?.summary ?? `No startup health check recorded for ${integration.type}`,
737
+ connectionReason: health?.reason ?? null,
738
+ fix: health?.fix ?? [],
739
+ restart: health?.restart ?? DEFAULT_RESTART_STEPS
740
+ };
741
+ });
742
+ const status = {
743
+ syncTime: config.sync.dailySyncTime,
744
+ timezone: config.sync.timezone,
745
+ storage: {
746
+ mode: runtimeHealth.storage.mode,
747
+ status: runtimeHealth.storage.status,
748
+ summary: runtimeHealth.storage.summary,
749
+ reason: runtimeHealth.storage.reason ?? null,
750
+ fix: runtimeHealth.storage.fix,
751
+ restart: runtimeHealth.storage.restart
752
+ },
753
+ integrations,
754
+ alerts: runtimeHealth.alerts
755
+ };
756
+ return textResult(JSON.stringify(status, null, 2));
757
+ } catch (err) {
758
+ return textResult(`Error: ${err}`, true);
759
+ }
760
+ });
761
+ server.tool("sync_list_integrations", "List all configured integrations", {}, async () => {
762
+ await storageReady;
763
+ try {
764
+ const config = getStatusConfig();
765
+ const list = config.integrations.map((integration) => {
766
+ const health = getIntegrationHealth(integration.type);
767
+ const connection = health ? health.status === "healthy" ? `healthy - ${health.summary}` : `error - ${health.reason ?? health.summary}` : "not checked";
768
+ return `${integration.type}: ${integration.enabled ? "enabled" : "disabled"} (connection: ${connection}; last sync: ${integration.lastSyncAt ?? "never"})`;
769
+ });
770
+ return textResult(list.join("\n") || "No integrations configured");
771
+ } catch (err) {
772
+ return textResult(`Error: ${err}`, true);
773
+ }
774
+ });
775
+ server.tool(
776
+ "events_ping",
777
+ "Emit a metadata-only event",
778
+ {
779
+ event_name: z.string().describe("Name of the event"),
780
+ metadata: z.record(z.string()).optional().describe("Optional metadata key-value pairs")
781
+ },
782
+ async ({ event_name, metadata }) => {
783
+ await storageReady;
784
+ try {
785
+ await eventClient.emit(event_name, metadata);
786
+ return textResult(`Event emitted: ${event_name}`);
787
+ } catch (err) {
788
+ return textResult(`Error: ${err}`, true);
789
+ }
790
+ }
791
+ );
792
+ const transport = new StdioServerTransport();
793
+ await server.connect(transport);
794
+ await resolveStorageBackend();
795
+ storageReadyResolve?.();
796
+ }
797
+
798
+ export {
799
+ DEFAULT_WORKSPACE,
800
+ resolveWorkspacePath,
801
+ resolvePacmanServerCommand,
802
+ resolveCompatServerCommand,
803
+ installClaudeMcp,
804
+ installCodexMcp,
805
+ startMcpServer
806
+ };
807
+ //# sourceMappingURL=chunk-AYFIQNZ5.js.map