@basegrid_tech/mcp 0.18.12 → 0.18.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 +183 -12
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -162,6 +162,32 @@ var DEFAULT_NO_PROXY_HOSTS = [
162
162
  ];
163
163
  var DEFAULT_NO_PROXY = DEFAULT_NO_PROXY_HOSTS.join(",");
164
164
 
165
+ // ../shared/src/connector-identity.ts
166
+ var IDENTITY_FIELDS = [
167
+ "owner",
168
+ // github
169
+ "repo",
170
+ // github
171
+ "orgId",
172
+ // yandex tracker
173
+ "queue",
174
+ // yandex tracker
175
+ "teamKey"
176
+ // linear
177
+ ];
178
+ function connectionIdentityKey(connectorId, filters) {
179
+ if (!filters) return "";
180
+ const parts = [];
181
+ for (const key of IDENTITY_FIELDS) {
182
+ const value = filters[key];
183
+ if (typeof value !== "string") continue;
184
+ const normalized = value.trim().toLowerCase();
185
+ if (!normalized) continue;
186
+ parts.push(`${key}=${normalized}`);
187
+ }
188
+ return parts.length > 0 ? `${connectorId}:${parts.join("&")}` : "";
189
+ }
190
+
165
191
  // ../shared/src/quick-commands.ts
166
192
  function normalizeQuickCommands(input) {
167
193
  if (!Array.isArray(input)) return [];
@@ -185,8 +211,13 @@ function normalizeOne(raw, seenIds) {
185
211
  const label = clampString(obj.label, QUICK_COMMAND_LIMITS.maxLabel);
186
212
  if (!label) return null;
187
213
  const scope = normalizeScope(obj.scope);
188
- const kind = obj.kind === "terminal" ? "terminal" : obj.kind === "agent" ? "agent" : null;
214
+ const kind = obj.kind === "terminal" || obj.kind === "agent" || obj.kind === "snippet" ? obj.kind : null;
189
215
  if (!kind) return null;
216
+ if (kind === "snippet") {
217
+ const text2 = clampString(obj.text, QUICK_COMMAND_LIMITS.maxBody);
218
+ if (!text2) return null;
219
+ return { id, label, scope, kind, text: text2 };
220
+ }
190
221
  if (kind === "agent") {
191
222
  const prompt = clampString(obj.prompt, QUICK_COMMAND_LIMITS.maxBody);
192
223
  if (!prompt) return null;
@@ -485,7 +516,8 @@ function createSchema() {
485
516
  );
486
517
 
487
518
  -- Quick commands (\u043F\u043E\u0440\u0442 \u0444\u0438\u0447\u0438 \u0438\u0437 orca): \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u044B.
488
- -- kind='agent' \u2192 \u0437\u0430\u043F\u0443\u0441\u043A \u0430\u0433\u0435\u043D\u0442\u0430 \u0441 \u043F\u0440\u043E\u043C\u043F\u0442\u043E\u043C; kind='terminal' \u2192 \u0448\u0435\u043B\u043B-\u043A\u043E\u043C\u0430\u043D\u0434\u0430.
519
+ -- kind='agent' \u2192 \u0437\u0430\u043F\u0443\u0441\u043A \u0430\u0433\u0435\u043D\u0442\u0430 \u0441 \u043F\u0440\u043E\u043C\u043F\u0442\u043E\u043C; kind='terminal' \u2192 \u0448\u0435\u043B\u043B-\u043A\u043E\u043C\u0430\u043D\u0434\u0430;
520
+ -- kind='snippet' \u2192 \u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u0442\u0435\u043A\u0441\u0442, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0432 \u0438\u043D\u043F\u0443\u0442 \u0441\u0435\u0441\u0441\u0438\u0438.
489
521
  -- scope \u0445\u0440\u0430\u043D\u0438\u0442\u0441\u044F JSON-\u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0434\u043B\u044F \u0431\u0443\u0434\u0443\u0449\u0435\u0439 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u043F\u0440\u043E\u0435\u043A\u0442\u0443 (\u0441\u0435\u0439\u0447\u0430\u0441 global).
490
522
  CREATE TABLE IF NOT EXISTS quick_commands (
491
523
  id TEXT PRIMARY KEY,
@@ -498,7 +530,8 @@ function createSchema() {
498
530
  command TEXT,
499
531
  append_enter INTEGER NOT NULL DEFAULT 1,
500
532
  position INTEGER NOT NULL DEFAULT 0,
501
- shell TEXT
533
+ shell TEXT,
534
+ snippet_text TEXT
502
535
  );
503
536
 
504
537
  CREATE TABLE IF NOT EXISTS remote_hosts (
@@ -1608,6 +1641,120 @@ function migrateSchema(d) {
1608
1641
  })();
1609
1642
  logger_default.info("[database] migrated schema to version 37 (session_activity timestamp index)");
1610
1643
  }
1644
+ if (version < 38) {
1645
+ d.transaction(() => {
1646
+ const cols = d.prepare("PRAGMA table_info(quick_commands)").all();
1647
+ if (!cols.some((c) => c.name === "snippet_text")) {
1648
+ d.exec("ALTER TABLE quick_commands ADD COLUMN snippet_text TEXT");
1649
+ }
1650
+ d.prepare(
1651
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '38')"
1652
+ ).run();
1653
+ })();
1654
+ logger_default.info("[database] migrated schema to version 38 (quick command snippets)");
1655
+ }
1656
+ if (version < 39) {
1657
+ d.transaction(() => {
1658
+ const cols = d.prepare("PRAGMA table_info(source_connections)").all();
1659
+ if (cols.length === 0) {
1660
+ d.prepare(
1661
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '39')"
1662
+ ).run();
1663
+ return;
1664
+ }
1665
+ if (!cols.some((c) => c.name === "identity_key")) {
1666
+ d.exec("ALTER TABLE source_connections ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''");
1667
+ }
1668
+ const rows = d.prepare(
1669
+ `SELECT c.id, c.connector_id, c.filters, c.created_at, c.sync_cursor,
1670
+ c.status_mapping, c.sync_interval_minutes, c.execution_project,
1671
+ (SELECT count(*) FROM task_source_links l WHERE l.connection_id = c.id) AS link_count
1672
+ FROM source_connections c`
1673
+ ).all();
1674
+ const setIdentity = d.prepare("UPDATE source_connections SET identity_key = ? WHERE id = ?");
1675
+ const setFilters = d.prepare("UPDATE source_connections SET filters = ? WHERE id = ?");
1676
+ const groups = /* @__PURE__ */ new Map();
1677
+ for (const row2 of rows) {
1678
+ let filters;
1679
+ try {
1680
+ filters = JSON.parse(row2.filters);
1681
+ } catch {
1682
+ filters = {};
1683
+ }
1684
+ if (row2.connector_id === "github" && "state" in filters) {
1685
+ delete filters.state;
1686
+ setFilters.run(JSON.stringify(filters), row2.id);
1687
+ }
1688
+ const key = connectionIdentityKey(row2.connector_id, filters);
1689
+ setIdentity.run(key, row2.id);
1690
+ if (!key) continue;
1691
+ const bucket = groups.get(key) ?? [];
1692
+ bucket.push({ ...row2, parsedFilters: filters });
1693
+ groups.set(key, bucket);
1694
+ }
1695
+ const relinkLinks = d.prepare(
1696
+ "UPDATE OR IGNORE task_source_links SET connection_id = ? WHERE connection_id = ?"
1697
+ );
1698
+ const relinkHistory = d.prepare(
1699
+ "UPDATE sync_history SET connection_id = ? WHERE connection_id = ?"
1700
+ );
1701
+ const adoptSettings = d.prepare(
1702
+ `UPDATE source_connections
1703
+ SET filters = ?, status_mapping = ?, sync_interval_minutes = ?,
1704
+ execution_project = ?, sync_cursor = ?
1705
+ WHERE id = ?`
1706
+ );
1707
+ const dropConn = d.prepare("DELETE FROM source_connections WHERE id = ?");
1708
+ let collapsed = 0;
1709
+ let strandedLinks = 0;
1710
+ for (const bucket of groups.values()) {
1711
+ if (bucket.length < 2) continue;
1712
+ const byWeight = [...bucket].sort(
1713
+ (a, b) => b.link_count - a.link_count || a.created_at.localeCompare(b.created_at)
1714
+ );
1715
+ const keep = byWeight[0];
1716
+ const dupes = byWeight.slice(1);
1717
+ const newest = [...bucket].sort((a, b) => b.created_at.localeCompare(a.created_at))[0];
1718
+ const cursors = bucket.map((c) => c.sync_cursor).filter((c) => !!c);
1719
+ const earliest = cursors.length > 0 ? cursors.sort()[0] : null;
1720
+ for (const dupe of dupes) {
1721
+ const moved = Number(relinkLinks.run(keep.id, dupe.id).changes);
1722
+ strandedLinks += dupe.link_count - moved;
1723
+ relinkHistory.run(keep.id, dupe.id);
1724
+ dropConn.run(dupe.id);
1725
+ collapsed++;
1726
+ }
1727
+ adoptSettings.run(
1728
+ JSON.stringify(newest.parsedFilters),
1729
+ newest.status_mapping,
1730
+ newest.sync_interval_minutes,
1731
+ newest.execution_project,
1732
+ earliest,
1733
+ keep.id
1734
+ );
1735
+ }
1736
+ const catchUpFrom = new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString();
1737
+ d.prepare(
1738
+ `UPDATE source_connections
1739
+ SET sync_cursor = CASE WHEN created_at > ? THEN created_at ELSE ? END
1740
+ WHERE sync_cursor IS NOT NULL AND sync_cursor > ?`
1741
+ ).run(catchUpFrom, catchUpFrom, catchUpFrom);
1742
+ d.exec(`
1743
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_source_connections_identity
1744
+ ON source_connections(identity_key)
1745
+ WHERE identity_key <> ''
1746
+ `);
1747
+ d.prepare(
1748
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '39')"
1749
+ ).run();
1750
+ if (collapsed > 0) {
1751
+ logger_default.warn(
1752
+ `[database] collapsed ${collapsed} duplicate source connection(s)` + (strandedLinks > 0 ? `; ${strandedLinks} duplicate task link(s) dropped` : "")
1753
+ );
1754
+ }
1755
+ })();
1756
+ logger_default.info("[database] migrated schema to version 39 (source_connections.identity_key)");
1757
+ }
1611
1758
  }
1612
1759
  function verifySchema(d) {
1613
1760
  const expectedTables = [
@@ -1634,7 +1781,8 @@ function verifySchema(d) {
1634
1781
  last_sync_at TEXT,
1635
1782
  last_sync_error TEXT,
1636
1783
  sync_cursor TEXT,
1637
- created_at TEXT NOT NULL
1784
+ created_at TEXT NOT NULL,
1785
+ identity_key TEXT NOT NULL DEFAULT ''
1638
1786
  )`
1639
1787
  },
1640
1788
  {
@@ -1804,8 +1952,17 @@ function verifySchema(d) {
1804
1952
  ddl: "ALTER TABLE external_worktree_settings ADD COLUMN hidden_paths TEXT NOT NULL DEFAULT '[]'"
1805
1953
  }
1806
1954
  ],
1807
- quick_commands: [{ column: "shell", ddl: "ALTER TABLE quick_commands ADD COLUMN shell TEXT" }],
1808
- account: [{ column: "member_since", ddl: "ALTER TABLE account ADD COLUMN member_since TEXT" }]
1955
+ quick_commands: [
1956
+ { column: "shell", ddl: "ALTER TABLE quick_commands ADD COLUMN shell TEXT" },
1957
+ { column: "snippet_text", ddl: "ALTER TABLE quick_commands ADD COLUMN snippet_text TEXT" }
1958
+ ],
1959
+ account: [{ column: "member_since", ddl: "ALTER TABLE account ADD COLUMN member_since TEXT" }],
1960
+ source_connections: [
1961
+ {
1962
+ column: "identity_key",
1963
+ ddl: "ALTER TABLE source_connections ADD COLUMN identity_key TEXT NOT NULL DEFAULT ''"
1964
+ }
1965
+ ]
1809
1966
  };
1810
1967
  for (const [table, columns] of Object.entries(expectedByTable)) {
1811
1968
  const existing = new Set(
@@ -1821,6 +1978,15 @@ function verifySchema(d) {
1821
1978
  }
1822
1979
  }
1823
1980
  }
1981
+ try {
1982
+ d.exec(`
1983
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_source_connections_identity
1984
+ ON source_connections(identity_key)
1985
+ WHERE identity_key <> ''
1986
+ `);
1987
+ } catch (err) {
1988
+ logger_default.error({ err }, "[database] self-heal: failed to ensure source connection identity index");
1989
+ }
1824
1990
  }
1825
1991
  function loadConfig() {
1826
1992
  const d = getDb();
@@ -1975,7 +2141,8 @@ function loadQuickCommands(d) {
1975
2141
  model: r.model ?? void 0,
1976
2142
  command: r.command ?? void 0,
1977
2143
  appendEnter: r.append_enter !== 0,
1978
- shell: r.shell ?? void 0
2144
+ shell: r.shell ?? void 0,
2145
+ text: r.snippet_text ?? void 0
1979
2146
  }));
1980
2147
  return normalizeQuickCommands(raw);
1981
2148
  }
@@ -2084,8 +2251,8 @@ function saveConfig(config) {
2084
2251
  }
2085
2252
  d.prepare("DELETE FROM quick_commands").run();
2086
2253
  const insertQuickCommand = d.prepare(
2087
- `INSERT INTO quick_commands (id, label, kind, scope, agent_type, prompt, model, command, append_enter, position, shell)
2088
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2254
+ `INSERT INTO quick_commands (id, label, kind, scope, agent_type, prompt, model, command, append_enter, position, shell, snippet_text)
2255
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2089
2256
  );
2090
2257
  const normalizedQuickCommands = normalizeQuickCommands(config.quickCommands ?? []);
2091
2258
  normalizedQuickCommands.forEach((q, index) => {
@@ -2100,7 +2267,8 @@ function saveConfig(config) {
2100
2267
  q.kind === "terminal" ? q.command : null,
2101
2268
  q.kind === "terminal" && q.appendEnter ? 1 : 0,
2102
2269
  index,
2103
- q.kind === "terminal" ? q.shell ?? null : null
2270
+ q.kind === "terminal" ? q.shell ?? null : null,
2271
+ q.kind === "snippet" ? q.text : null
2104
2272
  );
2105
2273
  });
2106
2274
  d.prepare("DELETE FROM agent_commands").run();
@@ -4084,7 +4252,10 @@ import { z as z7 } from "zod";
4084
4252
  var ASK_TIMEOUT_MS = 60 * 60 * 1e3;
4085
4253
  var questionSchema = z7.object({
4086
4254
  question: z7.string().min(1).max(1e3).describe("The question to ask the user."),
4087
- header: z7.string().max(40).optional().describe("Very short label/chip shown above the question (max 40 chars)."),
4255
+ // 12 символов потолок «чипа» в карточке (как у встроенного
4256
+ // AskUserQuestion). Обрезаем, а не отвергаем: завалить схему из-за косметики
4257
+ // значит не задать вопрос вовсе, и модель узнает об этом только по ошибке.
4258
+ header: z7.string().max(200).optional().transform((value) => value?.slice(0, 12)).describe("Very short label/chip shown above the question (kept to 12 chars)."),
4088
4259
  multiSelect: z7.boolean().optional().describe("Allow multiple options to be selected. Defaults to single-select."),
4089
4260
  options: z7.array(z7.string().min(1).max(200)).min(2).max(4).describe(
4090
4261
  'Available choices (2\u20134). Do NOT include "Other" yourself \u2014 the UI adds it automatically when supported.'
@@ -4587,7 +4758,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
4587
4758
  console.error = (...args) => _origError("[mcp:error]", ...args);
4588
4759
  async function main() {
4589
4760
  configManager.init();
4590
- const version = true ? "0.18.12" : createRequire(import.meta.url)("../package.json").version;
4761
+ const version = true ? "0.18.13" : createRequire(import.meta.url)("../package.json").version;
4591
4762
  const server = createMcpServer(version);
4592
4763
  const transport = new StdioServerTransport();
4593
4764
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basegrid_tech/mcp",
3
- "version": "0.18.12",
3
+ "version": "0.18.13",
4
4
  "description": "BaseGrid MCP server — task management, git, and workflow tools for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,8 +42,8 @@
42
42
  "zod": "^4.3.6"
43
43
  },
44
44
  "devDependencies": {
45
- "@basegrid/server": "0.18.12",
46
- "@basegrid/shared": "0.18.12",
45
+ "@basegrid/server": "0.18.13",
46
+ "@basegrid/shared": "0.18.13",
47
47
  "tsup": "^8.5.1",
48
48
  "tsx": "^4.21.0",
49
49
  "typescript": "^6.0.3"