@mgsoftwarebv/mg-dashboard-mcp 7.4.11 → 7.4.13

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 (2) hide show
  1. package/dist/index.js +556 -151
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import { sql } from 'drizzle-orm';
20
20
  import { once } from 'events';
21
21
  import { lookup } from 'dns/promises';
22
22
  import { connect } from 'tls';
23
- import { pgTable, timestamp, uuid, jsonb, boolean, text, pgEnum, integer, uniqueIndex, index, check, bigint, doublePrecision, primaryKey, foreignKey, date, real } from 'drizzle-orm/pg-core';
23
+ import { pgTable, timestamp, uuid, jsonb, boolean, text, customType, integer, date, real, index, uniqueIndex, pgEnum, check, bigint, doublePrecision, primaryKey, foreignKey } from 'drizzle-orm/pg-core';
24
24
  import { HeadObjectCommand, ListObjectsV2Command, DeleteObjectsCommand, S3Client, DeleteObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand, AbortMultipartUploadCommand, PutObjectCommand, GetObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
25
25
 
26
26
  var __defProp = Object.defineProperty;
@@ -990,9 +990,9 @@ ${raw2.substring(0, 500)}` }] };
990
990
  return { content: [{ type: "text", text: `No environment variables for ${project}/${env}.` }] };
991
991
  }
992
992
  const lines = vars.map((v) => `${v.isSecret ? "[secret]" : " "} ${v.name}`).sort();
993
- const text16 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
993
+ const text19 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
994
994
  ` + "-".repeat(50) + "\n" + lines.join("\n");
995
- return { content: [{ type: "text", text: text16 }] };
995
+ return { content: [{ type: "text", text: text19 }] };
996
996
  }
997
997
  if (action === "get") {
998
998
  const key2 = String(args2.key ?? "");
@@ -1079,9 +1079,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
1079
1079
  return { content: [{ type: "text", text: `Invalid API response:
1080
1080
  ${rawJson.substring(0, 500)}` }] };
1081
1081
  }
1082
- let text16 = formatRunDetail(run);
1083
- if (logs) text16 += "\n\n--- Logs ---\n" + logs;
1084
- return { content: [{ type: "text", text: text16 }] };
1082
+ let text19 = formatRunDetail(run);
1083
+ if (logs) text19 += "\n\n--- Logs ---\n" + logs;
1084
+ return { content: [{ type: "text", text: text19 }] };
1085
1085
  }
1086
1086
  async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
1087
1087
  const pollInterval = 3e3;
@@ -1103,10 +1103,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
1103
1103
  continue;
1104
1104
  }
1105
1105
  if (TERMINAL_STATUSES.has(run.status)) {
1106
- let text16 = formatRunDetail(run);
1106
+ let text19 = formatRunDetail(run);
1107
1107
  const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
1108
- if (logs) text16 += "\n\n--- Logs ---\n" + logs;
1109
- return { content: [{ type: "text", text: text16 }] };
1108
+ if (logs) text19 += "\n\n--- Logs ---\n" + logs;
1109
+ return { content: [{ type: "text", text: text19 }] };
1110
1110
  }
1111
1111
  }
1112
1112
  return {
@@ -3793,10 +3793,10 @@ var ZodObject = class _ZodObject extends ZodType {
3793
3793
  // }) as any;
3794
3794
  // return merged;
3795
3795
  // }
3796
- catchall(index13) {
3796
+ catchall(index16) {
3797
3797
  return new _ZodObject({
3798
3798
  ...this._def,
3799
- catchall: index13
3799
+ catchall: index16
3800
3800
  });
3801
3801
  }
3802
3802
  pick(mask) {
@@ -4114,9 +4114,9 @@ function mergeValues(a, b) {
4114
4114
  return { valid: false };
4115
4115
  }
4116
4116
  const newArray = [];
4117
- for (let index13 = 0; index13 < a.length; index13++) {
4118
- const itemA = a[index13];
4119
- const itemB = b[index13];
4117
+ for (let index16 = 0; index16 < a.length; index16++) {
4118
+ const itemA = a[index16];
4119
+ const itemB = b[index16];
4120
4120
  const sharedValue = mergeValues(itemA, itemB);
4121
4121
  if (!sharedValue.valid) {
4122
4122
  return { valid: false };
@@ -4322,10 +4322,10 @@ var ZodMap = class extends ZodType {
4322
4322
  }
4323
4323
  const keyType = this._def.keyType;
4324
4324
  const valueType = this._def.valueType;
4325
- const pairs = [...ctx.data.entries()].map(([key, value], index13) => {
4325
+ const pairs = [...ctx.data.entries()].map(([key, value], index16) => {
4326
4326
  return {
4327
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index13, "key"])),
4328
- value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index13, "value"]))
4327
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index16, "key"])),
4328
+ value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index16, "value"]))
4329
4329
  };
4330
4330
  });
4331
4331
  if (ctx.common.async) {
@@ -5295,6 +5295,232 @@ pgTable("two_factor", {
5295
5295
  secret: text("secret").notNull(),
5296
5296
  backupCodes: text("backup_codes")
5297
5297
  });
5298
+ var WIKI_EMBEDDING_DIMENSIONS = 1536;
5299
+ var vector = customType({
5300
+ dataType(config) {
5301
+ return `vector(${config?.dimensions ?? WIKI_EMBEDDING_DIMENSIONS})`;
5302
+ },
5303
+ toDriver(value) {
5304
+ return `[${value.join(",")}]`;
5305
+ },
5306
+ fromDriver(value) {
5307
+ if (Array.isArray(value)) return value.map(Number);
5308
+ if (typeof value !== "string") return [];
5309
+ const trimmed = value.replace(/^\[/, "").replace(/\]$/, "");
5310
+ if (!trimmed) return [];
5311
+ return trimmed.split(",").map((part) => Number(part.trim()));
5312
+ }
5313
+ });
5314
+ pgTable(
5315
+ "wiki_page",
5316
+ {
5317
+ id: uuid("id").primaryKey().defaultRandom(),
5318
+ slug: text("slug").notNull().unique(),
5319
+ title: text("title").notNull().default(""),
5320
+ summary: text("summary"),
5321
+ content: text("content").notNull().default(""),
5322
+ frontmatter: jsonb("frontmatter").$type(),
5323
+ sources: jsonb("sources").$type(),
5324
+ sourcesArchive: jsonb("sources_archive").$type(),
5325
+ tags: text("tags").array().notNull().default([]),
5326
+ status: text("status").notNull().default("draft"),
5327
+ pageUpdatedOn: date("page_updated_on"),
5328
+ contentHash: text("content_hash"),
5329
+ /** Legacy app-layer cosine storage; dual-written with embeddingVec. */
5330
+ embedding: real("embedding").array(),
5331
+ embeddingVec: vector("embedding_vec", {
5332
+ dimensions: WIKI_EMBEDDING_DIMENSIONS
5333
+ }),
5334
+ embeddingModel: text("embedding_model"),
5335
+ embeddedAt: timestamp("embedded_at", { withTimezone: true }),
5336
+ reviewedBy: text("reviewed_by"),
5337
+ reviewedOn: date("reviewed_on"),
5338
+ lastVerifiedOn: date("last_verified_on"),
5339
+ verifyCount: integer("verify_count").notNull().default(0),
5340
+ verifyNote: text("verify_note"),
5341
+ dirty: boolean("dirty").notNull().default(false),
5342
+ vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
5343
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5344
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5345
+ },
5346
+ (table) => [
5347
+ index("idx_wiki_page_status").on(table.status),
5348
+ index("idx_wiki_page_last_verified").on(table.lastVerifiedOn),
5349
+ index("idx_wiki_page_dirty").on(table.dirty)
5350
+ ]
5351
+ );
5352
+ pgTable(
5353
+ "wiki_change_log",
5354
+ {
5355
+ id: uuid("id").primaryKey().defaultRandom(),
5356
+ authorJob: text("author_job").notNull(),
5357
+ summary: text("summary").notNull().default(""),
5358
+ slugs: text("slugs").array().notNull().default([]),
5359
+ syncedAt: timestamp("synced_at", { withTimezone: true }),
5360
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5361
+ },
5362
+ (table) => [index("idx_wiki_change_log_created").on(table.createdAt)]
5363
+ );
5364
+ pgTable(
5365
+ "wiki_page_flag",
5366
+ {
5367
+ id: uuid("id").primaryKey().defaultRandom(),
5368
+ pageSlug: text("page_slug").notNull(),
5369
+ kind: text("kind").notNull(),
5370
+ detail: text("detail").notNull().default(""),
5371
+ sourceRef: text("source_ref"),
5372
+ createdBy: text("created_by"),
5373
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5374
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
5375
+ resolvedBy: text("resolved_by")
5376
+ },
5377
+ (table) => [index("idx_wiki_page_flag_created").on(table.createdAt)]
5378
+ );
5379
+ pgTable(
5380
+ "wiki_page_evidence",
5381
+ {
5382
+ id: uuid("id").primaryKey().defaultRandom(),
5383
+ pageSlug: text("page_slug").notNull(),
5384
+ evidenceType: text("evidence_type").notNull(),
5385
+ evidenceRef: text("evidence_ref").notNull(),
5386
+ matchScore: real("match_score"),
5387
+ matchReason: text("match_reason"),
5388
+ snippet: text("snippet"),
5389
+ status: text("status").notNull().default("pending"),
5390
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5391
+ resolvedAt: timestamp("resolved_at", { withTimezone: true })
5392
+ },
5393
+ (table) => [
5394
+ uniqueIndex("wiki_page_evidence_page_slug_evidence_type_evidence_ref_key").on(
5395
+ table.pageSlug,
5396
+ table.evidenceType,
5397
+ table.evidenceRef
5398
+ ),
5399
+ index("idx_wiki_page_evidence_created").on(table.createdAt)
5400
+ ]
5401
+ );
5402
+ pgTable(
5403
+ "wiki_page_link",
5404
+ {
5405
+ id: uuid("id").primaryKey().defaultRandom(),
5406
+ sourceSlug: text("source_slug").notNull(),
5407
+ targetSlug: text("target_slug").notNull(),
5408
+ kind: text("kind").notNull().default("wikilink"),
5409
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5410
+ },
5411
+ (table) => [
5412
+ uniqueIndex("wiki_page_link_source_target_kind_key").on(
5413
+ table.sourceSlug,
5414
+ table.targetSlug,
5415
+ table.kind
5416
+ ),
5417
+ index("idx_wiki_page_link_source").on(table.sourceSlug),
5418
+ index("idx_wiki_page_link_target").on(table.targetSlug)
5419
+ ]
5420
+ );
5421
+ pgTable(
5422
+ "wiki_page_read",
5423
+ {
5424
+ id: uuid("id").primaryKey().defaultRandom(),
5425
+ slug: text("slug").notNull(),
5426
+ caller: text("caller"),
5427
+ includedArchive: boolean("included_archive").notNull().default(false),
5428
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5429
+ },
5430
+ (table) => [
5431
+ index("idx_wiki_page_read_slug").on(table.slug, table.createdAt),
5432
+ index("idx_wiki_page_read_created").on(table.createdAt)
5433
+ ]
5434
+ );
5435
+ pgTable(
5436
+ "wiki_verify_event",
5437
+ {
5438
+ id: uuid("id").primaryKey().defaultRandom(),
5439
+ pageSlug: text("page_slug").notNull(),
5440
+ outcome: text("outcome").notNull(),
5441
+ runMode: text("run_mode"),
5442
+ note: text("note"),
5443
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5444
+ },
5445
+ (table) => [
5446
+ index("idx_wiki_verify_event_created").on(table.createdAt),
5447
+ index("idx_wiki_verify_event_outcome_created").on(
5448
+ table.outcome,
5449
+ table.createdAt
5450
+ )
5451
+ ]
5452
+ );
5453
+ pgTable(
5454
+ "wiki_search_log",
5455
+ {
5456
+ id: uuid("id").primaryKey().defaultRandom(),
5457
+ query: text("query").notNull(),
5458
+ hitCount: integer("hit_count").notNull().default(0),
5459
+ topScore: real("top_score"),
5460
+ topSlug: text("top_slug"),
5461
+ caller: text("caller"),
5462
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5463
+ },
5464
+ (table) => [index("idx_wiki_search_log_created").on(table.createdAt)]
5465
+ );
5466
+ pgTable(
5467
+ "team_memory_search_log",
5468
+ {
5469
+ id: uuid("id").primaryKey().defaultRandom(),
5470
+ query: text("query").notNull(),
5471
+ hitCount: integer("hit_count").notNull().default(0),
5472
+ topScore: real("top_score"),
5473
+ topId: text("top_id"),
5474
+ caller: text("caller"),
5475
+ repo: text("repo"),
5476
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5477
+ },
5478
+ (table) => [index("idx_team_memory_search_log_created").on(table.createdAt)]
5479
+ );
5480
+
5481
+ // ../db/src/schema/agent-memory.ts
5482
+ pgTable(
5483
+ "agent_memory",
5484
+ {
5485
+ id: uuid("id").primaryKey().defaultRandom(),
5486
+ conversationId: uuid("conversation_id"),
5487
+ localConversationId: text("local_conversation_id"),
5488
+ source: text("source").notNull().default("mirror"),
5489
+ userId: text("user_id"),
5490
+ hostId: uuid("host_id"),
5491
+ workspaceId: uuid("workspace_id"),
5492
+ gitRepository: text("git_repository"),
5493
+ title: text("title").notNull().default(""),
5494
+ summary: text("summary"),
5495
+ content: text("content").notNull().default(""),
5496
+ contentHash: text("content_hash"),
5497
+ embedding: real("embedding").array(),
5498
+ embeddingVec: vector("embedding_vec", {
5499
+ dimensions: WIKI_EMBEDDING_DIMENSIONS
5500
+ }),
5501
+ embeddingModel: text("embedding_model"),
5502
+ messageCount: integer("message_count").notNull().default(0),
5503
+ startedAt: timestamp("started_at", { withTimezone: true }),
5504
+ lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
5505
+ embeddedAt: timestamp("embedded_at", { withTimezone: true }),
5506
+ resolved: boolean("resolved"),
5507
+ category: text("category"),
5508
+ tech: text("tech").array(),
5509
+ kind: text("kind").notNull().default("human"),
5510
+ symbols: text("symbols").array(),
5511
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5512
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5513
+ },
5514
+ (table) => [
5515
+ uniqueIndex("uq_agent_memory_local_conversation").on(
5516
+ table.localConversationId
5517
+ ),
5518
+ index("idx_agent_memory_repo").on(table.gitRepository),
5519
+ index("idx_agent_memory_user").on(table.userId),
5520
+ index("idx_agent_memory_kind").on(table.kind),
5521
+ index("idx_agent_memory_category").on(table.category)
5522
+ ]
5523
+ );
5298
5524
  var contentSourceType = pgEnum("content_source_type", [
5299
5525
  "own_site",
5300
5526
  "client_site",
@@ -6012,6 +6238,47 @@ pgTable(
6012
6238
  index("alert_rule_enabled_severity_idx").on(table.enabled, table.severity)
6013
6239
  ]
6014
6240
  );
6241
+ var cursorSkill = pgTable(
6242
+ "cursor_skill",
6243
+ {
6244
+ id: uuid("id").primaryKey().defaultRandom(),
6245
+ slug: text("slug").notNull(),
6246
+ title: text("title").notNull().default(""),
6247
+ currentVersion: integer("current_version").notNull().default(0),
6248
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6249
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6250
+ },
6251
+ (table) => [
6252
+ uniqueIndex("uq_cursor_skill_slug").on(table.slug)
6253
+ ]
6254
+ );
6255
+ pgTable(
6256
+ "cursor_skill_revision",
6257
+ {
6258
+ id: uuid("id").primaryKey().defaultRandom(),
6259
+ skillId: uuid("skill_id").notNull().references(() => cursorSkill.id, { onDelete: "cascade" }),
6260
+ version: integer("version").notNull(),
6261
+ parentVersion: integer("parent_version"),
6262
+ message: text("message").notNull().default(""),
6263
+ files: jsonb("files").notNull().$type(),
6264
+ fileHashes: jsonb("file_hashes").notNull().$type().default({}),
6265
+ sourceRepo: text("source_repo"),
6266
+ sourcePath: text("source_path"),
6267
+ authorUserId: text("author_user_id"),
6268
+ authorLabel: text("author_label"),
6269
+ status: text("status").notNull().default("accepted"),
6270
+ published: boolean("published").notNull().default(true),
6271
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6272
+ },
6273
+ (table) => [
6274
+ uniqueIndex("uq_cursor_skill_revision_skill_version").on(
6275
+ table.skillId,
6276
+ table.version
6277
+ ),
6278
+ index("idx_cursor_skill_revision_skill").on(table.skillId),
6279
+ index("idx_cursor_skill_revision_created").on(table.createdAt)
6280
+ ]
6281
+ );
6015
6282
  pgTable(
6016
6283
  "deployment_project_server",
6017
6284
  {
@@ -6388,6 +6655,49 @@ pgTable(
6388
6655
  index("github_webhook_secret_owner_idx").on(table.owner)
6389
6656
  ]
6390
6657
  );
6658
+ pgTable(
6659
+ "handoff_loop_run",
6660
+ {
6661
+ id: uuid("id").primaryKey().defaultRandom(),
6662
+ runSlug: text("run_slug").notNull(),
6663
+ userId: text("user_id"),
6664
+ gitRepository: text("git_repository"),
6665
+ machine: text("machine"),
6666
+ planSummary: text("plan_summary").notNull().default(""),
6667
+ workerModel: text("worker_model").notNull().default(""),
6668
+ outcome: text("outcome").notNull().default("completed"),
6669
+ executeRounds: integer("execute_rounds").notNull().default(0),
6670
+ planRoundTrips: integer("plan_round_trips").notNull().default(0),
6671
+ questionsSelfAnswered: integer("questions_self_answered").notNull().default(0),
6672
+ questionsEscalated: integer("questions_escalated").notNull().default(0),
6673
+ protocolViolations: integer("protocol_violations").notNull().default(0),
6674
+ reviewIssuesFound: integer("review_issues_found").notNull().default(0),
6675
+ checklistInitial: integer("checklist_initial").notNull().default(0),
6676
+ checklistProposed: integer("checklist_proposed").notNull().default(0),
6677
+ checklistAccepted: integer("checklist_accepted").notNull().default(0),
6678
+ checklistVerified: integer("checklist_verified").notNull().default(0),
6679
+ inputTokens: bigint("input_tokens", { mode: "number" }).notNull().default(0),
6680
+ outputTokens: bigint("output_tokens", { mode: "number" }).notNull().default(0),
6681
+ cacheReadTokens: bigint("cache_read_tokens", { mode: "number" }).notNull().default(0),
6682
+ totalDurationMs: bigint("total_duration_ms", { mode: "number" }).notNull().default(0),
6683
+ retro: text("retro"),
6684
+ metrics: jsonb("metrics"),
6685
+ reviewed: boolean("reviewed").notNull().default(false),
6686
+ reviewNotes: text("review_notes"),
6687
+ reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
6688
+ startedAt: timestamp("started_at", { withTimezone: true }),
6689
+ completedAt: timestamp("completed_at", { withTimezone: true }),
6690
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6691
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6692
+ },
6693
+ (table) => [
6694
+ uniqueIndex("uq_handoff_loop_run_slug").on(table.runSlug),
6695
+ index("idx_handoff_loop_run_user").on(table.userId),
6696
+ index("idx_handoff_loop_run_outcome").on(table.outcome),
6697
+ index("idx_handoff_loop_run_reviewed").on(table.reviewed),
6698
+ index("idx_handoff_loop_run_created").on(table.createdAt)
6699
+ ]
6700
+ );
6391
6701
  var managedSiteSyncMode = pgEnum("managed_site_sync_mode", [
6392
6702
  "sitemap",
6393
6703
  "git",
@@ -6493,6 +6803,8 @@ var refrontCustomer = pgTable(
6493
6803
  status: text("status"),
6494
6804
  teamId: text("team_id"),
6495
6805
  teamName: text("team_name"),
6806
+ /** Alternative names from Refront (trading names / brands). */
6807
+ aliases: text("aliases").array().notNull().default([]),
6496
6808
  syncedAt: timestamp("synced_at", { withTimezone: true }).notNull(),
6497
6809
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6498
6810
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
@@ -6644,114 +6956,6 @@ pgTable(
6644
6956
  }).onDelete("cascade")
6645
6957
  ]
6646
6958
  );
6647
- pgTable(
6648
- "wiki_page",
6649
- {
6650
- id: uuid("id").primaryKey().defaultRandom(),
6651
- slug: text("slug").notNull().unique(),
6652
- title: text("title").notNull().default(""),
6653
- summary: text("summary"),
6654
- content: text("content").notNull().default(""),
6655
- status: text("status").notNull().default("draft"),
6656
- pageUpdatedOn: date("page_updated_on"),
6657
- reviewedBy: text("reviewed_by"),
6658
- reviewedOn: date("reviewed_on"),
6659
- lastVerifiedOn: date("last_verified_on"),
6660
- verifyCount: integer("verify_count").notNull().default(0),
6661
- verifyNote: text("verify_note"),
6662
- dirty: boolean("dirty").notNull().default(false),
6663
- vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
6664
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6665
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6666
- },
6667
- (table) => [
6668
- index("idx_wiki_page_status").on(table.status),
6669
- index("idx_wiki_page_last_verified").on(table.lastVerifiedOn),
6670
- index("idx_wiki_page_dirty").on(table.dirty)
6671
- ]
6672
- );
6673
- pgTable(
6674
- "wiki_change_log",
6675
- {
6676
- id: uuid("id").primaryKey().defaultRandom(),
6677
- authorJob: text("author_job").notNull(),
6678
- summary: text("summary").notNull().default(""),
6679
- slugs: text("slugs").array().notNull().default([]),
6680
- syncedAt: timestamp("synced_at", { withTimezone: true }),
6681
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6682
- },
6683
- (table) => [index("idx_wiki_change_log_created").on(table.createdAt)]
6684
- );
6685
- pgTable(
6686
- "wiki_page_flag",
6687
- {
6688
- id: uuid("id").primaryKey().defaultRandom(),
6689
- pageSlug: text("page_slug").notNull(),
6690
- kind: text("kind").notNull(),
6691
- detail: text("detail").notNull().default(""),
6692
- sourceRef: text("source_ref"),
6693
- createdBy: text("created_by"),
6694
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6695
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
6696
- resolvedBy: text("resolved_by")
6697
- },
6698
- (table) => [index("idx_wiki_page_flag_created").on(table.createdAt)]
6699
- );
6700
- pgTable(
6701
- "wiki_page_evidence",
6702
- {
6703
- id: uuid("id").primaryKey().defaultRandom(),
6704
- pageSlug: text("page_slug").notNull(),
6705
- evidenceType: text("evidence_type").notNull(),
6706
- evidenceRef: text("evidence_ref").notNull(),
6707
- matchScore: real("match_score"),
6708
- matchReason: text("match_reason"),
6709
- snippet: text("snippet"),
6710
- status: text("status").notNull().default("pending"),
6711
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6712
- resolvedAt: timestamp("resolved_at", { withTimezone: true })
6713
- },
6714
- (table) => [
6715
- uniqueIndex("wiki_page_evidence_page_slug_evidence_type_evidence_ref_key").on(
6716
- table.pageSlug,
6717
- table.evidenceType,
6718
- table.evidenceRef
6719
- ),
6720
- index("idx_wiki_page_evidence_created").on(table.createdAt)
6721
- ]
6722
- );
6723
- pgTable(
6724
- "wiki_page_read",
6725
- {
6726
- id: uuid("id").primaryKey().defaultRandom(),
6727
- slug: text("slug").notNull(),
6728
- caller: text("caller"),
6729
- includedArchive: boolean("included_archive").notNull().default(false),
6730
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6731
- },
6732
- (table) => [
6733
- index("idx_wiki_page_read_slug").on(table.slug, table.createdAt),
6734
- index("idx_wiki_page_read_created").on(table.createdAt)
6735
- ]
6736
- );
6737
- pgTable(
6738
- "wiki_verify_event",
6739
- {
6740
- id: uuid("id").primaryKey().defaultRandom(),
6741
- pageSlug: text("page_slug").notNull(),
6742
- outcome: text("outcome").notNull(),
6743
- runMode: text("run_mode"),
6744
- note: text("note"),
6745
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6746
- },
6747
- (table) => [
6748
- index("idx_wiki_verify_event_created").on(table.createdAt),
6749
- index("idx_wiki_verify_event_outcome_created").on(
6750
- table.outcome,
6751
- table.createdAt
6752
- )
6753
- ]
6754
- );
6755
6959
  var wikiActor = pgTable(
6756
6960
  "wiki_actor",
6757
6961
  {
@@ -8074,6 +8278,7 @@ var TOOL_MODULE_MAP = {
8074
8278
  "dns-record": "domains",
8075
8279
  "cursor-remote-list": "cursor_remote",
8076
8280
  "cursor-remote-run": "cursor_remote",
8281
+ // cursor-skill intentionally unmapped — any valid API key may pull shared skills
8077
8282
  ...TRIGGER_TOOL_MODULE_MAP
8078
8283
  };
8079
8284
  var SUPERADMIN_ONLY_TOOLS = /* @__PURE__ */ new Set(["trigger-env"]);
@@ -8635,7 +8840,7 @@ function getEncryptionKey() {
8635
8840
  throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
8636
8841
  return buf;
8637
8842
  }
8638
- function encrypt(text16) {
8843
+ function encrypt(text19) {
8639
8844
  const key = getEncryptionKey();
8640
8845
  const iv = randomBytes(ENC_IV_LENGTH);
8641
8846
  const cipher = createCipheriv(
@@ -8643,7 +8848,7 @@ function encrypt(text16) {
8643
8848
  new Uint8Array(key),
8644
8849
  new Uint8Array(iv)
8645
8850
  );
8646
- let encrypted = cipher.update(text16, "utf8", "hex");
8851
+ let encrypted = cipher.update(text19, "utf8", "hex");
8647
8852
  encrypted += cipher.final("hex");
8648
8853
  const authTag = cipher.getAuthTag();
8649
8854
  return Buffer.concat([
@@ -9363,10 +9568,10 @@ async function r2GetObjectRange(bucket, key, range) {
9363
9568
  const body = result.Body;
9364
9569
  if (!body?.transformToString)
9365
9570
  throw new Error("R2 returned no readable body");
9366
- const text16 = await body.transformToString();
9571
+ const text19 = await body.transformToString();
9367
9572
  const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
9368
9573
  return `${header}
9369
- ${text16}`;
9574
+ ${text19}`;
9370
9575
  } catch (e) {
9371
9576
  throw r2WrapError(bucket, key, e);
9372
9577
  }
@@ -9729,15 +9934,15 @@ async function sftpRead(opts, filePath, proxy, options) {
9729
9934
  clearTimeout(timer);
9730
9935
  cleanup?.();
9731
9936
  cleanup = void 0;
9732
- const text16 = Buffer.concat(
9937
+ const text19 = Buffer.concat(
9733
9938
  chunks.map((ch) => new Uint8Array(ch))
9734
9939
  ).toString("utf-8");
9735
9940
  if (!isWholeFileRequest) {
9736
9941
  const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
9737
9942
  resolve(`${header}
9738
- ${text16}`);
9943
+ ${text19}`);
9739
9944
  } else {
9740
- resolve(text16);
9945
+ resolve(text19);
9741
9946
  }
9742
9947
  });
9743
9948
  rs.on("error", (e) => {
@@ -9783,7 +9988,9 @@ var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
9783
9988
  "save_content_quality_run",
9784
9989
  "link_content_pack_to_ticket",
9785
9990
  "managed-site",
9786
- "content-sync"
9991
+ "content-sync",
9992
+ // Skill registry metadata only (no file bodies) — must stay valid JSON.
9993
+ "cursor-skill"
9787
9994
  ]);
9788
9995
  var TOOL_CACHE_TTL_MS = {
9789
9996
  "list-servers": 6e4,
@@ -9884,11 +10091,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
9884
10091
  if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
9885
10092
  return e.names;
9886
10093
  }
9887
- function truncateForLLM(text16, maxBytes) {
9888
- const totalBytes = Buffer.byteLength(text16, "utf8");
10094
+ function truncateForLLM(text19, maxBytes) {
10095
+ const totalBytes = Buffer.byteLength(text19, "utf8");
9889
10096
  if (totalBytes <= maxBytes)
9890
- return { text: text16, truncated: false, totalBytes, shownBytes: totalBytes };
9891
- const buf = Buffer.from(text16, "utf8");
10097
+ return { text: text19, truncated: false, totalBytes, shownBytes: totalBytes };
10098
+ const buf = Buffer.from(text19, "utf8");
9892
10099
  let cut = maxBytes;
9893
10100
  while (cut > 0 && (buf[cut] & 192) === 128) cut--;
9894
10101
  const head = buf.subarray(0, cut).toString("utf8");
@@ -9919,10 +10126,10 @@ function postprocessResult(result, meta) {
9919
10126
  if (!result.content?.length) return result;
9920
10127
  if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
9921
10128
  const block = result.content[0];
9922
- let text16 = String(block.text ?? "");
9923
- const trunc = truncateForLLM(text16, RESPONSE_MAX_BYTES);
10129
+ let text19 = String(block.text ?? "");
10130
+ const trunc = truncateForLLM(text19, RESPONSE_MAX_BYTES);
9924
10131
  if (trunc.truncated) {
9925
- text16 = trunc.text + "\n\n... " + buildTruncationHint(
10132
+ text19 = trunc.text + "\n\n... " + buildTruncationHint(
9926
10133
  meta.toolName,
9927
10134
  meta.args,
9928
10135
  trunc.totalBytes,
@@ -9936,11 +10143,11 @@ function postprocessResult(result, meta) {
9936
10143
  const parts = [`took ${tookStr}`, sizeStr];
9937
10144
  if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
9938
10145
  if (meta.cached) parts.push("cached");
9939
- text16 = `${text16}
10146
+ text19 = `${text19}
9940
10147
 
9941
10148
  [${parts.join(", ")}]`;
9942
10149
  }
9943
- return { ...result, content: [{ ...block, text: text16 }] };
10150
+ return { ...result, content: [{ ...block, text: text19 }] };
9944
10151
  }
9945
10152
  function buildPipelineScript(commands, shell, marker, stopOnError) {
9946
10153
  if (shell === "powershell") {
@@ -12595,10 +12802,38 @@ var TOOLS = [
12595
12802
  required: ["project", "prompt"]
12596
12803
  }
12597
12804
  },
12805
+ // ----- Shared Cursor skills (metadata only; install via downloads ZIP) -----
12806
+ {
12807
+ name: "cursor-skill",
12808
+ description: 'Shared Cursor skill registry (metadata only \u2014 never returns file bodies; MCP truncates large text). Actions: list (slugs+versions), status (localVersion vs tip), pull (install pointer only). To install bytes: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt (skills/<slug>.zip=N) then GET \u2026/api/downloads/skills/<slug>.zip and expand into .cursor/skills/<slug>/. Publish from mg-dashboard: bun scripts/skills/pack-and-publish.ts --slug <slug> --message "\u2026". No push via this tool.',
12809
+ inputSchema: {
12810
+ type: "object",
12811
+ properties: {
12812
+ action: {
12813
+ type: "string",
12814
+ enum: ["list", "status", "pull"],
12815
+ description: "list = registry overview; status = local vs remote; pull = tip metadata + installCommand (no files)"
12816
+ },
12817
+ slug: {
12818
+ type: "string",
12819
+ description: "Required for status/pull. e.g. plan-handoff-loop"
12820
+ },
12821
+ localVersion: {
12822
+ type: "number",
12823
+ description: "For status: version from .cursor/skills/<slug>/.mg-skill.json (0 if missing)"
12824
+ },
12825
+ version: {
12826
+ type: "number",
12827
+ description: "For pull: optional specific revision (default tip)"
12828
+ }
12829
+ },
12830
+ required: ["action"]
12831
+ }
12832
+ },
12598
12833
  // ----- Trigger.dev -----
12599
12834
  ...TRIGGER_TOOLS
12600
12835
  ];
12601
- var MCP_VERSION = "7.3.0";
12836
+ var MCP_VERSION = "7.4.13";
12602
12837
  async function handleListTools() {
12603
12838
  if (!authContext) return { tools: TOOLS };
12604
12839
  const allowedTools = authContext.allowedTools;
@@ -12856,7 +13091,7 @@ async function executeToolCall(name, a, _serverId) {
12856
13091
  ]
12857
13092
  };
12858
13093
  }
12859
- const lines = data.hits.map((hit, index13) => {
13094
+ const lines = data.hits.map((hit, index16) => {
12860
13095
  const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
12861
13096
  const repo = hit.repo ?? "unknown repo";
12862
13097
  const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
@@ -12866,7 +13101,7 @@ async function executeToolCall(name, a, _serverId) {
12866
13101
  ...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
12867
13102
  ].filter(Boolean);
12868
13103
  const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
12869
- return `${index13 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
13104
+ return `${index16 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
12870
13105
  id: ${hit.id}
12871
13106
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
12872
13107
  });
@@ -13073,12 +13308,12 @@ Searchable via search-team-memory once indexing completes (usually immediate).`
13073
13308
  ]
13074
13309
  };
13075
13310
  }
13076
- const lines = data.hits.map((hit, index13) => {
13311
+ const lines = data.hits.map((hit, index16) => {
13077
13312
  const sim = hit.similarity !== null ? ` \xB7 ${Math.round(hit.similarity * 100)}% match` : "";
13078
13313
  const tags = hit.tags.length ? ` \xB7 ${hit.tags.slice(0, 5).join(", ")}` : "";
13079
13314
  const sources = hit.sources.length ? `
13080
13315
  sources: ${hit.sources.slice(0, 6).map((s) => `${s.type}:${s.ref}`).join(", ")}` : "";
13081
- return `${index13 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
13316
+ return `${index16 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
13082
13317
  slug: ${hit.slug}${sources}
13083
13318
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 400)}`;
13084
13319
  });
@@ -14335,8 +14570,8 @@ ${sample.join("\n")}${files.length > 5 ? `
14335
14570
  };
14336
14571
  const filtered = sortRows(applyFilter(only.rows));
14337
14572
  if (format === "json") {
14338
- const text17 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
14339
- return { content: [{ type: "text", text: text17 }] };
14573
+ const text20 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
14574
+ return { content: [{ type: "text", text: text20 }] };
14340
14575
  }
14341
14576
  if (groupByProject) {
14342
14577
  const groups = /* @__PURE__ */ new Map();
@@ -14363,8 +14598,8 @@ ${sample.join("\n")}${files.length > 5 ? `
14363
14598
  }
14364
14599
  const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
14365
14600
  const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
14366
- const text16 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
14367
- return { content: [{ type: "text", text: text16 }] };
14601
+ const text19 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
14602
+ return { content: [{ type: "text", text: text19 }] };
14368
14603
  }
14369
14604
  if (format === "json") {
14370
14605
  const lines = [];
@@ -16010,6 +16245,176 @@ ${lines.join("\n")}`
16010
16245
  return { content: [{ type: "text", text: okMessage }] };
16011
16246
  });
16012
16247
  }
16248
+ // ----- Shared Cursor skills (pull) -----
16249
+ case "cursor-skill": {
16250
+ const action = String(a.action || "").trim().toLowerCase();
16251
+ if (!["list", "status", "pull"].includes(action)) {
16252
+ return {
16253
+ content: [
16254
+ {
16255
+ type: "text",
16256
+ text: 'Error: action must be "list" | "status" | "pull"'
16257
+ }
16258
+ ]
16259
+ };
16260
+ }
16261
+ if (action === "list") {
16262
+ const rows = await db.execute(sql`
16263
+ SELECT slug, title, current_version, updated_at
16264
+ FROM cursor_skill
16265
+ ORDER BY slug
16266
+ `);
16267
+ if (!rows.length) {
16268
+ return {
16269
+ content: [
16270
+ {
16271
+ type: "text",
16272
+ text: "No shared skills registered yet. Publish from mg-dashboard with scripts/skills/pack-and-publish.ts"
16273
+ }
16274
+ ]
16275
+ };
16276
+ }
16277
+ const lines = rows.map(
16278
+ (r) => `${r.slug}@${r.current_version} ${r.title || ""} (updated ${r.updated_at})`
16279
+ );
16280
+ return {
16281
+ content: [
16282
+ {
16283
+ type: "text",
16284
+ text: `${lines.join("\n")}
16285
+
16286
+ Install: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt then \u2026/skills/<slug>.zip. Metadata: cursor-skill action=pull slug=\u2026`
16287
+ }
16288
+ ]
16289
+ };
16290
+ }
16291
+ const slug = String(a.slug || "").trim().toLowerCase();
16292
+ if (!slug) {
16293
+ return {
16294
+ content: [
16295
+ {
16296
+ type: "text",
16297
+ text: `Error: slug is required for action=${action}`
16298
+ }
16299
+ ]
16300
+ };
16301
+ }
16302
+ if (action === "status") {
16303
+ const localVersion = Number(a.localVersion ?? 0);
16304
+ const rows = await db.execute(sql`
16305
+ SELECT current_version, title
16306
+ FROM cursor_skill
16307
+ WHERE slug = ${slug}
16308
+ LIMIT 1
16309
+ `);
16310
+ const tip = rows[0];
16311
+ if (!tip) {
16312
+ return {
16313
+ content: [
16314
+ {
16315
+ type: "text",
16316
+ text: JSON.stringify(
16317
+ {
16318
+ state: "unknown",
16319
+ slug,
16320
+ localVersion,
16321
+ remoteVersion: null,
16322
+ hint: "Skill not in registry"
16323
+ },
16324
+ null,
16325
+ 2
16326
+ )
16327
+ }
16328
+ ]
16329
+ };
16330
+ }
16331
+ const remoteVersion = Number(tip.current_version);
16332
+ const state = localVersion >= remoteVersion && localVersion > 0 ? "up-to-date" : remoteVersion > localVersion ? "behind" : "up-to-date";
16333
+ return {
16334
+ content: [
16335
+ {
16336
+ type: "text",
16337
+ text: JSON.stringify(
16338
+ {
16339
+ state,
16340
+ slug,
16341
+ title: tip.title,
16342
+ localVersion,
16343
+ remoteVersion,
16344
+ next: state === "behind" ? `GET https://dashboard.mgsoftware.nl/api/downloads/skills/${slug}.zip \u2192 expand into .cursor/skills/${slug}/` : "No pull needed"
16345
+ },
16346
+ null,
16347
+ 2
16348
+ )
16349
+ }
16350
+ ]
16351
+ };
16352
+ }
16353
+ const skills = await db.execute(sql`
16354
+ SELECT id, current_version, title
16355
+ FROM cursor_skill
16356
+ WHERE slug = ${slug}
16357
+ LIMIT 1
16358
+ `);
16359
+ const skill = skills[0];
16360
+ if (!skill) {
16361
+ return {
16362
+ content: [
16363
+ {
16364
+ type: "text",
16365
+ text: `Error: skill "${slug}" not found in registry`
16366
+ }
16367
+ ]
16368
+ };
16369
+ }
16370
+ const wantVersion = typeof a.version === "number" && Number.isFinite(a.version) ? Math.floor(a.version) : Number(skill.current_version);
16371
+ const revisions = await db.execute(sql`
16372
+ SELECT version, message, created_at
16373
+ FROM cursor_skill_revision
16374
+ WHERE skill_id = ${skill.id} AND version = ${wantVersion}
16375
+ LIMIT 1
16376
+ `);
16377
+ const rev = revisions[0];
16378
+ if (!rev) {
16379
+ return {
16380
+ content: [
16381
+ {
16382
+ type: "text",
16383
+ text: `Error: revision ${wantVersion} not found for ${slug}`
16384
+ }
16385
+ ]
16386
+ };
16387
+ }
16388
+ const installPath = `.cursor/skills/${slug}`;
16389
+ const zipDownloadsUrl = `${dashboardBaseUrl}/api/downloads/skills/${slug}.zip`;
16390
+ const versionsUrl = `${dashboardBaseUrl}/api/downloads/versions.txt`;
16391
+ const installCommand = `Invoke-WebRequest '${zipDownloadsUrl}' -OutFile skill.zip; Expand into ${installPath}/ (check ${versionsUrl} for tip version)`;
16392
+ return {
16393
+ content: [
16394
+ {
16395
+ type: "text",
16396
+ text: JSON.stringify(
16397
+ {
16398
+ slug,
16399
+ title: skill.title,
16400
+ version: rev.version,
16401
+ message: rev.message,
16402
+ createdAt: rev.created_at,
16403
+ installPath,
16404
+ zipApiPath: `/api/cursor-skill/${slug}/zip`,
16405
+ zipApiUrl: `${dashboardBaseUrl}/api/cursor-skill/${slug}/zip`,
16406
+ zipDownloadsUrl,
16407
+ versionsUrl,
16408
+ installCommand,
16409
+ instruction: `Do NOT expect file bodies from this tool. Download ZIP from \`${zipDownloadsUrl}\` (or compare tip via \`${versionsUrl}\` line skills/${slug}.zip=N) into ${installPath}/. Then re-read SKILL.md from disk. Do not commit unless the user asks. Publish: bun scripts/skills/pack-and-publish.ts --slug <slug> --message "\u2026".`
16410
+ },
16411
+ null,
16412
+ 2
16413
+ )
16414
+ }
16415
+ ]
16416
+ };
16417
+ }
16013
16418
  default:
16014
16419
  if (TRIGGER_TOOL_NAMES.has(name)) {
16015
16420
  return handleTriggerTool(name, a, { sshExec, getServerConnection });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgsoftwarebv/mg-dashboard-mcp",
3
- "version": "7.4.11",
3
+ "version": "7.4.13",
4
4
  "description": "MCP Server for MG Dashboard - SSH, SFTP, Docker, domains, DNS, and environment config tools for Cursor",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",