@cognistore/mcp-server 1.3.0 → 1.4.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 (2) hide show
  1. package/dist/index.js +200 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -537,11 +537,28 @@ var KnowledgeRepository = class {
537
537
  conditions.push(sql`${knowledgeEntries.scope} = ${filters.scope}`);
538
538
  return this.db.select().from(knowledgeEntries).where(and(...conditions)).orderBy(sql`${knowledgeEntries.createdAt} DESC`).limit(limit);
539
539
  }
540
- async listTags() {
540
+ async listTags(opts = {}) {
541
+ const { from, to } = opts;
542
+ if (from && to) {
543
+ const result2 = await this.db.all(sql`SELECT DISTINCT value FROM knowledge_entries, json_each(knowledge_entries.tags)
544
+ WHERE knowledge_entries.type != 'system'
545
+ AND knowledge_entries.created_at >= ${from}
546
+ AND knowledge_entries.created_at < ${to}`);
547
+ return result2.map((r) => r.value);
548
+ }
541
549
  const result = await this.db.all(sql`SELECT DISTINCT value FROM knowledge_entries, json_each(knowledge_entries.tags) WHERE knowledge_entries.type != 'system'`);
542
550
  return result.map((r) => r.value);
543
551
  }
544
- async topTags(limit = 10) {
552
+ async topTags(limit = 10, opts = {}) {
553
+ const { from, to } = opts;
554
+ if (from && to) {
555
+ const result2 = await this.db.all(sql`SELECT value as tag, COUNT(*) as count FROM knowledge_entries, json_each(knowledge_entries.tags)
556
+ WHERE knowledge_entries.type != 'system'
557
+ AND knowledge_entries.created_at >= ${from}
558
+ AND knowledge_entries.created_at < ${to}
559
+ GROUP BY value ORDER BY count DESC LIMIT ${limit}`);
560
+ return result2;
561
+ }
545
562
  const result = await this.db.all(sql`SELECT value as tag, COUNT(*) as count FROM knowledge_entries, json_each(knowledge_entries.tags) WHERE knowledge_entries.type != 'system' GROUP BY value ORDER BY count DESC LIMIT ${limit}`);
546
563
  return result;
547
564
  }
@@ -553,18 +570,30 @@ var KnowledgeRepository = class {
553
570
  const result = await this.db.all(sql`SELECT MAX(updated_at) as latest FROM knowledge_entries`);
554
571
  return result[0]?.latest ?? null;
555
572
  }
556
- async countByType() {
573
+ async countByType(opts = {}) {
574
+ const { from, to } = opts;
575
+ const conditions = [ne(knowledgeEntries.type, "system")];
576
+ if (from && to) {
577
+ conditions.push(sql`${knowledgeEntries.createdAt} >= ${from}`);
578
+ conditions.push(sql`${knowledgeEntries.createdAt} < ${to}`);
579
+ }
557
580
  const results = await this.db.select({
558
581
  type: knowledgeEntries.type,
559
582
  count: sql`count(*)`
560
- }).from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).groupBy(knowledgeEntries.type);
583
+ }).from(knowledgeEntries).where(and(...conditions)).groupBy(knowledgeEntries.type);
561
584
  return results.map((r) => ({ type: r.type, count: Number(r.count) }));
562
585
  }
563
- async countByScope() {
586
+ async countByScope(opts = {}) {
587
+ const { from, to } = opts;
588
+ const conditions = [ne(knowledgeEntries.type, "system")];
589
+ if (from && to) {
590
+ conditions.push(sql`${knowledgeEntries.createdAt} >= ${from}`);
591
+ conditions.push(sql`${knowledgeEntries.createdAt} < ${to}`);
592
+ }
564
593
  const results = await this.db.select({
565
594
  scope: knowledgeEntries.scope,
566
595
  count: sql`count(*)`
567
- }).from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).groupBy(knowledgeEntries.scope);
596
+ }).from(knowledgeEntries).where(and(...conditions)).groupBy(knowledgeEntries.scope);
568
597
  return results.map((r) => ({ scope: r.scope, count: Number(r.count) }));
569
598
  }
570
599
  async listAll() {
@@ -1043,11 +1072,11 @@ var KnowledgeService = class {
1043
1072
  const entries = await this.repository.listRecent(limit, filters);
1044
1073
  return entries.map((e) => this.toKnowledgeEntry(e));
1045
1074
  }
1046
- async topTags(limit = 10) {
1047
- return this.repository.topTags(limit);
1075
+ async topTags(limit = 10, opts = {}) {
1076
+ return this.repository.topTags(limit, opts);
1048
1077
  }
1049
- async listTags() {
1050
- return this.repository.listTags();
1078
+ async listTags(opts = {}) {
1079
+ return this.repository.listTags(opts);
1051
1080
  }
1052
1081
  async getStats() {
1053
1082
  const [count, byType, byScope, lastUpdatedAt] = await Promise.all([
@@ -1058,6 +1087,12 @@ var KnowledgeService = class {
1058
1087
  ]);
1059
1088
  return { total: count, byType, byScope, lastUpdatedAt };
1060
1089
  }
1090
+ async countByType(opts = {}) {
1091
+ return this.repository.countByType(opts);
1092
+ }
1093
+ async countByScope(opts = {}) {
1094
+ return this.repository.countByScope(opts);
1095
+ }
1061
1096
  // ─── Plans (separate entity) ────────────────────────────────
1062
1097
  async createPlan(input) {
1063
1098
  const { tasks, skipDedup, ...planInput } = input;
@@ -1498,11 +1533,13 @@ var TokenUsageScanner = class {
1498
1533
  batch = [];
1499
1534
  };
1500
1535
  for await (const { record, filePath, byteOffset, mtime } of adapter.scan((fp) => this.repo.getScanState(adapter.name, fp))) {
1501
- batch.push(record);
1536
+ if (record) {
1537
+ batch.push(record);
1538
+ if (batch.length >= BATCH_SIZE)
1539
+ flush();
1540
+ }
1502
1541
  offsets.set(filePath, { offset: byteOffset, mtime });
1503
1542
  stats.scanned++;
1504
- if (batch.length >= BATCH_SIZE)
1505
- flush();
1506
1543
  }
1507
1544
  flush();
1508
1545
  for (const [filePath, { offset, mtime }] of offsets) {
@@ -1656,6 +1693,135 @@ var ClaudeCodeAdapter = class {
1656
1693
  }
1657
1694
  };
1658
1695
 
1696
+ // ../../packages/core/dist/services/token-usage/adapters/copilot-cli.js
1697
+ import { createHash as createHash2 } from "crypto";
1698
+ import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync2, readFileSync as readFileSync2 } from "fs";
1699
+ import { homedir as homedir3 } from "os";
1700
+ import { resolve as resolve4, basename as basename2 } from "path";
1701
+ var SOURCE2 = "copilot-cli";
1702
+ function recordId2(sessionId, model, occurredAt) {
1703
+ const key = `${SOURCE2}|${sessionId ?? ""}|${model}|${occurredAt}`;
1704
+ return createHash2("sha256").update(key).digest("hex").slice(0, 32);
1705
+ }
1706
+ function parseEventsFile(filePath) {
1707
+ let raw;
1708
+ try {
1709
+ raw = readFileSync2(filePath, "utf8");
1710
+ } catch {
1711
+ return null;
1712
+ }
1713
+ let cwd = null;
1714
+ let sessionId = null;
1715
+ let shutdown = null;
1716
+ for (const line of raw.split("\n")) {
1717
+ if (!line)
1718
+ continue;
1719
+ let evt;
1720
+ try {
1721
+ evt = JSON.parse(line);
1722
+ } catch {
1723
+ continue;
1724
+ }
1725
+ if (!evt || typeof evt !== "object")
1726
+ continue;
1727
+ if (evt.type === "session.start") {
1728
+ const data = evt.data ?? {};
1729
+ sessionId = data.sessionId ?? sessionId;
1730
+ const ctx = data.context ?? {};
1731
+ cwd = ctx.cwd ?? cwd;
1732
+ } else if (evt.type === "session.shutdown") {
1733
+ const data = evt.data ?? {};
1734
+ const modelMetrics = data.modelMetrics;
1735
+ if (!modelMetrics || typeof modelMetrics !== "object")
1736
+ continue;
1737
+ const perModel = [];
1738
+ for (const [model, payload] of Object.entries(modelMetrics)) {
1739
+ const usage = payload?.usage;
1740
+ if (!usage || typeof usage !== "object")
1741
+ continue;
1742
+ perModel.push({
1743
+ model,
1744
+ inputTokens: Number(usage.inputTokens ?? 0) || 0,
1745
+ outputTokens: Number(usage.outputTokens ?? 0) || 0,
1746
+ cacheReadTokens: Number(usage.cacheReadTokens ?? 0) || 0,
1747
+ cacheCreationTokens: Number(usage.cacheWriteTokens ?? 0) || 0
1748
+ });
1749
+ }
1750
+ if (perModel.length === 0)
1751
+ continue;
1752
+ shutdown = {
1753
+ occurredAt: evt.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
1754
+ sessionId,
1755
+ cwd,
1756
+ perModel
1757
+ };
1758
+ }
1759
+ }
1760
+ return shutdown;
1761
+ }
1762
+ var CopilotCliAdapter = class {
1763
+ name = SOURCE2;
1764
+ sessionStateDir;
1765
+ constructor(options = {}) {
1766
+ this.sessionStateDir = options.sessionStateDir ?? resolve4(homedir3(), ".copilot", "session-state");
1767
+ }
1768
+ async *scan(getState) {
1769
+ if (!existsSync3(this.sessionStateDir))
1770
+ return;
1771
+ let entries;
1772
+ try {
1773
+ entries = readdirSync3(this.sessionStateDir);
1774
+ } catch {
1775
+ return;
1776
+ }
1777
+ for (const entry of entries) {
1778
+ const sessionDir = resolve4(this.sessionStateDir, entry);
1779
+ let stat;
1780
+ try {
1781
+ stat = statSync2(sessionDir);
1782
+ } catch {
1783
+ continue;
1784
+ }
1785
+ if (!stat.isDirectory())
1786
+ continue;
1787
+ const filePath = resolve4(sessionDir, "events.jsonl");
1788
+ let fstat;
1789
+ try {
1790
+ fstat = statSync2(filePath);
1791
+ } catch {
1792
+ continue;
1793
+ }
1794
+ const fileSize = fstat.size;
1795
+ const mtime = fstat.mtime.toISOString();
1796
+ const state = getState(filePath);
1797
+ if (state && state.lastOffset >= fileSize)
1798
+ continue;
1799
+ const parsed = parseEventsFile(filePath);
1800
+ if (!parsed) {
1801
+ yield { record: null, filePath, byteOffset: fileSize, mtime };
1802
+ continue;
1803
+ }
1804
+ const project = parsed.cwd ? basename2(parsed.cwd) : null;
1805
+ for (const m of parsed.perModel) {
1806
+ const record = {
1807
+ id: recordId2(parsed.sessionId, m.model, parsed.occurredAt),
1808
+ source: SOURCE2,
1809
+ model: m.model,
1810
+ project,
1811
+ sessionId: parsed.sessionId,
1812
+ messageId: null,
1813
+ occurredAt: parsed.occurredAt,
1814
+ inputTokens: m.inputTokens,
1815
+ outputTokens: m.outputTokens,
1816
+ cacheReadTokens: m.cacheReadTokens,
1817
+ cacheCreationTokens: m.cacheCreationTokens
1818
+ };
1819
+ yield { record, filePath, byteOffset: fileSize, mtime };
1820
+ }
1821
+ }
1822
+ }
1823
+ };
1824
+
1659
1825
  // ../../packages/core/dist/services/token-usage/service.js
1660
1826
  var DEFAULT_TOP_SESSIONS = 20;
1661
1827
  var TokenUsageService = class {
@@ -1663,7 +1829,7 @@ var TokenUsageService = class {
1663
1829
  scanner;
1664
1830
  constructor(repo, adapters) {
1665
1831
  this.repo = repo;
1666
- this.scanner = new TokenUsageScanner(repo, adapters ?? [new ClaudeCodeAdapter()]);
1832
+ this.scanner = new TokenUsageScanner(repo, adapters ?? [new ClaudeCodeAdapter(), new CopilotCliAdapter()]);
1667
1833
  }
1668
1834
  scan() {
1669
1835
  return this.scanner.scanAll();
@@ -2001,22 +2167,38 @@ var KnowledgeSDK = class {
2001
2167
  throw this.wrapError(error, "Failed to list recent knowledge");
2002
2168
  }
2003
2169
  }
2004
- async getTopTags(limit = 10) {
2170
+ async getTopTags(limit = 10, opts = {}) {
2005
2171
  this.ensureInitialized();
2006
2172
  try {
2007
- return await this.service.topTags(limit);
2173
+ return await this.service.topTags(limit, opts);
2008
2174
  } catch (error) {
2009
2175
  throw this.wrapError(error, "Failed to get top tags");
2010
2176
  }
2011
2177
  }
2012
- async listTags() {
2178
+ async listTags(opts = {}) {
2013
2179
  this.ensureInitialized();
2014
2180
  try {
2015
- return await this.service.listTags();
2181
+ return await this.service.listTags(opts);
2016
2182
  } catch (error) {
2017
2183
  throw this.wrapError(error, "Failed to list tags");
2018
2184
  }
2019
2185
  }
2186
+ async countByType(opts = {}) {
2187
+ this.ensureInitialized();
2188
+ try {
2189
+ return await this.service.countByType(opts);
2190
+ } catch (error) {
2191
+ throw this.wrapError(error, "Failed to count by type");
2192
+ }
2193
+ }
2194
+ async countByScope(opts = {}) {
2195
+ this.ensureInitialized();
2196
+ try {
2197
+ return await this.service.countByScope(opts);
2198
+ } catch (error) {
2199
+ throw this.wrapError(error, "Failed to count by scope");
2200
+ }
2201
+ }
2020
2202
  async getStats() {
2021
2203
  this.ensureInitialized();
2022
2204
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognistore/mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "MCP server for CogniStore — integrates with Claude Code and GitHub Copilot",