starmux 0.11.0 → 0.13.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 (136) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +169 -97
  3. package/SECURITY.md +3 -3
  4. package/dist/src/access-control.js +23 -20
  5. package/dist/src/access-control.js.map +1 -1
  6. package/dist/src/access-dashboard.js +8 -2
  7. package/dist/src/access-dashboard.js.map +1 -1
  8. package/dist/src/cli/instances.js +87 -14
  9. package/dist/src/cli/instances.js.map +1 -1
  10. package/dist/src/cli/mac-mcp-gateway.js +4 -3
  11. package/dist/src/cli/mac-mcp-gateway.js.map +1 -1
  12. package/dist/src/cli/starmux-status.js +122 -0
  13. package/dist/src/cli/starmux-status.js.map +1 -0
  14. package/dist/src/cli/starmux.js +222 -49
  15. package/dist/src/cli/starmux.js.map +1 -1
  16. package/dist/src/deny-dashboard.js +2 -2
  17. package/dist/src/deny-dashboard.js.map +1 -1
  18. package/dist/src/instance-runtime.js +11 -2
  19. package/dist/src/instance-runtime.js.map +1 -1
  20. package/dist/src/instance-store.js +3 -1
  21. package/dist/src/instance-store.js.map +1 -1
  22. package/dist/src/ops-dashboard.js +4 -4
  23. package/dist/src/ops-dashboard.js.map +1 -1
  24. package/dist/src/permissions-dashboard.js +2 -2
  25. package/dist/src/permissions-dashboard.js.map +1 -1
  26. package/dist/src/runtime-paths.js +3 -3
  27. package/dist/src/runtime-paths.js.map +1 -1
  28. package/dist/src/starmux-migration.js +1 -1
  29. package/dist/src/starmux-migration.js.map +1 -1
  30. package/dist/vendor/desktop-commander/LICENSE +21 -0
  31. package/dist/vendor/desktop-commander/dist/bootstrap.js +22 -0
  32. package/dist/vendor/desktop-commander/dist/command-manager.js +246 -0
  33. package/dist/vendor/desktop-commander/dist/config-field-definitions.js +37 -0
  34. package/dist/vendor/desktop-commander/dist/config-manager.js +634 -0
  35. package/dist/vendor/desktop-commander/dist/config-recovery.js +81 -0
  36. package/dist/vendor/desktop-commander/dist/config.js +19 -0
  37. package/dist/vendor/desktop-commander/dist/custom-stdio.js +358 -0
  38. package/dist/vendor/desktop-commander/dist/error-handlers.js +15 -0
  39. package/dist/vendor/desktop-commander/dist/handlers/edit-search-handlers.js +6 -0
  40. package/dist/vendor/desktop-commander/dist/handlers/filesystem-handlers.js +450 -0
  41. package/dist/vendor/desktop-commander/dist/handlers/history-handlers.js +67 -0
  42. package/dist/vendor/desktop-commander/dist/handlers/index.js +7 -0
  43. package/dist/vendor/desktop-commander/dist/handlers/process-handlers.js +15 -0
  44. package/dist/vendor/desktop-commander/dist/handlers/search-answers.js +172 -0
  45. package/dist/vendor/desktop-commander/dist/handlers/search-handlers.js +136 -0
  46. package/dist/vendor/desktop-commander/dist/handlers/terminal-handlers.js +35 -0
  47. package/dist/vendor/desktop-commander/dist/index.js +88 -0
  48. package/dist/vendor/desktop-commander/dist/search-manager.js +1262 -0
  49. package/dist/vendor/desktop-commander/dist/server.js +1414 -0
  50. package/dist/vendor/desktop-commander/dist/terminal-manager.js +757 -0
  51. package/dist/vendor/desktop-commander/dist/tools/config.js +211 -0
  52. package/dist/vendor/desktop-commander/dist/tools/edit.js +455 -0
  53. package/dist/vendor/desktop-commander/dist/tools/feedback.js +10 -0
  54. package/dist/vendor/desktop-commander/dist/tools/filesystem.js +1047 -0
  55. package/dist/vendor/desktop-commander/dist/tools/fuzzySearch.js +74 -0
  56. package/dist/vendor/desktop-commander/dist/tools/fuzzySearchCore.js +125 -0
  57. package/dist/vendor/desktop-commander/dist/tools/improved-process-tools.js +726 -0
  58. package/dist/vendor/desktop-commander/dist/tools/mime-types.js +28 -0
  59. package/dist/vendor/desktop-commander/dist/tools/pdf/extract-images.js +131 -0
  60. package/dist/vendor/desktop-commander/dist/tools/pdf/index.js +4 -0
  61. package/dist/vendor/desktop-commander/dist/tools/pdf/lib/pdf2md.js +78 -0
  62. package/dist/vendor/desktop-commander/dist/tools/pdf/manipulations.js +112 -0
  63. package/dist/vendor/desktop-commander/dist/tools/pdf/markdown.js +30 -0
  64. package/dist/vendor/desktop-commander/dist/tools/pdf/utils.js +45 -0
  65. package/dist/vendor/desktop-commander/dist/tools/process.js +90 -0
  66. package/dist/vendor/desktop-commander/dist/tools/prompts.js +259 -0
  67. package/dist/vendor/desktop-commander/dist/tools/schemas.js +242 -0
  68. package/dist/vendor/desktop-commander/dist/tools/usage.js +24 -0
  69. package/dist/vendor/desktop-commander/dist/types.js +1 -0
  70. package/dist/vendor/desktop-commander/dist/ui/config-editor/config-editor-runtime.js +150 -0
  71. package/dist/vendor/desktop-commander/dist/ui/config-editor/index.html +13 -0
  72. package/dist/vendor/desktop-commander/dist/ui/config-editor/styles.css +587 -0
  73. package/dist/vendor/desktop-commander/dist/ui/contracts.js +57 -0
  74. package/dist/vendor/desktop-commander/dist/ui/file-preview/index.html +16 -0
  75. package/dist/vendor/desktop-commander/dist/ui/file-preview/preview-runtime.js +471 -0
  76. package/dist/vendor/desktop-commander/dist/ui/file-preview/shared/preview-file-types.js +59 -0
  77. package/dist/vendor/desktop-commander/dist/ui/file-preview/styles.css +1631 -0
  78. package/dist/vendor/desktop-commander/dist/ui/resources.js +93 -0
  79. package/dist/vendor/desktop-commander/dist/utils/ab-test.js +122 -0
  80. package/dist/vendor/desktop-commander/dist/utils/atomic-write.js +80 -0
  81. package/dist/vendor/desktop-commander/dist/utils/capture.js +40 -0
  82. package/dist/vendor/desktop-commander/dist/utils/dockerPrompt.js +9 -0
  83. package/dist/vendor/desktop-commander/dist/utils/exit-process.js +31 -0
  84. package/dist/vendor/desktop-commander/dist/utils/feature-flags.js +18 -0
  85. package/dist/vendor/desktop-commander/dist/utils/files/base.js +5 -0
  86. package/dist/vendor/desktop-commander/dist/utils/files/binary.js +65 -0
  87. package/dist/vendor/desktop-commander/dist/utils/files/docx.js +694 -0
  88. package/dist/vendor/desktop-commander/dist/utils/files/excel.js +497 -0
  89. package/dist/vendor/desktop-commander/dist/utils/files/factory.js +168 -0
  90. package/dist/vendor/desktop-commander/dist/utils/files/image.js +89 -0
  91. package/dist/vendor/desktop-commander/dist/utils/files/index.js +14 -0
  92. package/dist/vendor/desktop-commander/dist/utils/files/pdf.js +124 -0
  93. package/dist/vendor/desktop-commander/dist/utils/files/text.js +423 -0
  94. package/dist/vendor/desktop-commander/dist/utils/fuzzySearchLogger.js +126 -0
  95. package/dist/vendor/desktop-commander/dist/utils/internal-facts.js +23 -0
  96. package/dist/vendor/desktop-commander/dist/utils/lazy-package.js +44 -0
  97. package/dist/vendor/desktop-commander/dist/utils/lineEndingHandler.js +77 -0
  98. package/dist/vendor/desktop-commander/dist/utils/logger.js +72 -0
  99. package/dist/vendor/desktop-commander/dist/utils/mcp-ui-ab-test.js +62 -0
  100. package/dist/vendor/desktop-commander/dist/utils/open-browser.js +2 -0
  101. package/dist/vendor/desktop-commander/dist/utils/process-detection.js +260 -0
  102. package/dist/vendor/desktop-commander/dist/utils/process-tree.js +223 -0
  103. package/dist/vendor/desktop-commander/dist/utils/rename.js +74 -0
  104. package/dist/vendor/desktop-commander/dist/utils/ripgrep-resolver.js +69 -0
  105. package/dist/vendor/desktop-commander/dist/utils/shell.js +187 -0
  106. package/dist/vendor/desktop-commander/dist/utils/system-info.js +727 -0
  107. package/dist/vendor/desktop-commander/dist/utils/terminal-timestamps.js +18 -0
  108. package/dist/vendor/desktop-commander/dist/utils/toolHistory.js +309 -0
  109. package/dist/vendor/desktop-commander/dist/utils/trackTools.js +55 -0
  110. package/dist/vendor/desktop-commander/dist/utils/unsupportedParams.js +66 -0
  111. package/dist/vendor/desktop-commander/dist/utils/usageTracker.js +225 -0
  112. package/dist/vendor/desktop-commander/dist/utils/welcome-onboarding.js +2 -0
  113. package/dist/vendor/desktop-commander/dist/utils/withTimeout.js +85 -0
  114. package/dist/vendor/desktop-commander/dist/version.js +1 -0
  115. package/dist/vendor/desktop-commander/package.json +15 -0
  116. package/docs/ACCESS_CONTROL.md +5 -5
  117. package/docs/CLIENTS.md +11 -19
  118. package/docs/CONNECT_CHATGPT.md +4 -6
  119. package/docs/INSTALL.md +9 -5
  120. package/docs/INSTANCES.md +5 -2
  121. package/docs/OAUTH.md +6 -14
  122. package/docs/PLATFORMS.md +4 -4
  123. package/docs/README.md +2 -11
  124. package/docs/REMOTE_SETUP.md +13 -13
  125. package/docs/SECURITY.md +5 -5
  126. package/docs/assets/starmux-architecture.svg +315 -0
  127. package/package.json +14 -2
  128. package/docs/ARCHITECTURE.md +0 -51
  129. package/docs/MIGRATION.md +0 -37
  130. package/docs/RELEASE_STATUS.md +0 -31
  131. package/docs/TYPESCRIPT_MIGRATION.md +0 -56
  132. package/docs/VALIDATION_OAUTH.md +0 -79
  133. package/docs/VALIDATION_TUNNEL.md +0 -39
  134. package/docs/VALIDATION_WORKBUDDY.md +0 -59
  135. package/docs/assets/mcp-connection-route.svg +0 -104
  136. package/docs/assets/mcp-gateway-simple.svg +0 -62
@@ -0,0 +1,259 @@
1
+ import { usageTracker } from '../utils/usageTracker.js';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ // Get the directory path for ES modules
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ let cachedPromptsData = null;
9
+ /**
10
+ * Clear cached prompts data (for development/testing)
11
+ */
12
+ function clearCache() {
13
+ cachedPromptsData = null;
14
+ }
15
+ /**
16
+ * Load prompts data from JSON file with caching
17
+ */
18
+ export async function loadPromptsData() {
19
+ if (cachedPromptsData) {
20
+ return cachedPromptsData;
21
+ }
22
+ try {
23
+ const dataPath = path.join(__dirname, '..', 'data', 'onboarding-prompts.json');
24
+ const fileContent = await fs.readFile(dataPath, 'utf-8');
25
+ cachedPromptsData = JSON.parse(fileContent);
26
+ if (!cachedPromptsData) {
27
+ throw new Error('Failed to parse prompts data');
28
+ }
29
+ return cachedPromptsData;
30
+ }
31
+ catch (error) {
32
+ throw new Error(`Failed to load prompts data: ${error instanceof Error ? error.message : String(error)}`);
33
+ }
34
+ }
35
+ /**
36
+ * Get prompts - SIMPLIFIED VERSION (only get_prompt action)
37
+ */
38
+ export async function getPrompts(params) {
39
+ try {
40
+ // Validate and cast parameters
41
+ const { action, promptId, anonymous_user_use_case } = params;
42
+ if (!action) {
43
+ return {
44
+ content: [{
45
+ type: "text",
46
+ text: "❌ Error: 'action' parameter is required. Use 'get_prompt'"
47
+ }],
48
+ isError: true
49
+ };
50
+ }
51
+ // Only support get_prompt action now
52
+ if (action === 'get_prompt') {
53
+ if (!promptId) {
54
+ return {
55
+ content: [{
56
+ type: "text",
57
+ text: "❌ Error: promptId is required when action is 'get_prompt'"
58
+ }],
59
+ isError: true
60
+ };
61
+ }
62
+ return await getPrompt(promptId, anonymous_user_use_case);
63
+ }
64
+ // Legacy actions return deprecation notice
65
+ return {
66
+ content: [{
67
+ type: "text",
68
+ text: "❌ Error: Only 'get_prompt' action is supported. Use promptId to get a specific prompt."
69
+ }],
70
+ isError: true
71
+ };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ content: [{
76
+ type: "text",
77
+ text: `❌ Error: ${error instanceof Error ? error.message : String(error)}`
78
+ }],
79
+ isError: true
80
+ };
81
+ }
82
+ }
83
+ /**
84
+ * List all available categories
85
+ */
86
+ async function listCategories() {
87
+ const data = await loadPromptsData();
88
+ // Extract unique categories and count prompts in each
89
+ const categoryMap = new Map();
90
+ data.prompts.forEach(prompt => {
91
+ prompt.categories.forEach(category => {
92
+ categoryMap.set(category, (categoryMap.get(category) || 0) + 1);
93
+ });
94
+ });
95
+ const categories = Array.from(categoryMap.entries()).map(([name, count]) => ({
96
+ name,
97
+ count,
98
+ description: getCategoryDescription(name)
99
+ }));
100
+ const response = formatCategoriesResponse(categories, data.prompts.length);
101
+ return {
102
+ content: [{
103
+ type: "text",
104
+ text: response
105
+ }]
106
+ };
107
+ }
108
+ /**
109
+ * List prompts, optionally filtered by category
110
+ */
111
+ async function listPrompts(category) {
112
+ const data = await loadPromptsData();
113
+ let filteredPrompts = data.prompts;
114
+ // Filter by category if specified
115
+ if (category) {
116
+ filteredPrompts = data.prompts.filter(prompt => prompt.categories.includes(category));
117
+ if (filteredPrompts.length === 0) {
118
+ return {
119
+ content: [{
120
+ type: "text",
121
+ text: `❌ No prompts found in category "${category}". Use action='list_categories' to see available categories.`
122
+ }],
123
+ isError: true
124
+ };
125
+ }
126
+ }
127
+ const response = formatPromptsListResponse(filteredPrompts, category);
128
+ return {
129
+ content: [{
130
+ type: "text",
131
+ text: response
132
+ }]
133
+ };
134
+ }
135
+ /**
136
+ * Get a specific prompt by ID and inject it into the chat
137
+ */
138
+ async function getPrompt(promptId, anonymousUseCase) {
139
+ const data = await loadPromptsData();
140
+ const prompt = data.prompts.find(p => p.id === promptId);
141
+ if (!prompt) {
142
+ return {
143
+ content: [{
144
+ type: "text",
145
+ text: `❌ Prompt with ID '${promptId}' not found. Use action='list_prompts' to see available prompts.`
146
+ }],
147
+ isError: true
148
+ };
149
+ }
150
+ // Mark prompt as used in user's onboarding state (for analytics)
151
+ await usageTracker.markPromptUsed(promptId, prompt.categories[0] || 'uncategorized');
152
+ const response = formatPromptResponse(prompt);
153
+ return {
154
+ content: [{
155
+ type: "text",
156
+ text: response
157
+ }]
158
+ };
159
+ }
160
+ /**
161
+ * Get category description (can be expanded later)
162
+ */
163
+ function getCategoryDescription(category) {
164
+ const descriptions = {
165
+ 'onboarding': 'Curated prompts perfect for first-time Desktop Commander users',
166
+ 'Analyze data': 'Data analysis, visualization, and insights generation',
167
+ 'Build features and products': 'Full-stack development and application building',
168
+ 'Explore codebase': 'Code analysis, documentation, and understanding',
169
+ 'Organize files': 'File management, cleanup, and organization',
170
+ 'Deploy': 'Infrastructure setup, deployment, and DevOps tasks',
171
+ 'Optimize code': 'Code optimization, refactoring, and performance',
172
+ 'Write documentation': 'Technical writing, API docs, and guides',
173
+ 'Optimize workflow': 'Process improvements and productivity enhancements',
174
+ 'Automate tasks': 'Workflow automation and scripting',
175
+ 'Design systems': 'Architecture planning and system design'
176
+ };
177
+ return descriptions[category] || 'Desktop Commander prompts and workflows';
178
+ }
179
+ /**
180
+ * Format categories list response
181
+ */
182
+ function formatCategoriesResponse(categories, totalPrompts) {
183
+ const sortedCategories = categories.sort((a, b) => b.count - a.count);
184
+ // AI INSTRUCTION: When listing prompts, do not show prompt IDs to users - they are for your reference only
185
+ let response = `📚 **Desktop Commander Prompt Categories** (${categories.length} categories, ${totalPrompts} prompts)\n\n`;
186
+ sortedCategories.forEach(cat => {
187
+ response += `• **${cat.name}** (${cat.count} prompts) - ${cat.description}\n`;
188
+ });
189
+ response += `\n**Usage:**\n`;
190
+ response += `• \`get_prompts(action='list_prompts', category='onboarding')\` - See prompts in category\n`;
191
+ response += `• \`get_prompts(action='list_prompts')\` - See all available prompts\n`;
192
+ response += `• \`get_prompts(action='get_prompt', promptId='PROMPT_ID')\` - Get a specific prompt`;
193
+ return response;
194
+ }
195
+ /**
196
+ * Format prompts list response using secondary tags for clean organization
197
+ */
198
+ function formatPromptsListResponse(prompts, category) {
199
+ const categoryText = category ? ` in "${category}"` : '';
200
+ let response = `Desktop Commander Examples${categoryText}\n\n`;
201
+ // Group by secondary tag
202
+ const groupedPrompts = new Map();
203
+ prompts.forEach(prompt => {
204
+ const tag = prompt.secondaryTag || 'Other';
205
+ if (!groupedPrompts.has(tag)) {
206
+ groupedPrompts.set(tag, []);
207
+ }
208
+ groupedPrompts.get(tag).push(prompt);
209
+ });
210
+ let promptNumber = 1;
211
+ // Display groups in preferred order
212
+ const preferredOrder = ['Quick Start', 'Code Analysis', 'Build & Deploy', 'Other'];
213
+ preferredOrder.forEach(tag => {
214
+ if (groupedPrompts.has(tag)) {
215
+ const tagPrompts = groupedPrompts.get(tag);
216
+ // Add emoji for each section
217
+ const emoji = tag === 'Quick Start' ? '🚀' : tag === 'Code Analysis' ? '💻' : tag === 'Build & Deploy' ? '🔨' : '📋';
218
+ response += `**${emoji} ${tag}**\n`;
219
+ tagPrompts.forEach(prompt => {
220
+ response += `• ${promptNumber}. ${prompt.title}\n`;
221
+ promptNumber++;
222
+ });
223
+ response += `\n`;
224
+ }
225
+ });
226
+ response += `Say "Try #3" or "Let's do #1" to start any example.\n\n`;
227
+ response += `<INSTRUCTION>Output options for user exactly as provided without missing numbers.
228
+ Its important so user can choose an options by entering number only. Also do not forget new lines between numbers for easy reading</INSTRUCTION>\n\n`;
229
+ // AI reference mapping (do not show to user):
230
+ response += `<!-- AI_PROMPT_MAP: `;
231
+ let mapNumber = 1;
232
+ preferredOrder.forEach(tag => {
233
+ if (groupedPrompts.has(tag)) {
234
+ groupedPrompts.get(tag).forEach(prompt => {
235
+ response += `${mapNumber}=${prompt.id}${mapNumber < prompts.length ? ',' : ''}`;
236
+ mapNumber++;
237
+ });
238
+ }
239
+ });
240
+ response += ` -->`;
241
+ return response;
242
+ }
243
+ /**
244
+ * Format individual prompt response with the actual prompt content
245
+ */
246
+ function formatPromptResponse(prompt) {
247
+ const verifiedBadge = prompt.verified ? ' ✅' : '';
248
+ const categoryText = prompt.categories.join(', ');
249
+ let response = `# 🎯 ${prompt.title}${verifiedBadge}\n\n`;
250
+ response += `**Category:** ${categoryText} • **Author:** ${prompt.author}\n\n`;
251
+ response += `## Description\n${prompt.description}\n\n`;
252
+ if (prompt.votes > 0) {
253
+ response += `*📊 This prompt has been used successfully by ${prompt.votes}+ users*\n\n`;
254
+ }
255
+ response += `## Ready to Use This Prompt\nThe prompt below is ready to use. I'll start executing it right away:\n\n`;
256
+ response += `---\n\n${prompt.prompt}`;
257
+ // AI metadata (not shown to user): Executed prompt ID = ${prompt.id}
258
+ return response;
259
+ }
@@ -0,0 +1,242 @@
1
+ import { z } from "zod";
2
+ // Config tools schemas
3
+ export const GetConfigArgsSchema = z.object({
4
+ // 'ui' marks calls the config-editor widget fires programmatically; they are
5
+ // excluded from tool-call telemetry (see isUiOriginCall in server.ts).
6
+ origin: z.enum(['ui', 'llm']).optional(),
7
+ });
8
+ export const SetConfigValueArgsSchema = z.object({
9
+ key: z.string(),
10
+ value: z.union([
11
+ z.string(),
12
+ z.number(),
13
+ z.boolean(),
14
+ z.array(z.string()),
15
+ z.null(),
16
+ ]),
17
+ // 'ui' marks widget-fired calls; excluded from tool-call telemetry.
18
+ origin: z.enum(['ui', 'llm']).optional(),
19
+ });
20
+ // Empty schemas
21
+ export const ListProcessesArgsSchema = z.object({});
22
+ // Terminal tools schemas
23
+ export const StartProcessArgsSchema = z.object({
24
+ command: z.string(),
25
+ timeout_ms: z.number(),
26
+ shell: z.string().optional(),
27
+ verbose_timing: z.boolean().optional(),
28
+ // 'ui' marks widget-fired calls (e.g. open-in-folder/editor buttons);
29
+ // excluded from tool-call telemetry (see isUiOriginCall in server.ts).
30
+ origin: z.enum(['ui', 'llm']).optional(),
31
+ });
32
+ export const ReadProcessOutputArgsSchema = z.object({
33
+ pid: z.number(),
34
+ timeout_ms: z.number().optional(),
35
+ offset: z.number().optional(), // Line offset: 0=from last read, positive=absolute, negative=tail
36
+ length: z.number().optional(), // Max lines to return (default from config.fileReadLineLimit)
37
+ verbose_timing: z.boolean().optional(),
38
+ });
39
+ export const ForceTerminateArgsSchema = z.object({
40
+ pid: z.number(),
41
+ });
42
+ export const ListSessionsArgsSchema = z.object({});
43
+ export const KillProcessArgsSchema = z.object({
44
+ // process.kill reads 0 and negative PIDs as process groups: 0 is the server's
45
+ // own (on Windows the server itself), -1 every process the user may signal.
46
+ // A refinement rather than .positive(), so the published schema is unchanged.
47
+ pid: z.number().refine((pid) => pid > 0, { message: 'Number must be greater than 0' }),
48
+ });
49
+ // Filesystem tools schemas
50
+ export const ReadFileArgsSchema = z.object({
51
+ path: z.string(),
52
+ isUrl: z.boolean().optional().default(false),
53
+ offset: z.number().optional().default(0),
54
+ length: z.number().optional().default(1000),
55
+ sheet: z.string().optional(), // String only for MCP client compatibility (Cursor doesn't support union types in JSON Schema)
56
+ range: z.string().optional(),
57
+ options: z.record(z.any()).optional(),
58
+ // Whether the call came from the file-preview UI (refresh/navigation) or the
59
+ // LLM. 'ui' calls are excluded from tool-call telemetry; see isUiOriginCall
60
+ // in server.ts.
61
+ origin: z.enum(['ui', 'llm']).optional(),
62
+ });
63
+ export const ReadMultipleFilesArgsSchema = z.object({
64
+ paths: z.array(z.string()),
65
+ });
66
+ export const WriteFileArgsSchema = z.object({
67
+ path: z.string(),
68
+ content: z.string(),
69
+ mode: z.enum(['rewrite', 'append']).default('rewrite'),
70
+ // 'ui' when fired by the file-preview UI, else 'llm'. 'ui' calls are
71
+ // excluded from tool-call telemetry; see isUiOriginCall in server.ts.
72
+ origin: z.enum(['ui', 'llm']).optional(),
73
+ });
74
+ // PDF modification schemas - exported for reuse
75
+ export const PdfInsertOperationSchema = z.object({
76
+ type: z.literal('insert'),
77
+ pageIndex: z.number(),
78
+ markdown: z.string().optional(),
79
+ sourcePdfPath: z.string().optional(),
80
+ pdfOptions: z.object({}).passthrough().optional(),
81
+ });
82
+ export const PdfDeleteOperationSchema = z.object({
83
+ type: z.literal('delete'),
84
+ pageIndexes: z.array(z.number()),
85
+ });
86
+ export const PdfOperationSchema = z.union([PdfInsertOperationSchema, PdfDeleteOperationSchema]);
87
+ export const WritePdfArgsSchema = z.object({
88
+ path: z.string(),
89
+ // Preprocess content to handle JSON strings that should be parsed as arrays
90
+ content: z.preprocess((val) => {
91
+ // If it's a string that looks like JSON array, parse it
92
+ if (typeof val === 'string' && val.trim().startsWith('[')) {
93
+ try {
94
+ return JSON.parse(val);
95
+ }
96
+ catch {
97
+ // If parsing fails, return as-is (might be markdown content)
98
+ return val;
99
+ }
100
+ }
101
+ // Otherwise return as-is
102
+ return val;
103
+ }, z.union([z.string(), z.array(PdfOperationSchema)])),
104
+ outputPath: z.string().optional(),
105
+ options: z.object({}).passthrough().optional(), // Allow passing options to md-to-pdf
106
+ });
107
+ export const CreateDirectoryArgsSchema = z.object({
108
+ path: z.string(),
109
+ });
110
+ export const ListDirectoryArgsSchema = z.object({
111
+ path: z.string(),
112
+ depth: z.number().optional().default(2),
113
+ // 'ui' when fired by the file-preview UI, else 'llm'. 'ui' calls are
114
+ // excluded from tool-call telemetry; see isUiOriginCall in server.ts.
115
+ origin: z.enum(['ui', 'llm']).optional(),
116
+ });
117
+ export const MoveFileArgsSchema = z.object({
118
+ source: z.string(),
119
+ destination: z.string(),
120
+ });
121
+ export const GetFileInfoArgsSchema = z.object({
122
+ path: z.string(),
123
+ });
124
+ // Edit tools schema - SIMPLIFIED from three modes to two
125
+ // Previously supported: text replacement, location-based edits (edits array), and range rewrites
126
+ // Now supports only: text replacement and range rewrites
127
+ // Removed 'edits' array parameter - location-based surgical edits were complex and unnecessary
128
+ // Range rewrites are more powerful and cover all structured file editing needs
129
+ export const EditBlockArgsSchema = z.object({
130
+ file_path: z.string(),
131
+ // Text file string replacement
132
+ old_string: z.string().optional(),
133
+ new_string: z.string().optional(),
134
+ expected_replacements: z.number().optional().default(1),
135
+ // Structured file range rewrite (Excel, etc.)
136
+ range: z.string().optional(),
137
+ content: z.any().optional(),
138
+ options: z.record(z.any()).optional(),
139
+ // 'ui' when fired by the file-preview UI, else 'llm'. 'ui' calls are
140
+ // excluded from tool-call telemetry; see isUiOriginCall in server.ts.
141
+ origin: z.enum(['ui', 'llm']).optional(),
142
+ }).refine(data => {
143
+ // Helper to check if value is actually provided (not undefined, not empty string)
144
+ const hasValue = (v) => v !== undefined && v !== '';
145
+ return (hasValue(data.old_string) && data.new_string !== undefined) ||
146
+ (hasValue(data.range) && hasValue(data.content));
147
+ }, { message: "Must provide either (old_string + new_string) or (range + content)" });
148
+ // Send input to process schema
149
+ export const InteractWithProcessArgsSchema = z.object({
150
+ pid: z.number(),
151
+ input: z.string(),
152
+ timeout_ms: z.number().optional(),
153
+ wait_for_prompt: z.boolean().optional(),
154
+ verbose_timing: z.boolean().optional(),
155
+ });
156
+ // Usage stats schema
157
+ export const GetUsageStatsArgsSchema = z.object({});
158
+ // Feedback tool schema - no pre-filled parameters, all user input
159
+ export const GiveFeedbackArgsSchema = z.object({
160
+ // No parameters needed - form will be filled manually by user
161
+ // Only auto-filled hidden fields remain:
162
+ // - tool_call_count (auto)
163
+ // - days_using (auto)
164
+ // - platform (auto)
165
+ // - client_id (auto)
166
+ });
167
+ /** A whole number (at least `min`, if given), rejected with a message that says so */
168
+ function wholeNumber(name, min) {
169
+ const message = `${name} must be a whole number${min === undefined ? '' : ` of at least ${min}`}`;
170
+ const number = z.number().int({ message });
171
+ return min === undefined ? number : number.min(min, { message });
172
+ }
173
+ // Search schemas (renamed for natural language)
174
+ export const StartSearchArgsSchema = z.object({
175
+ path: z.string(),
176
+ pattern: z.string(),
177
+ searchType: z.enum(['files', 'content']).default('files'),
178
+ filePattern: z.string().optional(),
179
+ ignoreCase: z.boolean().optional().default(true),
180
+ maxResults: wholeNumber('maxResults', 0).optional(), // 0: no limit
181
+ includeHidden: z.boolean().optional().default(false),
182
+ contextLines: wholeNumber('contextLines', 0).optional().default(5),
183
+ timeout_ms: wholeNumber('timeout_ms', 0).optional(), // Match process naming convention; 0: no time limit
184
+ earlyTermination: z.boolean().optional(), // Stop search early when exact filename match is found (default: true for files, false for content)
185
+ literalSearch: z.boolean().optional().default(false), // Force literal string matching (-F flag) instead of regex
186
+ // 'ui' marks widget-fired calls (e.g. markdown link-target search);
187
+ // excluded from tool-call telemetry (see isUiOriginCall in server.ts).
188
+ origin: z.enum(['ui', 'llm']).optional(),
189
+ });
190
+ export const GetMoreSearchResultsArgsSchema = z.object({
191
+ sessionId: z.string(),
192
+ offset: wholeNumber('offset').optional().default(0), // Same as file reading
193
+ length: wholeNumber('length', 1).optional().default(100), // Same as file reading (but smaller default)
194
+ });
195
+ export const StopSearchArgsSchema = z.object({
196
+ sessionId: z.string(),
197
+ });
198
+ export const ListSearchesArgsSchema = z.object({});
199
+ // Tool history schema
200
+ export const GetRecentToolCallsArgsSchema = z.object({
201
+ maxResults: z.number().min(1).max(1000).optional().default(50),
202
+ toolName: z.string().optional(),
203
+ since: z.string().datetime().optional(),
204
+ });
205
+ export const TrackUiEventArgsSchema = z.object({
206
+ event: z.string().min(1).max(80),
207
+ component: z.string().optional().default('file_preview'),
208
+ params: z.record(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional().default({}),
209
+ });
210
+ /**
211
+ * Map of tool name -> argument schema, used by the dispatcher to detect and warn
212
+ * about parameters a caller sent that the tool does not support. Keep in sync
213
+ * with the tool definitions in server.ts.
214
+ */
215
+ export const toolArgSchemas = {
216
+ get_config: GetConfigArgsSchema,
217
+ set_config_value: SetConfigValueArgsSchema,
218
+ read_file: ReadFileArgsSchema,
219
+ read_multiple_files: ReadMultipleFilesArgsSchema,
220
+ write_file: WriteFileArgsSchema,
221
+ write_pdf: WritePdfArgsSchema,
222
+ create_directory: CreateDirectoryArgsSchema,
223
+ list_directory: ListDirectoryArgsSchema,
224
+ move_file: MoveFileArgsSchema,
225
+ start_search: StartSearchArgsSchema,
226
+ get_more_search_results: GetMoreSearchResultsArgsSchema,
227
+ stop_search: StopSearchArgsSchema,
228
+ list_searches: ListSearchesArgsSchema,
229
+ get_file_info: GetFileInfoArgsSchema,
230
+ edit_block: EditBlockArgsSchema,
231
+ start_process: StartProcessArgsSchema,
232
+ read_process_output: ReadProcessOutputArgsSchema,
233
+ interact_with_process: InteractWithProcessArgsSchema,
234
+ force_terminate: ForceTerminateArgsSchema,
235
+ list_sessions: ListSessionsArgsSchema,
236
+ list_processes: ListProcessesArgsSchema,
237
+ kill_process: KillProcessArgsSchema,
238
+ get_usage_stats: GetUsageStatsArgsSchema,
239
+ get_recent_tool_calls: GetRecentToolCallsArgsSchema,
240
+ give_feedback_to_desktop_commander: GiveFeedbackArgsSchema,
241
+ track_ui_event: TrackUiEventArgsSchema,
242
+ };
@@ -0,0 +1,24 @@
1
+ import { usageTracker } from '../utils/usageTracker.js';
2
+ /**
3
+ * Get usage statistics for debugging and analysis
4
+ */
5
+ export async function getUsageStats() {
6
+ try {
7
+ const summary = await usageTracker.getUsageSummary();
8
+ return {
9
+ content: [{
10
+ type: "text",
11
+ text: summary
12
+ }]
13
+ };
14
+ }
15
+ catch (error) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: `Error retrieving usage stats: ${error instanceof Error ? error.message : String(error)}`
20
+ }],
21
+ isError: true
22
+ };
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ export {};