copilot-chat-analyzer 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AI_USAGE.md ADDED
@@ -0,0 +1,93 @@
1
+ # AI Usage Guide for copilot-chat-analyzer
2
+
3
+ This is a TypeScript/JavaScript library for analyzing GitHub Copilot chat exports.
4
+
5
+ ## Quick Start for AI Assistants
6
+
7
+ ```typescript
8
+ import CopilotChatAnalyzer, { DialogStatus } from "copilot-chat-analyzer";
9
+ import { readFileSync } from "fs";
10
+
11
+ // Load chat data from JSON export
12
+ const chatData = JSON.parse(readFileSync("chat.json", "utf8"));
13
+ const analyzer = new CopilotChatAnalyzer();
14
+
15
+ // Basic analysis
16
+ const status = analyzer.getDialogStatus(chatData);
17
+ const requestCount = analyzer.getRequestsCount(chatData);
18
+ ```
19
+
20
+ ## Core Methods
21
+
22
+ ### getDialogStatus(chatData)
23
+
24
+ Returns one of four status strings:
25
+
26
+ - `"pending"` - Chat created but no requests made (empty requests array)
27
+ - `"in_progress"` - Chat has requests but not completed
28
+ - `"completed"` - Chat finished successfully (has followups: [])
29
+ - `"canceled"` - Chat was canceled (isCanceled: true)
30
+
31
+ ### getRequestsCount(chatData)
32
+
33
+ Returns number of requests in the chat.
34
+
35
+ ### getDialogStatusDetails(chatData)
36
+
37
+ Returns detailed object:
38
+
39
+ ```typescript
40
+ {
41
+ status: DialogStatusType,
42
+ statusText: string, // Human readable status
43
+ hasResult: boolean, // Has result data
44
+ hasFollowups: boolean, // Has followups property
45
+ isCanceled: boolean, // Was canceled
46
+ lastRequestId?: string // ID of last request
47
+ }
48
+ ```
49
+
50
+ ### getMcpToolMonitoring(chatData, toolName?)
51
+
52
+ Monitor MCP (Model Context Protocol) tool usage:
53
+
54
+ - Without toolName: Returns summary of all tools
55
+ - With toolName: Returns detailed stats for specific tool
56
+
57
+ ## Chat Data Structure
58
+
59
+ Expected JSON structure from GitHub Copilot export:
60
+
61
+ ```typescript
62
+ {
63
+ requesterUsername: string,
64
+ responderUsername: string,
65
+ requests: Array<{
66
+ requestId: string,
67
+ isCanceled?: boolean,
68
+ followups?: any[],
69
+ result?: any,
70
+ response?: any[]
71
+ }>
72
+ }
73
+ ```
74
+
75
+ ## Status Detection Logic
76
+
77
+ 1. No requests or empty array → `"pending"`
78
+ 2. Last request has `isCanceled: true` → `"canceled"`
79
+ 3. Last request has `followups: []` (empty array) → `"completed"`
80
+ 4. Otherwise → `"in_progress"`
81
+
82
+ ## MCP Tool Analysis
83
+
84
+ The library can extract and analyze Model Context Protocol tool calls from chat responses, providing:
85
+
86
+ - Tool success rates
87
+ - Call counts
88
+ - Input/output data
89
+ - Error tracking
90
+
91
+ ## Examples Directory
92
+
93
+ Check `/examples` folder for complete usage examples with sample data.
package/README.md CHANGED
@@ -4,14 +4,39 @@
4
4
  [![Code Quality](https://github.com/dealenx/copilot-chat-analyzer/actions/workflows/quality.yml/badge.svg)](https://github.com/dealenx/copilot-chat-analyzer/actions/workflows/quality.yml)
5
5
  [![npm version](https://badge.fury.io/js/copilot-chat-analyzer.svg)](https://badge.fury.io/js/copilot-chat-analyzer)
6
6
 
7
- Простая библиотека для анализа экспортированных чатов GitHub Copilot.
7
+ TypeScript библиотека для анализа экспортированных чатов GitHub Copilot с поддержкой мониторинга MCP инструментов.
8
+
9
+ ## 🤖 AI Assistant Quick Reference
10
+
11
+ ```typescript
12
+ // Basic import and usage
13
+ import CopilotChatAnalyzer, { DialogStatus } from "copilot-chat-analyzer";
14
+
15
+ const analyzer = new CopilotChatAnalyzer();
16
+ const chatData = JSON.parse(fs.readFileSync("chat.json", "utf8"));
17
+
18
+ // Core methods:
19
+ analyzer.getDialogStatus(chatData); // Returns: 'pending' | 'in_progress' | 'completed' | 'canceled'
20
+ analyzer.getRequestsCount(chatData); // Returns: number
21
+ analyzer.getDialogStatusDetails(chatData); // Returns: { status, statusText, hasResult, hasFollowups, isCanceled, lastRequestId }
22
+ analyzer.getMcpToolMonitoring(chatData); // Returns: MCP tool usage statistics
23
+
24
+ // Dialog statuses:
25
+ // - 'pending': Empty requests array, chat not started
26
+ // - 'in_progress': Has requests but not finished
27
+ // - 'completed': Has followups:[] and not canceled
28
+ // - 'canceled': isCanceled:true in last request
29
+ ```
8
30
 
9
31
  ## Особенности
10
32
 
11
- - 📊 Подсчет количества запросов в диалоге
12
- - 🔍 Определение статуса диалога (завершен, отменен, в процессе)
13
- - 📝 Получение детальной информации о статусе
14
- - 🚀 Простой и понятный API
33
+ - 📊 **Анализ статусов диалога** - автоматическое определение состояния чата
34
+ - 🔢 **Подсчет запросов** - точный подсчет количества запросов в диалоге
35
+ - 🔍 **Детальная диагностика** - получение развернутой информации о статусе
36
+ - �️ **MCP мониторинг** - отслеживание использования Model Context Protocol инструментов
37
+ - 📈 **Статистика успешности** - анализ успешности выполнения MCP вызовов
38
+ - 🚀 **Простой API** - интуитивно понятный интерфейс
39
+ - 💪 **TypeScript** - полная поддержка типов
15
40
 
16
41
  ## Установка
17
42
 
@@ -31,6 +56,17 @@ yarn add copilot-chat-analyzer
31
56
  pnpm add copilot-chat-analyzer
32
57
  ```
33
58
 
59
+ ## Как экспортировать чат
60
+
61
+ Для получения данных чата из GitHub Copilot Chat:
62
+
63
+ 1. **Откройте VS Code** с установленным GitHub Copilot Chat
64
+ 2. **Откройте панель чата** (обычно справа или через `Ctrl+Shift+I`)
65
+ 3. **Ведите диалог** с Copilot
66
+ 4. **Нажмите `F1`** для открытия Command Palette
67
+ 5. **Введите и выберите** `"Export Chat"`
68
+ 6. **Сохраните файл** в формате JSON
69
+
34
70
  ## 🚀 Использование
35
71
 
36
72
  ### Основной API
@@ -48,7 +84,7 @@ console.log(`Количество запросов: ${requestsCount}`);
48
84
 
49
85
  // Определение статуса диалога
50
86
  const status = analyzer.getDialogStatus(chatData);
51
- console.log(`Статус: ${status}`); // 'completed', 'canceled', 'in_progress'
87
+ console.log(`Статус: ${status}`); // 'pending', 'completed', 'canceled', 'in_progress'
52
88
 
53
89
  // Получение детальной информации о статусе
54
90
  const details = analyzer.getDialogStatusDetails(chatData);
@@ -62,8 +98,43 @@ console.log({
62
98
  });
63
99
  ```
64
100
 
101
+ ### Мониторинг MCP инструментов
102
+
103
+ ```javascript
104
+ // Получить список всех MCP инструментов в чате
105
+ const toolNames = analyzer.getMcpToolNames(chatData);
106
+ console.log("Инструменты:", toolNames);
107
+
108
+ // Получить все вызовы конкретного инструмента
109
+ const calls = analyzer.getMcpToolCalls(chatData, "update_entry_fields");
110
+ calls.forEach((call, i) => {
111
+ console.log(
112
+ `${i + 1}. ${call.isError ? "❌ Ошибка" : "✅ Успех"}: ${JSON.stringify(
113
+ call.input
114
+ )}`
115
+ );
116
+ });
117
+
118
+ // Получить только успешные или только ошибочные вызовы
119
+ const successCalls = analyzer.getMcpToolSuccessfulCalls(
120
+ chatData,
121
+ "update_entry_fields"
122
+ );
123
+ const errorCalls = analyzer.getMcpToolErrorCalls(
124
+ chatData,
125
+ "update_entry_fields"
126
+ );
127
+ ```
128
+
65
129
  ## Статусы диалога
66
130
 
131
+ Библиотека автоматически определяет текущий статус чата при экспорте:
132
+
133
+ - **`DialogStatus.PENDING`** (`"pending"`) - Диалог еще не начат
134
+
135
+ - Массив `requests` пустой или отсутствует
136
+ - Еще не было сделано ни одного запроса к Copilot
137
+
67
138
  - **`DialogStatus.COMPLETED`** (`"completed"`) - Диалог завершен успешно
68
139
 
69
140
  - Есть поле `followups: []` (пустой массив)
@@ -78,6 +149,7 @@ console.log({
78
149
  - **`DialogStatus.IN_PROGRESS`** (`"in_progress"`) - Диалог в процессе
79
150
  - Отсутствует поле `followups`
80
151
  - `isCanceled: false`
152
+ - **Такой статус получается при экспорте во время диалога**
81
153
 
82
154
  ## Примеры использования
83
155
 
@@ -111,18 +183,26 @@ pnpm test:coverage
111
183
 
112
184
  ## Структура экспорта чата
113
185
 
114
- Библиотека работает с JSON файлами, экспортированными из GitHub Copilot Chat со следующей структурой:
186
+ Библиотека работает с JSON файлами, экспортированными из GitHub Copilot Chat через команду **`F1 > Export Chat`**.
187
+
188
+ ### Формат файла
189
+
190
+ Экспортированный файл содержит следующую структуру:
115
191
 
116
192
  ```json
117
193
  {
194
+ "requesterUsername": "your-username",
195
+ "responderUsername": "GitHub Copilot",
118
196
  "requests": [
119
197
  {
120
- "requestId": "...",
121
- "message": {...},
122
- "response": [...],
123
- "followups": [], // только в завершенных диалогах
124
- "result": {...}, // только в завершенных диалогах
125
- "isCanceled": false
198
+ "requestId": "request_abc123...",
199
+ "message": {
200
+ "text": "Ваш вопрос к Copilot"
201
+ },
202
+ "response": [...], // массив частей ответа
203
+ "followups": [], // только в завершенных диалогах (пустой массив)
204
+ "result": {...}, // метаданные результата
205
+ "isCanceled": false // true если диалог был отменен
126
206
  }
127
207
  ]
128
208
  }
@@ -136,7 +216,7 @@ interface CopilotChatData {
136
216
  [key: string]: any;
137
217
  }
138
218
 
139
- type DialogStatusType = "completed" | "canceled" | "in_progress";
219
+ type DialogStatusType = "pending" | "completed" | "canceled" | "in_progress";
140
220
 
141
221
  interface DialogStatusDetails {
142
222
  status: DialogStatusType;
package/dist/index.cjs ADDED
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CopilotChatAnalyzer: () => CopilotChatAnalyzer,
24
+ DialogStatus: () => DialogStatus,
25
+ default: () => index_default
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var DialogStatus = {
29
+ PENDING: "pending",
30
+ COMPLETED: "completed",
31
+ CANCELED: "canceled",
32
+ IN_PROGRESS: "in_progress"
33
+ };
34
+ var CopilotChatAnalyzer = class {
35
+ getRequestsCount(chatData) {
36
+ if (!chatData || !Array.isArray(chatData.requests)) {
37
+ return 0;
38
+ }
39
+ return chatData.requests.length;
40
+ }
41
+ hasRequests(chatData) {
42
+ return Array.isArray(chatData.requests) && chatData.requests.length > 0;
43
+ }
44
+ getLastRequest(chatData) {
45
+ if (!this.hasRequests(chatData)) {
46
+ return null;
47
+ }
48
+ return chatData.requests[chatData.requests.length - 1];
49
+ }
50
+ getDialogStatus(chatData) {
51
+ if (!this.hasRequests(chatData)) {
52
+ return DialogStatus.PENDING;
53
+ }
54
+ const lastRequest = this.getLastRequest(chatData);
55
+ if (!lastRequest) {
56
+ return DialogStatus.PENDING;
57
+ }
58
+ if (lastRequest.isCanceled === true) {
59
+ return DialogStatus.CANCELED;
60
+ }
61
+ if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
62
+ if (lastRequest.followups.length === 0) {
63
+ return DialogStatus.COMPLETED;
64
+ }
65
+ }
66
+ if (!("followups" in lastRequest)) {
67
+ return DialogStatus.IN_PROGRESS;
68
+ }
69
+ return DialogStatus.IN_PROGRESS;
70
+ }
71
+ getDialogStatusDetails(chatData) {
72
+ const status = this.getDialogStatus(chatData);
73
+ if (!this.hasRequests(chatData)) {
74
+ return {
75
+ status: DialogStatus.PENDING,
76
+ statusText: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0435\u0449\u0435 \u043D\u0435 \u043D\u0430\u0447\u0430\u0442",
77
+ hasResult: false,
78
+ hasFollowups: false,
79
+ isCanceled: false
80
+ };
81
+ }
82
+ const lastRequest = this.getLastRequest(chatData);
83
+ const statusTexts = {
84
+ [DialogStatus.PENDING]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0435\u0449\u0435 \u043D\u0435 \u043D\u0430\u0447\u0430\u0442",
85
+ [DialogStatus.COMPLETED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D \u0443\u0441\u043F\u0435\u0448\u043D\u043E",
86
+ [DialogStatus.CANCELED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0431\u044B\u043B \u043E\u0442\u043C\u0435\u043D\u0435\u043D",
87
+ [DialogStatus.IN_PROGRESS]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0432 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F"
88
+ };
89
+ return {
90
+ status,
91
+ statusText: statusTexts[status],
92
+ hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
93
+ hasFollowups: lastRequest && "followups" in lastRequest,
94
+ isCanceled: lastRequest && lastRequest.isCanceled === true,
95
+ lastRequestId: lastRequest?.requestId
96
+ };
97
+ }
98
+ extractMcpToolCalls(chatData) {
99
+ if (!this.hasRequests(chatData)) {
100
+ return [];
101
+ }
102
+ const toolCalls = [];
103
+ chatData.requests.forEach((request) => {
104
+ if (!request.response || !Array.isArray(request.response)) {
105
+ return;
106
+ }
107
+ request.response.forEach((responseItem) => {
108
+ if (responseItem.kind === "toolInvocationSerialized" && responseItem.source?.type === "mcp") {
109
+ const toolCall = {
110
+ toolId: responseItem.toolId || responseItem.toolName || "unknown",
111
+ toolName: responseItem.toolName || responseItem.toolId || "unknown",
112
+ requestId: request.requestId,
113
+ input: responseItem.resultDetails?.input || responseItem.toolSpecificData?.rawInput || null,
114
+ output: responseItem.resultDetails?.output || null,
115
+ isError: responseItem.resultDetails?.isError || false,
116
+ timestamp: request.timestamp,
117
+ source: responseItem.source
118
+ };
119
+ toolCalls.push(toolCall);
120
+ }
121
+ });
122
+ });
123
+ return toolCalls;
124
+ }
125
+ getMcpToolMonitoring(chatData, toolName) {
126
+ const allToolCalls = this.extractMcpToolCalls(chatData);
127
+ if (toolName) {
128
+ const toolCalls = allToolCalls.filter(
129
+ (call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
130
+ );
131
+ const successfulCalls = toolCalls.filter((call) => !call.isError).length;
132
+ const errorCalls = toolCalls.filter((call) => call.isError).length;
133
+ const successRate = toolCalls.length > 0 ? successfulCalls / toolCalls.length * 100 : 0;
134
+ return {
135
+ toolName,
136
+ totalCalls: toolCalls.length,
137
+ successfulCalls,
138
+ errorCalls,
139
+ successRate: Math.round(successRate * 100) / 100,
140
+ calls: toolCalls
141
+ };
142
+ } else {
143
+ const toolsMap = /* @__PURE__ */ new Map();
144
+ allToolCalls.forEach((call) => {
145
+ const key = call.toolName || call.toolId;
146
+ if (!toolsMap.has(key)) {
147
+ toolsMap.set(key, []);
148
+ }
149
+ toolsMap.get(key).push(call);
150
+ });
151
+ const tools = Array.from(toolsMap.entries()).map(
152
+ ([toolName2, calls]) => {
153
+ const successfulCalls = calls.filter((call) => !call.isError).length;
154
+ const errorCalls = calls.filter((call) => call.isError).length;
155
+ const successRate = calls.length > 0 ? successfulCalls / calls.length * 100 : 0;
156
+ return {
157
+ toolName: toolName2,
158
+ totalCalls: calls.length,
159
+ successfulCalls,
160
+ errorCalls,
161
+ successRate: Math.round(successRate * 100) / 100,
162
+ calls
163
+ };
164
+ }
165
+ );
166
+ const totalCalls = allToolCalls.length;
167
+ const totalSuccessful = allToolCalls.filter(
168
+ (call) => !call.isError
169
+ ).length;
170
+ const overallSuccessRate = totalCalls > 0 ? totalSuccessful / totalCalls * 100 : 0;
171
+ return {
172
+ totalTools: tools.length,
173
+ totalCalls,
174
+ overallSuccessRate: Math.round(overallSuccessRate * 100) / 100,
175
+ tools
176
+ };
177
+ }
178
+ }
179
+ getMcpToolSuccessfulCalls(chatData, toolName) {
180
+ const monitoring = this.getMcpToolMonitoring(
181
+ chatData,
182
+ toolName
183
+ );
184
+ return monitoring.calls.filter((call) => !call.isError);
185
+ }
186
+ getMcpToolErrorCalls(chatData, toolName) {
187
+ const monitoring = this.getMcpToolMonitoring(
188
+ chatData,
189
+ toolName
190
+ );
191
+ return monitoring.calls.filter((call) => call.isError);
192
+ }
193
+ /**
194
+ * Простой метод для получения всех вызовов конкретного MCP инструмента
195
+ * @param chatData - данные чата
196
+ * @param toolName - название инструмента (например, 'update_entry_fields')
197
+ * @returns массив всех вызовов инструмента
198
+ */
199
+ getMcpToolCalls(chatData, toolName) {
200
+ const allToolCalls = this.extractMcpToolCalls(chatData);
201
+ return allToolCalls.filter(
202
+ (call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
203
+ );
204
+ }
205
+ /**
206
+ * Получить список всех уникальных MCP инструментов, использованных в чате
207
+ * @param chatData - данные чата
208
+ * @returns массив названий инструментов
209
+ */
210
+ getMcpToolNames(chatData) {
211
+ const allToolCalls = this.extractMcpToolCalls(chatData);
212
+ const uniqueNames = /* @__PURE__ */ new Set();
213
+ allToolCalls.forEach((call) => {
214
+ uniqueNames.add(call.toolName || call.toolId);
215
+ });
216
+ return Array.from(uniqueNames);
217
+ }
218
+ };
219
+ var index_default = CopilotChatAnalyzer;
220
+ // Annotate the CommonJS export names for ESM import in node:
221
+ 0 && (module.exports = {
222
+ CopilotChatAnalyzer,
223
+ DialogStatus
224
+ });
package/dist/index.d.cts CHANGED
@@ -10,7 +10,36 @@ interface DialogStatusDetails {
10
10
  isCanceled: boolean;
11
11
  lastRequestId?: string;
12
12
  }
13
+ interface McpToolCall {
14
+ toolId: string;
15
+ toolName: string;
16
+ requestId: string;
17
+ input: any;
18
+ output: any;
19
+ isError: boolean;
20
+ timestamp?: number;
21
+ source?: {
22
+ type: string;
23
+ serverLabel: string;
24
+ label: string;
25
+ };
26
+ }
27
+ interface McpToolMonitoring {
28
+ toolName: string;
29
+ totalCalls: number;
30
+ successfulCalls: number;
31
+ errorCalls: number;
32
+ successRate: number;
33
+ calls: McpToolCall[];
34
+ }
35
+ interface McpMonitoringSummary {
36
+ totalTools: number;
37
+ totalCalls: number;
38
+ overallSuccessRate: number;
39
+ tools: McpToolMonitoring[];
40
+ }
13
41
  declare const DialogStatus: {
42
+ readonly PENDING: "pending";
14
43
  readonly COMPLETED: "completed";
15
44
  readonly CANCELED: "canceled";
16
45
  readonly IN_PROGRESS: "in_progress";
@@ -22,6 +51,23 @@ declare class CopilotChatAnalyzer {
22
51
  private getLastRequest;
23
52
  getDialogStatus(chatData: CopilotChatData): DialogStatusType;
24
53
  getDialogStatusDetails(chatData: CopilotChatData): DialogStatusDetails;
54
+ private extractMcpToolCalls;
55
+ getMcpToolMonitoring(chatData: CopilotChatData, toolName?: string): McpToolMonitoring | McpMonitoringSummary;
56
+ getMcpToolSuccessfulCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
57
+ getMcpToolErrorCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
58
+ /**
59
+ * Простой метод для получения всех вызовов конкретного MCP инструмента
60
+ * @param chatData - данные чата
61
+ * @param toolName - название инструмента (например, 'update_entry_fields')
62
+ * @returns массив всех вызовов инструмента
63
+ */
64
+ getMcpToolCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
65
+ /**
66
+ * Получить список всех уникальных MCP инструментов, использованных в чате
67
+ * @param chatData - данные чата
68
+ * @returns массив названий инструментов
69
+ */
70
+ getMcpToolNames(chatData: CopilotChatData): string[];
25
71
  }
26
72
 
27
- export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, CopilotChatAnalyzer as default };
73
+ export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, CopilotChatAnalyzer as default };
package/dist/index.d.ts CHANGED
@@ -10,7 +10,36 @@ interface DialogStatusDetails {
10
10
  isCanceled: boolean;
11
11
  lastRequestId?: string;
12
12
  }
13
+ interface McpToolCall {
14
+ toolId: string;
15
+ toolName: string;
16
+ requestId: string;
17
+ input: any;
18
+ output: any;
19
+ isError: boolean;
20
+ timestamp?: number;
21
+ source?: {
22
+ type: string;
23
+ serverLabel: string;
24
+ label: string;
25
+ };
26
+ }
27
+ interface McpToolMonitoring {
28
+ toolName: string;
29
+ totalCalls: number;
30
+ successfulCalls: number;
31
+ errorCalls: number;
32
+ successRate: number;
33
+ calls: McpToolCall[];
34
+ }
35
+ interface McpMonitoringSummary {
36
+ totalTools: number;
37
+ totalCalls: number;
38
+ overallSuccessRate: number;
39
+ tools: McpToolMonitoring[];
40
+ }
13
41
  declare const DialogStatus: {
42
+ readonly PENDING: "pending";
14
43
  readonly COMPLETED: "completed";
15
44
  readonly CANCELED: "canceled";
16
45
  readonly IN_PROGRESS: "in_progress";
@@ -22,6 +51,23 @@ declare class CopilotChatAnalyzer {
22
51
  private getLastRequest;
23
52
  getDialogStatus(chatData: CopilotChatData): DialogStatusType;
24
53
  getDialogStatusDetails(chatData: CopilotChatData): DialogStatusDetails;
54
+ private extractMcpToolCalls;
55
+ getMcpToolMonitoring(chatData: CopilotChatData, toolName?: string): McpToolMonitoring | McpMonitoringSummary;
56
+ getMcpToolSuccessfulCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
57
+ getMcpToolErrorCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
58
+ /**
59
+ * Простой метод для получения всех вызовов конкретного MCP инструмента
60
+ * @param chatData - данные чата
61
+ * @param toolName - название инструмента (например, 'update_entry_fields')
62
+ * @returns массив всех вызовов инструмента
63
+ */
64
+ getMcpToolCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
65
+ /**
66
+ * Получить список всех уникальных MCP инструментов, использованных в чате
67
+ * @param chatData - данные чата
68
+ * @returns массив названий инструментов
69
+ */
70
+ getMcpToolNames(chatData: CopilotChatData): string[];
25
71
  }
26
72
 
27
- export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, CopilotChatAnalyzer as default };
73
+ export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, CopilotChatAnalyzer as default };
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  var DialogStatus = {
3
+ PENDING: "pending",
3
4
  COMPLETED: "completed",
4
5
  CANCELED: "canceled",
5
6
  IN_PROGRESS: "in_progress"
@@ -22,11 +23,11 @@ var CopilotChatAnalyzer = class {
22
23
  }
23
24
  getDialogStatus(chatData) {
24
25
  if (!this.hasRequests(chatData)) {
25
- return DialogStatus.IN_PROGRESS;
26
+ return DialogStatus.PENDING;
26
27
  }
27
28
  const lastRequest = this.getLastRequest(chatData);
28
29
  if (!lastRequest) {
29
- return DialogStatus.IN_PROGRESS;
30
+ return DialogStatus.PENDING;
30
31
  }
31
32
  if (lastRequest.isCanceled === true) {
32
33
  return DialogStatus.CANCELED;
@@ -45,8 +46,8 @@ var CopilotChatAnalyzer = class {
45
46
  const status = this.getDialogStatus(chatData);
46
47
  if (!this.hasRequests(chatData)) {
47
48
  return {
48
- status: DialogStatus.IN_PROGRESS,
49
- statusText: "\u041D\u0435\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432",
49
+ status: DialogStatus.PENDING,
50
+ statusText: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0435\u0449\u0435 \u043D\u0435 \u043D\u0430\u0447\u0430\u0442",
50
51
  hasResult: false,
51
52
  hasFollowups: false,
52
53
  isCanceled: false
@@ -54,6 +55,7 @@ var CopilotChatAnalyzer = class {
54
55
  }
55
56
  const lastRequest = this.getLastRequest(chatData);
56
57
  const statusTexts = {
58
+ [DialogStatus.PENDING]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0435\u0449\u0435 \u043D\u0435 \u043D\u0430\u0447\u0430\u0442",
57
59
  [DialogStatus.COMPLETED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D \u0443\u0441\u043F\u0435\u0448\u043D\u043E",
58
60
  [DialogStatus.CANCELED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0431\u044B\u043B \u043E\u0442\u043C\u0435\u043D\u0435\u043D",
59
61
  [DialogStatus.IN_PROGRESS]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0432 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F"
@@ -67,6 +69,126 @@ var CopilotChatAnalyzer = class {
67
69
  lastRequestId: lastRequest?.requestId
68
70
  };
69
71
  }
72
+ extractMcpToolCalls(chatData) {
73
+ if (!this.hasRequests(chatData)) {
74
+ return [];
75
+ }
76
+ const toolCalls = [];
77
+ chatData.requests.forEach((request) => {
78
+ if (!request.response || !Array.isArray(request.response)) {
79
+ return;
80
+ }
81
+ request.response.forEach((responseItem) => {
82
+ if (responseItem.kind === "toolInvocationSerialized" && responseItem.source?.type === "mcp") {
83
+ const toolCall = {
84
+ toolId: responseItem.toolId || responseItem.toolName || "unknown",
85
+ toolName: responseItem.toolName || responseItem.toolId || "unknown",
86
+ requestId: request.requestId,
87
+ input: responseItem.resultDetails?.input || responseItem.toolSpecificData?.rawInput || null,
88
+ output: responseItem.resultDetails?.output || null,
89
+ isError: responseItem.resultDetails?.isError || false,
90
+ timestamp: request.timestamp,
91
+ source: responseItem.source
92
+ };
93
+ toolCalls.push(toolCall);
94
+ }
95
+ });
96
+ });
97
+ return toolCalls;
98
+ }
99
+ getMcpToolMonitoring(chatData, toolName) {
100
+ const allToolCalls = this.extractMcpToolCalls(chatData);
101
+ if (toolName) {
102
+ const toolCalls = allToolCalls.filter(
103
+ (call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
104
+ );
105
+ const successfulCalls = toolCalls.filter((call) => !call.isError).length;
106
+ const errorCalls = toolCalls.filter((call) => call.isError).length;
107
+ const successRate = toolCalls.length > 0 ? successfulCalls / toolCalls.length * 100 : 0;
108
+ return {
109
+ toolName,
110
+ totalCalls: toolCalls.length,
111
+ successfulCalls,
112
+ errorCalls,
113
+ successRate: Math.round(successRate * 100) / 100,
114
+ calls: toolCalls
115
+ };
116
+ } else {
117
+ const toolsMap = /* @__PURE__ */ new Map();
118
+ allToolCalls.forEach((call) => {
119
+ const key = call.toolName || call.toolId;
120
+ if (!toolsMap.has(key)) {
121
+ toolsMap.set(key, []);
122
+ }
123
+ toolsMap.get(key).push(call);
124
+ });
125
+ const tools = Array.from(toolsMap.entries()).map(
126
+ ([toolName2, calls]) => {
127
+ const successfulCalls = calls.filter((call) => !call.isError).length;
128
+ const errorCalls = calls.filter((call) => call.isError).length;
129
+ const successRate = calls.length > 0 ? successfulCalls / calls.length * 100 : 0;
130
+ return {
131
+ toolName: toolName2,
132
+ totalCalls: calls.length,
133
+ successfulCalls,
134
+ errorCalls,
135
+ successRate: Math.round(successRate * 100) / 100,
136
+ calls
137
+ };
138
+ }
139
+ );
140
+ const totalCalls = allToolCalls.length;
141
+ const totalSuccessful = allToolCalls.filter(
142
+ (call) => !call.isError
143
+ ).length;
144
+ const overallSuccessRate = totalCalls > 0 ? totalSuccessful / totalCalls * 100 : 0;
145
+ return {
146
+ totalTools: tools.length,
147
+ totalCalls,
148
+ overallSuccessRate: Math.round(overallSuccessRate * 100) / 100,
149
+ tools
150
+ };
151
+ }
152
+ }
153
+ getMcpToolSuccessfulCalls(chatData, toolName) {
154
+ const monitoring = this.getMcpToolMonitoring(
155
+ chatData,
156
+ toolName
157
+ );
158
+ return monitoring.calls.filter((call) => !call.isError);
159
+ }
160
+ getMcpToolErrorCalls(chatData, toolName) {
161
+ const monitoring = this.getMcpToolMonitoring(
162
+ chatData,
163
+ toolName
164
+ );
165
+ return monitoring.calls.filter((call) => call.isError);
166
+ }
167
+ /**
168
+ * Простой метод для получения всех вызовов конкретного MCP инструмента
169
+ * @param chatData - данные чата
170
+ * @param toolName - название инструмента (например, 'update_entry_fields')
171
+ * @returns массив всех вызовов инструмента
172
+ */
173
+ getMcpToolCalls(chatData, toolName) {
174
+ const allToolCalls = this.extractMcpToolCalls(chatData);
175
+ return allToolCalls.filter(
176
+ (call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
177
+ );
178
+ }
179
+ /**
180
+ * Получить список всех уникальных MCP инструментов, использованных в чате
181
+ * @param chatData - данные чата
182
+ * @returns массив названий инструментов
183
+ */
184
+ getMcpToolNames(chatData) {
185
+ const allToolCalls = this.extractMcpToolCalls(chatData);
186
+ const uniqueNames = /* @__PURE__ */ new Set();
187
+ allToolCalls.forEach((call) => {
188
+ uniqueNames.add(call.toolName || call.toolId);
189
+ });
190
+ return Array.from(uniqueNames);
191
+ }
70
192
  };
71
193
  var index_default = CopilotChatAnalyzer;
72
194
  export {
package/package.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "name": "copilot-chat-analyzer",
3
3
  "private": false,
4
- "version": "0.0.1",
5
- "description": "Simple library for analyzing exported GitHub Copilot chats",
4
+ "version": "0.0.3",
5
+ "description": "TypeScript library for analyzing and monitoring GitHub Copilot chat exports with status detection, request counting, and MCP tool tracking",
6
6
  "keywords": [
7
7
  "copilot",
8
8
  "chat",
9
9
  "analyzer",
10
10
  "github",
11
- "typescript"
11
+ "typescript",
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "monitoring",
15
+ "status-detection",
16
+ "dialog-analysis",
17
+ "ai-tools",
18
+ "export-analysis"
12
19
  ],
13
20
  "author": "dealenx",
14
21
  "license": "MIT",
@@ -20,20 +27,52 @@
20
27
  "url": "https://github.com/dealenx/copilot-chat-analyzer/issues"
21
28
  },
22
29
  "homepage": "https://github.com/dealenx/copilot-chat-analyzer#readme",
30
+ "funding": {
31
+ "type": "github",
32
+ "url": "https://github.com/sponsors/dealenx"
33
+ },
34
+ "engines": {
35
+ "node": ">=16.0.0"
36
+ },
37
+ "ai-usage": {
38
+ "purpose": "Analyze GitHub Copilot chat exports for dialog status, request counting, and MCP tool monitoring",
39
+ "quick-start": "import CopilotChatAnalyzer, { DialogStatus } from 'copilot-chat-analyzer'; const analyzer = new CopilotChatAnalyzer(); const status = analyzer.getDialogStatus(chatData);",
40
+ "main-features": [
41
+ "Dialog status detection (pending, in_progress, completed, canceled)",
42
+ "Request counting and analysis",
43
+ "MCP (Model Context Protocol) tool monitoring",
44
+ "Chat export validation and parsing"
45
+ ],
46
+ "common-methods": {
47
+ "getDialogStatus": "Returns dialog status: 'pending' | 'completed' | 'canceled' | 'in_progress'",
48
+ "getRequestsCount": "Returns number of requests in chat",
49
+ "getDialogStatusDetails": "Returns detailed status information with flags",
50
+ "getMcpToolMonitoring": "Returns MCP tool usage statistics and monitoring data"
51
+ },
52
+ "status-meanings": {
53
+ "pending": "Chat created but no requests made yet (requests: [])",
54
+ "in_progress": "Chat has requests but not completed",
55
+ "completed": "Chat finished successfully (has followups: [])",
56
+ "canceled": "Chat was canceled (isCanceled: true)"
57
+ }
58
+ },
23
59
  "type": "module",
24
- "main": "dist/index.js",
60
+ "main": "dist/index.cjs",
25
61
  "module": "dist/index.mjs",
26
62
  "types": "dist/index.d.ts",
27
63
  "exports": {
28
64
  ".": {
29
65
  "types": "./dist/index.d.ts",
30
66
  "import": "./dist/index.mjs",
31
- "require": "./dist/index.js",
67
+ "require": "./dist/index.cjs",
32
68
  "default": "./dist/index.mjs"
33
69
  }
34
70
  },
35
71
  "files": [
36
- "dist"
72
+ "dist",
73
+ "README.md",
74
+ "AI_USAGE.md",
75
+ "LICENSE"
37
76
  ],
38
77
  "devDependencies": {
39
78
  "@types/jest": "^30.0.0",
package/dist/index.js DELETED
@@ -1,102 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- CopilotChatAnalyzer: () => CopilotChatAnalyzer,
24
- DialogStatus: () => DialogStatus,
25
- default: () => index_default
26
- });
27
- module.exports = __toCommonJS(index_exports);
28
- var DialogStatus = {
29
- COMPLETED: "completed",
30
- CANCELED: "canceled",
31
- IN_PROGRESS: "in_progress"
32
- };
33
- var CopilotChatAnalyzer = class {
34
- getRequestsCount(chatData) {
35
- if (!chatData || !Array.isArray(chatData.requests)) {
36
- return 0;
37
- }
38
- return chatData.requests.length;
39
- }
40
- hasRequests(chatData) {
41
- return Array.isArray(chatData.requests) && chatData.requests.length > 0;
42
- }
43
- getLastRequest(chatData) {
44
- if (!this.hasRequests(chatData)) {
45
- return null;
46
- }
47
- return chatData.requests[chatData.requests.length - 1];
48
- }
49
- getDialogStatus(chatData) {
50
- if (!this.hasRequests(chatData)) {
51
- return DialogStatus.IN_PROGRESS;
52
- }
53
- const lastRequest = this.getLastRequest(chatData);
54
- if (!lastRequest) {
55
- return DialogStatus.IN_PROGRESS;
56
- }
57
- if (lastRequest.isCanceled === true) {
58
- return DialogStatus.CANCELED;
59
- }
60
- if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
61
- if (lastRequest.followups.length === 0) {
62
- return DialogStatus.COMPLETED;
63
- }
64
- }
65
- if (!("followups" in lastRequest)) {
66
- return DialogStatus.IN_PROGRESS;
67
- }
68
- return DialogStatus.IN_PROGRESS;
69
- }
70
- getDialogStatusDetails(chatData) {
71
- const status = this.getDialogStatus(chatData);
72
- if (!this.hasRequests(chatData)) {
73
- return {
74
- status: DialogStatus.IN_PROGRESS,
75
- statusText: "\u041D\u0435\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432",
76
- hasResult: false,
77
- hasFollowups: false,
78
- isCanceled: false
79
- };
80
- }
81
- const lastRequest = this.getLastRequest(chatData);
82
- const statusTexts = {
83
- [DialogStatus.COMPLETED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D \u0443\u0441\u043F\u0435\u0448\u043D\u043E",
84
- [DialogStatus.CANCELED]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0431\u044B\u043B \u043E\u0442\u043C\u0435\u043D\u0435\u043D",
85
- [DialogStatus.IN_PROGRESS]: "\u0414\u0438\u0430\u043B\u043E\u0433 \u0432 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F"
86
- };
87
- return {
88
- status,
89
- statusText: statusTexts[status],
90
- hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
91
- hasFollowups: lastRequest && "followups" in lastRequest,
92
- isCanceled: lastRequest && lastRequest.isCanceled === true,
93
- lastRequestId: lastRequest?.requestId
94
- };
95
- }
96
- };
97
- var index_default = CopilotChatAnalyzer;
98
- // Annotate the CommonJS export names for ESM import in node:
99
- 0 && (module.exports = {
100
- CopilotChatAnalyzer,
101
- DialogStatus
102
- });