claude-threads 0.15.0 → 0.16.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +5 -5
  3. package/dist/index.js +20410 -387
  4. package/dist/mcp/permission-server.js +34038 -139
  5. package/package.json +14 -18
  6. package/dist/changelog.d.ts +0 -20
  7. package/dist/changelog.js +0 -134
  8. package/dist/claude/cli.d.ts +0 -50
  9. package/dist/claude/cli.js +0 -181
  10. package/dist/config/migration.d.ts +0 -45
  11. package/dist/config/migration.js +0 -35
  12. package/dist/config.d.ts +0 -21
  13. package/dist/config.js +0 -7
  14. package/dist/git/worktree.d.ts +0 -46
  15. package/dist/git/worktree.js +0 -228
  16. package/dist/index.d.ts +0 -2
  17. package/dist/logo.d.ts +0 -14
  18. package/dist/logo.js +0 -41
  19. package/dist/mattermost/api.d.ts +0 -85
  20. package/dist/mattermost/api.js +0 -124
  21. package/dist/mattermost/api.test.d.ts +0 -1
  22. package/dist/mattermost/api.test.js +0 -319
  23. package/dist/mcp/permission-server.d.ts +0 -2
  24. package/dist/onboarding.d.ts +0 -1
  25. package/dist/onboarding.js +0 -318
  26. package/dist/persistence/session-store.d.ts +0 -71
  27. package/dist/persistence/session-store.js +0 -152
  28. package/dist/platform/client.d.ts +0 -140
  29. package/dist/platform/client.js +0 -1
  30. package/dist/platform/formatter.d.ts +0 -74
  31. package/dist/platform/formatter.js +0 -1
  32. package/dist/platform/index.d.ts +0 -11
  33. package/dist/platform/index.js +0 -8
  34. package/dist/platform/mattermost/client.d.ts +0 -70
  35. package/dist/platform/mattermost/client.js +0 -404
  36. package/dist/platform/mattermost/formatter.d.ts +0 -20
  37. package/dist/platform/mattermost/formatter.js +0 -46
  38. package/dist/platform/mattermost/permission-api.d.ts +0 -10
  39. package/dist/platform/mattermost/permission-api.js +0 -139
  40. package/dist/platform/mattermost/types.d.ts +0 -71
  41. package/dist/platform/mattermost/types.js +0 -1
  42. package/dist/platform/permission-api-factory.d.ts +0 -11
  43. package/dist/platform/permission-api-factory.js +0 -21
  44. package/dist/platform/permission-api.d.ts +0 -67
  45. package/dist/platform/permission-api.js +0 -8
  46. package/dist/platform/types.d.ts +0 -70
  47. package/dist/platform/types.js +0 -7
  48. package/dist/session/commands.d.ts +0 -52
  49. package/dist/session/commands.js +0 -323
  50. package/dist/session/events.d.ts +0 -25
  51. package/dist/session/events.js +0 -368
  52. package/dist/session/index.d.ts +0 -7
  53. package/dist/session/index.js +0 -6
  54. package/dist/session/lifecycle.d.ts +0 -70
  55. package/dist/session/lifecycle.js +0 -456
  56. package/dist/session/manager.d.ts +0 -96
  57. package/dist/session/manager.js +0 -537
  58. package/dist/session/reactions.d.ts +0 -25
  59. package/dist/session/reactions.js +0 -151
  60. package/dist/session/streaming.d.ts +0 -47
  61. package/dist/session/streaming.js +0 -152
  62. package/dist/session/types.d.ts +0 -78
  63. package/dist/session/types.js +0 -9
  64. package/dist/session/worktree.d.ts +0 -56
  65. package/dist/session/worktree.js +0 -339
  66. package/dist/update-notifier.d.ts +0 -3
  67. package/dist/update-notifier.js +0 -41
  68. package/dist/utils/emoji.d.ts +0 -43
  69. package/dist/utils/emoji.js +0 -65
  70. package/dist/utils/emoji.test.d.ts +0 -1
  71. package/dist/utils/emoji.test.js +0 -131
  72. package/dist/utils/logger.d.ts +0 -34
  73. package/dist/utils/logger.js +0 -42
  74. package/dist/utils/logger.test.d.ts +0 -1
  75. package/dist/utils/logger.test.js +0 -121
  76. package/dist/utils/tool-formatter.d.ts +0 -53
  77. package/dist/utils/tool-formatter.js +0 -252
  78. package/dist/utils/tool-formatter.test.d.ts +0 -1
  79. package/dist/utils/tool-formatter.test.js +0 -372
@@ -1,42 +0,0 @@
1
- /**
2
- * Simple debug logging utility
3
- *
4
- * Provides consistent logging across the codebase with:
5
- * - Configurable prefix for different components
6
- * - DEBUG environment variable check
7
- * - stdout vs stderr routing option
8
- */
9
- /**
10
- * Create a logger with a specific prefix
11
- *
12
- * @param prefix - Prefix to add to all messages (e.g., '[MCP]', '[ws]')
13
- * @param useStderr - If true, use stderr for all output (default: false)
14
- * @returns Logger object with debug, info, and error methods
15
- */
16
- export function createLogger(prefix, useStderr = false) {
17
- const isDebug = () => process.env.DEBUG === '1';
18
- const log = useStderr ? console.error : console.log;
19
- return {
20
- debug: (msg) => {
21
- if (isDebug()) {
22
- log(`${prefix} ${msg}`);
23
- }
24
- },
25
- info: (msg) => {
26
- log(`${prefix} ${msg}`);
27
- },
28
- error: (msg) => {
29
- console.error(`${prefix} ${msg}`);
30
- },
31
- };
32
- }
33
- /**
34
- * Pre-configured logger for MCP permission server
35
- * Uses stderr (required for MCP stdio communication)
36
- */
37
- export const mcpLogger = createLogger('[MCP]', true);
38
- /**
39
- * Pre-configured logger for WebSocket client
40
- * Uses stdout with indentation for visual hierarchy
41
- */
42
- export const wsLogger = createLogger(' [ws]', false);
@@ -1 +0,0 @@
1
- export {};
@@ -1,121 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { createLogger, mcpLogger, wsLogger } from './logger.js';
3
- describe('createLogger', () => {
4
- let consoleLogSpy;
5
- let consoleErrorSpy;
6
- const originalEnv = process.env.DEBUG;
7
- beforeEach(() => {
8
- consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
9
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
10
- delete process.env.DEBUG;
11
- });
12
- afterEach(() => {
13
- consoleLogSpy.mockRestore();
14
- consoleErrorSpy.mockRestore();
15
- if (originalEnv !== undefined) {
16
- process.env.DEBUG = originalEnv;
17
- }
18
- else {
19
- delete process.env.DEBUG;
20
- }
21
- });
22
- describe('debug', () => {
23
- it('does not log when DEBUG is not set', () => {
24
- const logger = createLogger('[test]');
25
- logger.debug('test message');
26
- expect(consoleLogSpy).not.toHaveBeenCalled();
27
- expect(consoleErrorSpy).not.toHaveBeenCalled();
28
- });
29
- it('logs to stdout when DEBUG=1 and useStderr=false', () => {
30
- process.env.DEBUG = '1';
31
- const logger = createLogger('[test]');
32
- logger.debug('test message');
33
- expect(consoleLogSpy).toHaveBeenCalledWith('[test] test message');
34
- expect(consoleErrorSpy).not.toHaveBeenCalled();
35
- });
36
- it('logs to stderr when DEBUG=1 and useStderr=true', () => {
37
- process.env.DEBUG = '1';
38
- const logger = createLogger('[test]', true);
39
- logger.debug('test message');
40
- expect(consoleErrorSpy).toHaveBeenCalledWith('[test] test message');
41
- expect(consoleLogSpy).not.toHaveBeenCalled();
42
- });
43
- it('does not log when DEBUG is set to something other than 1', () => {
44
- process.env.DEBUG = 'true';
45
- const logger = createLogger('[test]');
46
- logger.debug('test message');
47
- expect(consoleLogSpy).not.toHaveBeenCalled();
48
- });
49
- });
50
- describe('info', () => {
51
- it('always logs to stdout when useStderr=false', () => {
52
- const logger = createLogger('[test]');
53
- logger.info('info message');
54
- expect(consoleLogSpy).toHaveBeenCalledWith('[test] info message');
55
- });
56
- it('logs to stderr when useStderr=true', () => {
57
- const logger = createLogger('[test]', true);
58
- logger.info('info message');
59
- expect(consoleErrorSpy).toHaveBeenCalledWith('[test] info message');
60
- });
61
- it('logs even when DEBUG is not set', () => {
62
- const logger = createLogger('[test]');
63
- logger.info('info message');
64
- expect(consoleLogSpy).toHaveBeenCalled();
65
- });
66
- });
67
- describe('error', () => {
68
- it('always logs to stderr', () => {
69
- const logger = createLogger('[test]');
70
- logger.error('error message');
71
- expect(consoleErrorSpy).toHaveBeenCalledWith('[test] error message');
72
- });
73
- it('logs to stderr even when useStderr=false', () => {
74
- const logger = createLogger('[test]', false);
75
- logger.error('error message');
76
- expect(consoleErrorSpy).toHaveBeenCalledWith('[test] error message');
77
- });
78
- it('logs even when DEBUG is not set', () => {
79
- const logger = createLogger('[test]');
80
- logger.error('error message');
81
- expect(consoleErrorSpy).toHaveBeenCalled();
82
- });
83
- });
84
- describe('prefix formatting', () => {
85
- it('includes prefix in debug messages', () => {
86
- process.env.DEBUG = '1';
87
- const logger = createLogger('[MyPrefix]');
88
- logger.debug('my message');
89
- expect(consoleLogSpy).toHaveBeenCalledWith('[MyPrefix] my message');
90
- });
91
- it('includes prefix in info messages', () => {
92
- const logger = createLogger('[MyPrefix]');
93
- logger.info('my message');
94
- expect(consoleLogSpy).toHaveBeenCalledWith('[MyPrefix] my message');
95
- });
96
- it('includes prefix in error messages', () => {
97
- const logger = createLogger('[MyPrefix]');
98
- logger.error('my message');
99
- expect(consoleErrorSpy).toHaveBeenCalledWith('[MyPrefix] my message');
100
- });
101
- });
102
- });
103
- describe('pre-configured loggers', () => {
104
- // Note: mcpLogger and wsLogger are module-level singletons created at import time.
105
- // Since they capture console.log/error at creation, we test their behavior by
106
- // verifying they have the expected interface and configuration.
107
- describe('mcpLogger', () => {
108
- it('has debug, info, and error methods', () => {
109
- expect(typeof mcpLogger.debug).toBe('function');
110
- expect(typeof mcpLogger.info).toBe('function');
111
- expect(typeof mcpLogger.error).toBe('function');
112
- });
113
- });
114
- describe('wsLogger', () => {
115
- it('has debug, info, and error methods', () => {
116
- expect(typeof wsLogger.debug).toBe('function');
117
- expect(typeof wsLogger.info).toBe('function');
118
- expect(typeof wsLogger.error).toBe('function');
119
- });
120
- });
121
- });
@@ -1,53 +0,0 @@
1
- /**
2
- * Tool formatting utilities for displaying Claude tool calls in chat platforms
3
- *
4
- * This module provides shared formatting logic used by both:
5
- * - src/session/events.ts (main bot)
6
- * - src/mcp/permission-server.ts (MCP permission handler)
7
- *
8
- * Uses PlatformFormatter abstraction to support different markdown dialects
9
- * (e.g., Mattermost standard markdown vs Slack mrkdwn).
10
- */
11
- import type { PlatformFormatter } from '../platform/formatter.js';
12
- export interface ToolInput {
13
- [key: string]: unknown;
14
- }
15
- export interface FormatOptions {
16
- /** Include detailed previews (diffs, file content). Default: false */
17
- detailed?: boolean;
18
- /** Max command length for Bash. Default: 50 */
19
- maxCommandLength?: number;
20
- /** Max path display length. Default: 60 */
21
- maxPathLength?: number;
22
- /** Max lines to show in previews. Default: 20 for diff, 6 for content */
23
- maxPreviewLines?: number;
24
- }
25
- /**
26
- * Shorten a file path for display by replacing home directory with ~
27
- */
28
- export declare function shortenPath(path: string, homeDir?: string): string;
29
- /**
30
- * Check if a tool name is an MCP tool and extract server/tool parts
31
- */
32
- export declare function parseMcpToolName(toolName: string): {
33
- server: string;
34
- tool: string;
35
- } | null;
36
- /**
37
- * Format a tool use for display in chat platforms
38
- *
39
- * @param toolName - The name of the tool being called
40
- * @param input - The tool input parameters
41
- * @param options - Formatting options
42
- * @returns Formatted string or null if the tool should not be displayed
43
- */
44
- export declare function formatToolUse(toolName: string, input: ToolInput, formatter: PlatformFormatter, options?: FormatOptions): string | null;
45
- /**
46
- * Format tool info for permission prompts (simpler format)
47
- *
48
- * @param toolName - The name of the tool
49
- * @param input - The tool input parameters
50
- * @param formatter - Platform-specific markdown formatter
51
- * @returns Formatted string for permission prompts
52
- */
53
- export declare function formatToolForPermission(toolName: string, input: ToolInput, formatter: PlatformFormatter): string;
@@ -1,252 +0,0 @@
1
- /**
2
- * Tool formatting utilities for displaying Claude tool calls in chat platforms
3
- *
4
- * This module provides shared formatting logic used by both:
5
- * - src/session/events.ts (main bot)
6
- * - src/mcp/permission-server.ts (MCP permission handler)
7
- *
8
- * Uses PlatformFormatter abstraction to support different markdown dialects
9
- * (e.g., Mattermost standard markdown vs Slack mrkdwn).
10
- */
11
- import * as Diff from 'diff';
12
- const DEFAULT_OPTIONS = {
13
- detailed: false,
14
- maxCommandLength: 50,
15
- maxPathLength: 60,
16
- maxPreviewLines: 20,
17
- };
18
- /**
19
- * Shorten a file path for display by replacing home directory with ~
20
- */
21
- export function shortenPath(path, homeDir) {
22
- if (!path)
23
- return '';
24
- const home = homeDir ?? process.env.HOME ?? '';
25
- if (home && path.startsWith(home)) {
26
- return '~' + path.slice(home.length);
27
- }
28
- return path;
29
- }
30
- /**
31
- * Check if a tool name is an MCP tool and extract server/tool parts
32
- */
33
- export function parseMcpToolName(toolName) {
34
- if (!toolName.startsWith('mcp__'))
35
- return null;
36
- const parts = toolName.split('__');
37
- if (parts.length < 3)
38
- return null;
39
- return {
40
- server: parts[1],
41
- tool: parts.slice(2).join('__'),
42
- };
43
- }
44
- /**
45
- * Format a tool use for display in chat platforms
46
- *
47
- * @param toolName - The name of the tool being called
48
- * @param input - The tool input parameters
49
- * @param options - Formatting options
50
- * @returns Formatted string or null if the tool should not be displayed
51
- */
52
- export function formatToolUse(toolName, input, formatter, options = {}) {
53
- const opts = { ...DEFAULT_OPTIONS, ...options };
54
- const short = (p) => shortenPath(p);
55
- switch (toolName) {
56
- case 'Read':
57
- return `📄 ${formatter.formatBold('Read')} ${formatter.formatCode(short(input.file_path))}`;
58
- case 'Edit': {
59
- const filePath = short(input.file_path);
60
- const oldStr = input.old_string || '';
61
- const newStr = input.new_string || '';
62
- // Show diff if detailed mode and we have old/new strings
63
- if (opts.detailed && (oldStr || newStr)) {
64
- const changes = Diff.diffLines(oldStr, newStr);
65
- const maxLines = opts.maxPreviewLines;
66
- let lineCount = 0;
67
- const diffLines = [];
68
- for (const change of changes) {
69
- const lines = change.value.replace(/\n$/, '').split('\n');
70
- for (const line of lines) {
71
- if (lineCount >= maxLines)
72
- break;
73
- if (change.added) {
74
- diffLines.push(`+ ${line}`);
75
- lineCount++;
76
- }
77
- else if (change.removed) {
78
- diffLines.push(`- ${line}`);
79
- lineCount++;
80
- }
81
- else {
82
- diffLines.push(` ${line}`);
83
- lineCount++;
84
- }
85
- }
86
- if (lineCount >= maxLines)
87
- break;
88
- }
89
- const totalLines = changes.reduce((sum, c) => sum + c.value.split('\n').length - 1, 0);
90
- let diff = `✏️ ${formatter.formatBold('Edit')} ${formatter.formatCode(filePath)}\n${formatter.formatCodeBlock(diffLines.join('\n'), 'diff')}`;
91
- if (totalLines > maxLines) {
92
- diff = `✏️ ${formatter.formatBold('Edit')} ${formatter.formatCode(filePath)}\n`;
93
- diff += formatter.formatCodeBlock(diffLines.join('\n') + `\n... (+${totalLines - maxLines} more lines)`, 'diff');
94
- }
95
- return diff;
96
- }
97
- return `✏️ ${formatter.formatBold('Edit')} ${formatter.formatCode(filePath)}`;
98
- }
99
- case 'Write': {
100
- const filePath = short(input.file_path);
101
- const content = input.content || '';
102
- const lines = content.split('\n');
103
- const lineCount = lines.length;
104
- // Show preview if detailed mode
105
- if (opts.detailed && content && lineCount > 0) {
106
- const maxLines = 6;
107
- const previewLines = lines.slice(0, maxLines);
108
- let preview = `📝 ${formatter.formatBold('Write')} ${formatter.formatCode(filePath)} ${formatter.formatItalic(`(${lineCount} lines)`)}\n`;
109
- if (lineCount > maxLines) {
110
- preview += formatter.formatCodeBlock(previewLines.join('\n') + `\n... (${lineCount - maxLines} more lines)`);
111
- }
112
- else {
113
- preview += formatter.formatCodeBlock(previewLines.join('\n'));
114
- }
115
- return preview;
116
- }
117
- return `📝 ${formatter.formatBold('Write')} ${formatter.formatCode(filePath)}`;
118
- }
119
- case 'Bash': {
120
- const cmd = (input.command || '').substring(0, opts.maxCommandLength);
121
- const truncated = cmd.length >= opts.maxCommandLength;
122
- return `💻 ${formatter.formatBold('Bash')} ${formatter.formatCode(cmd + (truncated ? '...' : ''))}`;
123
- }
124
- case 'Glob':
125
- return `🔍 ${formatter.formatBold('Glob')} ${formatter.formatCode(input.pattern)}`;
126
- case 'Grep':
127
- return `🔎 ${formatter.formatBold('Grep')} ${formatter.formatCode(input.pattern)}`;
128
- case 'Task':
129
- return null; // Handled specially with subagent display
130
- case 'EnterPlanMode':
131
- return `📋 ${formatter.formatBold('Planning...')}`;
132
- case 'ExitPlanMode':
133
- return null; // Handled specially with approval buttons
134
- case 'AskUserQuestion':
135
- return null; // Don't show, the question text follows
136
- case 'TodoWrite':
137
- return null; // Handled specially with task list display
138
- case 'WebFetch': {
139
- const url = (input.url || '').substring(0, 40);
140
- return `🌐 ${formatter.formatBold('Fetching')} ${formatter.formatCode(url)}`;
141
- }
142
- case 'WebSearch':
143
- return `🔍 ${formatter.formatBold('Searching')} ${formatter.formatCode(input.query)}`;
144
- default: {
145
- // Handle MCP tools: mcp__server__tool
146
- const mcpParts = parseMcpToolName(toolName);
147
- if (mcpParts) {
148
- // Special formatting for Claude in Chrome tools
149
- if (mcpParts.server === 'claude-in-chrome') {
150
- return formatChromeToolUse(mcpParts.tool, input, formatter);
151
- }
152
- return `🔌 ${formatter.formatBold(mcpParts.tool)} ${formatter.formatItalic(`(${mcpParts.server})`)}`;
153
- }
154
- return `● ${formatter.formatBold(toolName)}`;
155
- }
156
- }
157
- }
158
- /**
159
- * Format tool info for permission prompts (simpler format)
160
- *
161
- * @param toolName - The name of the tool
162
- * @param input - The tool input parameters
163
- * @param formatter - Platform-specific markdown formatter
164
- * @returns Formatted string for permission prompts
165
- */
166
- export function formatToolForPermission(toolName, input, formatter) {
167
- const short = (p) => shortenPath(p);
168
- switch (toolName) {
169
- case 'Read':
170
- return `📄 ${formatter.formatBold('Read')} ${formatter.formatCode(short(input.file_path))}`;
171
- case 'Write':
172
- return `📝 ${formatter.formatBold('Write')} ${formatter.formatCode(short(input.file_path))}`;
173
- case 'Edit':
174
- return `✏️ ${formatter.formatBold('Edit')} ${formatter.formatCode(short(input.file_path))}`;
175
- case 'Bash': {
176
- const cmd = (input.command || '').substring(0, 100);
177
- return `💻 ${formatter.formatBold('Bash')} ${formatter.formatCode(cmd + (cmd.length >= 100 ? '...' : ''))}`;
178
- }
179
- default: {
180
- const mcpParts = parseMcpToolName(toolName);
181
- if (mcpParts) {
182
- return `🔌 ${formatter.formatBold(mcpParts.tool)} ${formatter.formatItalic(`(${mcpParts.server})`)}`;
183
- }
184
- return `● ${formatter.formatBold(toolName)}`;
185
- }
186
- }
187
- }
188
- /**
189
- * Format Claude in Chrome tool calls
190
- *
191
- * @param tool - The Chrome tool name (after mcp__claude-in-chrome__)
192
- * @param input - The tool input parameters
193
- * @param formatter - Platform-specific markdown formatter
194
- * @returns Formatted string for display
195
- */
196
- function formatChromeToolUse(tool, input, formatter) {
197
- const action = input.action || '';
198
- const coord = input.coordinate;
199
- const url = input.url || '';
200
- const text = input.text || '';
201
- switch (tool) {
202
- case 'computer': {
203
- let detail = '';
204
- switch (action) {
205
- case 'screenshot':
206
- detail = 'screenshot';
207
- break;
208
- case 'left_click':
209
- case 'right_click':
210
- case 'double_click':
211
- case 'triple_click':
212
- detail = coord ? `${action} at (${coord[0]}, ${coord[1]})` : action;
213
- break;
214
- case 'type':
215
- detail = `type "${text.substring(0, 30)}${text.length > 30 ? '...' : ''}"`;
216
- break;
217
- case 'key':
218
- detail = `key ${text}`;
219
- break;
220
- case 'scroll':
221
- detail = `scroll ${input.scroll_direction || 'down'}`;
222
- break;
223
- case 'wait':
224
- detail = `wait ${input.duration}s`;
225
- break;
226
- default:
227
- detail = action || 'action';
228
- }
229
- return `🌐 ${formatter.formatBold('Chrome')}[computer] ${formatter.formatCode(detail)}`;
230
- }
231
- case 'navigate':
232
- return `🌐 ${formatter.formatBold('Chrome')}[navigate] ${formatter.formatCode(url.substring(0, 50) + (url.length > 50 ? '...' : ''))}`;
233
- case 'tabs_context_mcp':
234
- return `🌐 ${formatter.formatBold('Chrome')}[tabs] reading context`;
235
- case 'tabs_create_mcp':
236
- return `🌐 ${formatter.formatBold('Chrome')}[tabs] creating new tab`;
237
- case 'read_page':
238
- return `🌐 ${formatter.formatBold('Chrome')}[read_page] ${input.filter === 'interactive' ? 'interactive elements' : 'accessibility tree'}`;
239
- case 'find':
240
- return `🌐 ${formatter.formatBold('Chrome')}[find] ${formatter.formatCode(input.query || '')}`;
241
- case 'form_input':
242
- return `🌐 ${formatter.formatBold('Chrome')}[form_input] setting value`;
243
- case 'get_page_text':
244
- return `🌐 ${formatter.formatBold('Chrome')}[get_page_text] extracting content`;
245
- case 'javascript_tool':
246
- return `🌐 ${formatter.formatBold('Chrome')}[javascript] executing script`;
247
- case 'gif_creator':
248
- return `🌐 ${formatter.formatBold('Chrome')}[gif] ${action}`;
249
- default:
250
- return `🌐 ${formatter.formatBold('Chrome')}[${tool}]`;
251
- }
252
- }
@@ -1 +0,0 @@
1
- export {};