@cloudcli-ai/cloudcli 1.29.5 → 1.30.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.
- package/README.de.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +252 -0
- package/README.zh-CN.md +1 -1
- package/dist/assets/index-DI22MZMb.css +32 -0
- package/dist/assets/index-RX-W_g_Q.js +1262 -0
- package/dist/index.html +2 -2
- package/dist-server/server/claude-sdk.js +13 -8
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +7 -7
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +5 -5
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +2 -2
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +27 -25
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +266 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +13 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +282 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +13 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +369 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +13 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +131 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +207 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +13 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +31 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
- package/dist-server/server/modules/providers/provider.routes.js +159 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
- package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +29 -0
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
- package/dist-server/server/openai-codex.js +5 -5
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/codex.js +2 -278
- package/dist-server/server/routes/codex.js.map +1 -1
- package/dist-server/server/routes/commands.js +0 -44
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +9 -489
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/mcp-utils.js +1 -18
- package/dist-server/server/routes/mcp-utils.js.map +1 -1
- package/dist-server/server/routes/messages.js +6 -6
- package/dist-server/server/routes/messages.js.map +1 -1
- package/dist-server/server/routes/settings.js +10 -0
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +0 -459
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/shared/interfaces.js +2 -0
- package/dist-server/server/shared/interfaces.js.map +1 -0
- package/dist-server/server/shared/types.js +3 -0
- package/dist-server/server/shared/types.js.map +1 -0
- package/dist-server/server/shared/utils.js +150 -0
- package/dist-server/server/shared/utils.js.map +1 -0
- package/dist-server/server/utils/mcp-detector.js +0 -49
- package/dist-server/server/utils/mcp-detector.js.map +1 -1
- package/dist-server/shared/modelConstants.js +1 -0
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +5 -2
- package/server/claude-sdk.js +14 -8
- package/server/cursor-cli.js +7 -7
- package/server/gemini-cli.js +5 -5
- package/server/gemini-response-handler.js +2 -2
- package/server/index.js +33 -29
- package/server/modules/providers/list/claude/claude-auth.provider.ts +123 -0
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -0
- package/server/modules/providers/list/claude/claude.provider.ts +15 -0
- package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -0
- package/server/modules/providers/list/codex/codex.provider.ts +15 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -0
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +151 -0
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -0
- package/server/modules/providers/provider.registry.ts +36 -0
- package/server/modules/providers/provider.routes.ts +217 -0
- package/server/modules/providers/services/mcp.service.ts +94 -0
- package/server/modules/providers/services/provider-auth.service.ts +26 -0
- package/server/modules/providers/services/sessions.service.ts +45 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -0
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
- package/server/modules/providers/tests/mcp.test.ts +293 -0
- package/server/openai-codex.js +5 -5
- package/server/routes/codex.js +2 -312
- package/server/routes/commands.js +0 -49
- package/server/routes/cursor.js +15 -527
- package/server/routes/mcp-utils.js +2 -19
- package/server/routes/messages.js +6 -6
- package/server/routes/settings.js +10 -0
- package/server/routes/taskmaster.js +0 -492
- package/server/shared/interfaces.ts +54 -0
- package/server/shared/types.ts +172 -0
- package/server/shared/utils.ts +193 -0
- package/server/tsconfig.json +6 -3
- package/server/utils/mcp-detector.js +0 -51
- package/shared/modelConstants.js +1 -0
- package/dist/assets/index-BBAE1OJ_.css +0 -32
- package/dist/assets/index-Ci1GOyV7.js +0 -1262
- package/dist-server/server/providers/claude/adapter.js +0 -266
- package/dist-server/server/providers/claude/adapter.js.map +0 -1
- package/dist-server/server/providers/claude/status.js +0 -121
- package/dist-server/server/providers/claude/status.js.map +0 -1
- package/dist-server/server/providers/codex/adapter.js +0 -232
- package/dist-server/server/providers/codex/adapter.js.map +0 -1
- package/dist-server/server/providers/codex/status.js +0 -70
- package/dist-server/server/providers/codex/status.js.map +0 -1
- package/dist-server/server/providers/cursor/adapter.js +0 -342
- package/dist-server/server/providers/cursor/adapter.js.map +0 -1
- package/dist-server/server/providers/cursor/status.js +0 -118
- package/dist-server/server/providers/cursor/status.js.map +0 -1
- package/dist-server/server/providers/gemini/adapter.js +0 -174
- package/dist-server/server/providers/gemini/adapter.js.map +0 -1
- package/dist-server/server/providers/gemini/status.js +0 -104
- package/dist-server/server/providers/gemini/status.js.map +0 -1
- package/dist-server/server/providers/registry.js +0 -58
- package/dist-server/server/providers/registry.js.map +0 -1
- package/dist-server/server/providers/types.js +0 -117
- package/dist-server/server/providers/types.js.map +0 -1
- package/dist-server/server/providers/utils.js +0 -28
- package/dist-server/server/providers/utils.js.map +0 -1
- package/dist-server/server/routes/cli-auth.js +0 -25
- package/dist-server/server/routes/cli-auth.js.map +0 -1
- package/dist-server/server/routes/mcp.js +0 -480
- package/dist-server/server/routes/mcp.js.map +0 -1
- package/server/providers/claude/adapter.js +0 -278
- package/server/providers/claude/status.js +0 -136
- package/server/providers/codex/adapter.js +0 -248
- package/server/providers/codex/status.js +0 -78
- package/server/providers/cursor/adapter.js +0 -348
- package/server/providers/cursor/status.js +0 -128
- package/server/providers/gemini/adapter.js +0 -186
- package/server/providers/gemini/status.js +0 -111
- package/server/providers/registry.js +0 -67
- package/server/providers/types.js +0 -132
- package/server/providers/utils.js +0 -29
- package/server/routes/cli-auth.js +0 -27
- package/server/routes/mcp.js +0 -552
|
@@ -273,4 +273,14 @@ router.post('/push/unsubscribe', async (req, res) => {
|
|
|
273
273
|
}
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
+
// Host OS for UI (e.g. hide Cursor agent when the backend runs on Windows).
|
|
277
|
+
router.get('/server-env', async (req, res) => {
|
|
278
|
+
try {
|
|
279
|
+
res.json({ platform: process.platform });
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('Error reading server environment:', error);
|
|
282
|
+
res.status(500).json({ error: 'Failed to read server environment' });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
276
286
|
export default router;
|
|
@@ -13,16 +13,10 @@ import fs from 'fs';
|
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import { promises as fsPromises } from 'fs';
|
|
15
15
|
import { spawn } from 'child_process';
|
|
16
|
-
import { fileURLToPath } from 'url';
|
|
17
|
-
import { dirname } from 'path';
|
|
18
|
-
import os from 'os';
|
|
19
16
|
import { extractProjectDirectory } from '../projects.js';
|
|
20
17
|
import { detectTaskMasterMCPServer } from '../utils/mcp-detector.js';
|
|
21
18
|
import { broadcastTaskMasterProjectUpdate, broadcastTaskMasterTasksUpdate } from '../utils/taskmaster-websocket.js';
|
|
22
19
|
|
|
23
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
-
const __dirname = dirname(__filename);
|
|
25
|
-
|
|
26
20
|
const router = express.Router();
|
|
27
21
|
|
|
28
22
|
/**
|
|
@@ -100,140 +94,6 @@ async function checkTaskMasterInstallation() {
|
|
|
100
94
|
});
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
/**
|
|
104
|
-
* Detect .taskmaster folder presence in a given project directory
|
|
105
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
106
|
-
* @returns {Promise<Object>} Detection result with status and metadata
|
|
107
|
-
*/
|
|
108
|
-
async function detectTaskMasterFolder(projectPath) {
|
|
109
|
-
try {
|
|
110
|
-
const taskMasterPath = path.join(projectPath, '.taskmaster');
|
|
111
|
-
|
|
112
|
-
// Check if .taskmaster directory exists
|
|
113
|
-
try {
|
|
114
|
-
const stats = await fsPromises.stat(taskMasterPath);
|
|
115
|
-
if (!stats.isDirectory()) {
|
|
116
|
-
return {
|
|
117
|
-
hasTaskmaster: false,
|
|
118
|
-
reason: '.taskmaster exists but is not a directory'
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (error.code === 'ENOENT') {
|
|
123
|
-
return {
|
|
124
|
-
hasTaskmaster: false,
|
|
125
|
-
reason: '.taskmaster directory not found'
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Check for key TaskMaster files
|
|
132
|
-
const keyFiles = [
|
|
133
|
-
'tasks/tasks.json',
|
|
134
|
-
'config.json'
|
|
135
|
-
];
|
|
136
|
-
|
|
137
|
-
const fileStatus = {};
|
|
138
|
-
let hasEssentialFiles = true;
|
|
139
|
-
|
|
140
|
-
for (const file of keyFiles) {
|
|
141
|
-
const filePath = path.join(taskMasterPath, file);
|
|
142
|
-
try {
|
|
143
|
-
await fsPromises.access(filePath, fs.constants.R_OK);
|
|
144
|
-
fileStatus[file] = true;
|
|
145
|
-
} catch (error) {
|
|
146
|
-
fileStatus[file] = false;
|
|
147
|
-
if (file === 'tasks/tasks.json') {
|
|
148
|
-
hasEssentialFiles = false;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Parse tasks.json if it exists for metadata
|
|
154
|
-
let taskMetadata = null;
|
|
155
|
-
if (fileStatus['tasks/tasks.json']) {
|
|
156
|
-
try {
|
|
157
|
-
const tasksPath = path.join(taskMasterPath, 'tasks/tasks.json');
|
|
158
|
-
const tasksContent = await fsPromises.readFile(tasksPath, 'utf8');
|
|
159
|
-
const tasksData = JSON.parse(tasksContent);
|
|
160
|
-
|
|
161
|
-
// Handle both tagged and legacy formats
|
|
162
|
-
let tasks = [];
|
|
163
|
-
if (tasksData.tasks) {
|
|
164
|
-
// Legacy format
|
|
165
|
-
tasks = tasksData.tasks;
|
|
166
|
-
} else {
|
|
167
|
-
// Tagged format - get tasks from all tags
|
|
168
|
-
Object.values(tasksData).forEach(tagData => {
|
|
169
|
-
if (tagData.tasks) {
|
|
170
|
-
tasks = tasks.concat(tagData.tasks);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Calculate task statistics
|
|
176
|
-
const stats = tasks.reduce((acc, task) => {
|
|
177
|
-
acc.total++;
|
|
178
|
-
acc[task.status] = (acc[task.status] || 0) + 1;
|
|
179
|
-
|
|
180
|
-
// Count subtasks
|
|
181
|
-
if (task.subtasks) {
|
|
182
|
-
task.subtasks.forEach(subtask => {
|
|
183
|
-
acc.subtotalTasks++;
|
|
184
|
-
acc.subtasks = acc.subtasks || {};
|
|
185
|
-
acc.subtasks[subtask.status] = (acc.subtasks[subtask.status] || 0) + 1;
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return acc;
|
|
190
|
-
}, {
|
|
191
|
-
total: 0,
|
|
192
|
-
subtotalTasks: 0,
|
|
193
|
-
pending: 0,
|
|
194
|
-
'in-progress': 0,
|
|
195
|
-
done: 0,
|
|
196
|
-
review: 0,
|
|
197
|
-
deferred: 0,
|
|
198
|
-
cancelled: 0,
|
|
199
|
-
subtasks: {}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
taskMetadata = {
|
|
203
|
-
taskCount: stats.total,
|
|
204
|
-
subtaskCount: stats.subtotalTasks,
|
|
205
|
-
completed: stats.done || 0,
|
|
206
|
-
pending: stats.pending || 0,
|
|
207
|
-
inProgress: stats['in-progress'] || 0,
|
|
208
|
-
review: stats.review || 0,
|
|
209
|
-
completionPercentage: stats.total > 0 ? Math.round((stats.done / stats.total) * 100) : 0,
|
|
210
|
-
lastModified: (await fsPromises.stat(tasksPath)).mtime.toISOString()
|
|
211
|
-
};
|
|
212
|
-
} catch (parseError) {
|
|
213
|
-
console.warn('Failed to parse tasks.json:', parseError.message);
|
|
214
|
-
taskMetadata = { error: 'Failed to parse tasks.json' };
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
hasTaskmaster: true,
|
|
220
|
-
hasEssentialFiles,
|
|
221
|
-
files: fileStatus,
|
|
222
|
-
metadata: taskMetadata,
|
|
223
|
-
path: taskMasterPath
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
} catch (error) {
|
|
227
|
-
console.error('Error detecting TaskMaster folder:', error);
|
|
228
|
-
return {
|
|
229
|
-
hasTaskmaster: false,
|
|
230
|
-
reason: `Error checking directory: ${error.message}`
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// MCP detection is now handled by the centralized utility
|
|
236
|
-
|
|
237
97
|
// API Routes
|
|
238
98
|
|
|
239
99
|
/**
|
|
@@ -271,298 +131,6 @@ router.get('/installation-status', async (req, res) => {
|
|
|
271
131
|
}
|
|
272
132
|
});
|
|
273
133
|
|
|
274
|
-
/**
|
|
275
|
-
* GET /api/taskmaster/detect/:projectName
|
|
276
|
-
* Detect TaskMaster configuration for a specific project
|
|
277
|
-
*/
|
|
278
|
-
router.get('/detect/:projectName', async (req, res) => {
|
|
279
|
-
try {
|
|
280
|
-
const { projectName } = req.params;
|
|
281
|
-
|
|
282
|
-
// Use the existing extractProjectDirectory function to get actual project path
|
|
283
|
-
let projectPath;
|
|
284
|
-
try {
|
|
285
|
-
projectPath = await extractProjectDirectory(projectName);
|
|
286
|
-
} catch (error) {
|
|
287
|
-
console.error('Error extracting project directory:', error);
|
|
288
|
-
return res.status(404).json({
|
|
289
|
-
error: 'Project path not found',
|
|
290
|
-
projectName,
|
|
291
|
-
message: error.message
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Verify the project path exists
|
|
296
|
-
try {
|
|
297
|
-
await fsPromises.access(projectPath, fs.constants.R_OK);
|
|
298
|
-
} catch (error) {
|
|
299
|
-
return res.status(404).json({
|
|
300
|
-
error: 'Project path not accessible',
|
|
301
|
-
projectPath,
|
|
302
|
-
projectName,
|
|
303
|
-
message: error.message
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Run detection in parallel
|
|
308
|
-
const [taskMasterResult, mcpResult] = await Promise.all([
|
|
309
|
-
detectTaskMasterFolder(projectPath),
|
|
310
|
-
detectTaskMasterMCPServer()
|
|
311
|
-
]);
|
|
312
|
-
|
|
313
|
-
// Determine overall status
|
|
314
|
-
let status = 'not-configured';
|
|
315
|
-
if (taskMasterResult.hasTaskmaster && taskMasterResult.hasEssentialFiles) {
|
|
316
|
-
if (mcpResult.hasMCPServer && mcpResult.isConfigured) {
|
|
317
|
-
status = 'fully-configured';
|
|
318
|
-
} else {
|
|
319
|
-
status = 'taskmaster-only';
|
|
320
|
-
}
|
|
321
|
-
} else if (mcpResult.hasMCPServer && mcpResult.isConfigured) {
|
|
322
|
-
status = 'mcp-only';
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const responseData = {
|
|
326
|
-
projectName,
|
|
327
|
-
projectPath,
|
|
328
|
-
status,
|
|
329
|
-
taskmaster: taskMasterResult,
|
|
330
|
-
mcp: mcpResult,
|
|
331
|
-
timestamp: new Date().toISOString()
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
res.json(responseData);
|
|
335
|
-
|
|
336
|
-
} catch (error) {
|
|
337
|
-
console.error('TaskMaster detection error:', error);
|
|
338
|
-
res.status(500).json({
|
|
339
|
-
error: 'Failed to detect TaskMaster configuration',
|
|
340
|
-
message: error.message
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* GET /api/taskmaster/detect-all
|
|
347
|
-
* Detect TaskMaster configuration for all known projects
|
|
348
|
-
* This endpoint works with the existing projects system
|
|
349
|
-
*/
|
|
350
|
-
router.get('/detect-all', async (req, res) => {
|
|
351
|
-
try {
|
|
352
|
-
// Import getProjects from the projects module
|
|
353
|
-
const { getProjects } = await import('../projects.js');
|
|
354
|
-
const projects = await getProjects();
|
|
355
|
-
|
|
356
|
-
// Run detection for all projects in parallel
|
|
357
|
-
const detectionPromises = projects.map(async (project) => {
|
|
358
|
-
try {
|
|
359
|
-
// Use the project's fullPath if available, otherwise extract the directory
|
|
360
|
-
let projectPath;
|
|
361
|
-
if (project.fullPath) {
|
|
362
|
-
projectPath = project.fullPath;
|
|
363
|
-
} else {
|
|
364
|
-
try {
|
|
365
|
-
projectPath = await extractProjectDirectory(project.name);
|
|
366
|
-
} catch (error) {
|
|
367
|
-
throw new Error(`Failed to extract project directory: ${error.message}`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const [taskMasterResult, mcpResult] = await Promise.all([
|
|
372
|
-
detectTaskMasterFolder(projectPath),
|
|
373
|
-
detectTaskMasterMCPServer()
|
|
374
|
-
]);
|
|
375
|
-
|
|
376
|
-
// Determine status
|
|
377
|
-
let status = 'not-configured';
|
|
378
|
-
if (taskMasterResult.hasTaskmaster && taskMasterResult.hasEssentialFiles) {
|
|
379
|
-
if (mcpResult.hasMCPServer && mcpResult.isConfigured) {
|
|
380
|
-
status = 'fully-configured';
|
|
381
|
-
} else {
|
|
382
|
-
status = 'taskmaster-only';
|
|
383
|
-
}
|
|
384
|
-
} else if (mcpResult.hasMCPServer && mcpResult.isConfigured) {
|
|
385
|
-
status = 'mcp-only';
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
projectName: project.name,
|
|
390
|
-
displayName: project.displayName,
|
|
391
|
-
projectPath,
|
|
392
|
-
status,
|
|
393
|
-
taskmaster: taskMasterResult,
|
|
394
|
-
mcp: mcpResult
|
|
395
|
-
};
|
|
396
|
-
} catch (error) {
|
|
397
|
-
return {
|
|
398
|
-
projectName: project.name,
|
|
399
|
-
displayName: project.displayName,
|
|
400
|
-
status: 'error',
|
|
401
|
-
error: error.message
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
const results = await Promise.all(detectionPromises);
|
|
407
|
-
|
|
408
|
-
res.json({
|
|
409
|
-
projects: results,
|
|
410
|
-
summary: {
|
|
411
|
-
total: results.length,
|
|
412
|
-
fullyConfigured: results.filter(p => p.status === 'fully-configured').length,
|
|
413
|
-
taskmasterOnly: results.filter(p => p.status === 'taskmaster-only').length,
|
|
414
|
-
mcpOnly: results.filter(p => p.status === 'mcp-only').length,
|
|
415
|
-
notConfigured: results.filter(p => p.status === 'not-configured').length,
|
|
416
|
-
errors: results.filter(p => p.status === 'error').length
|
|
417
|
-
},
|
|
418
|
-
timestamp: new Date().toISOString()
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
} catch (error) {
|
|
422
|
-
console.error('Bulk TaskMaster detection error:', error);
|
|
423
|
-
res.status(500).json({
|
|
424
|
-
error: 'Failed to detect TaskMaster configuration for projects',
|
|
425
|
-
message: error.message
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* POST /api/taskmaster/initialize/:projectName
|
|
432
|
-
* Initialize TaskMaster in a project (placeholder for future CLI integration)
|
|
433
|
-
*/
|
|
434
|
-
router.post('/initialize/:projectName', async (req, res) => {
|
|
435
|
-
try {
|
|
436
|
-
const { projectName } = req.params;
|
|
437
|
-
const { rules } = req.body; // Optional rule profiles
|
|
438
|
-
|
|
439
|
-
// This will be implemented in a later subtask with CLI integration
|
|
440
|
-
res.status(501).json({
|
|
441
|
-
error: 'TaskMaster initialization not yet implemented',
|
|
442
|
-
message: 'This endpoint will execute task-master init via CLI in a future update',
|
|
443
|
-
projectName,
|
|
444
|
-
rules
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
} catch (error) {
|
|
448
|
-
console.error('TaskMaster initialization error:', error);
|
|
449
|
-
res.status(500).json({
|
|
450
|
-
error: 'Failed to initialize TaskMaster',
|
|
451
|
-
message: error.message
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* GET /api/taskmaster/next/:projectName
|
|
458
|
-
* Get the next recommended task using task-master CLI
|
|
459
|
-
*/
|
|
460
|
-
router.get('/next/:projectName', async (req, res) => {
|
|
461
|
-
try {
|
|
462
|
-
const { projectName } = req.params;
|
|
463
|
-
|
|
464
|
-
// Get project path
|
|
465
|
-
let projectPath;
|
|
466
|
-
try {
|
|
467
|
-
projectPath = await extractProjectDirectory(projectName);
|
|
468
|
-
} catch (error) {
|
|
469
|
-
return res.status(404).json({
|
|
470
|
-
error: 'Project not found',
|
|
471
|
-
message: `Project "${projectName}" does not exist`
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Try to execute task-master next command
|
|
476
|
-
try {
|
|
477
|
-
const { spawn } = await import('child_process');
|
|
478
|
-
|
|
479
|
-
const nextTaskCommand = spawn('task-master', ['next'], {
|
|
480
|
-
cwd: projectPath,
|
|
481
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
let stdout = '';
|
|
485
|
-
let stderr = '';
|
|
486
|
-
|
|
487
|
-
nextTaskCommand.stdout.on('data', (data) => {
|
|
488
|
-
stdout += data.toString();
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
nextTaskCommand.stderr.on('data', (data) => {
|
|
492
|
-
stderr += data.toString();
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
await new Promise((resolve, reject) => {
|
|
496
|
-
nextTaskCommand.on('close', (code) => {
|
|
497
|
-
if (code === 0) {
|
|
498
|
-
resolve();
|
|
499
|
-
} else {
|
|
500
|
-
reject(new Error(`task-master next failed with code ${code}: ${stderr}`));
|
|
501
|
-
}
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
nextTaskCommand.on('error', (error) => {
|
|
505
|
-
reject(error);
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
// Parse the output - task-master next usually returns JSON
|
|
510
|
-
let nextTaskData = null;
|
|
511
|
-
if (stdout.trim()) {
|
|
512
|
-
try {
|
|
513
|
-
nextTaskData = JSON.parse(stdout);
|
|
514
|
-
} catch (parseError) {
|
|
515
|
-
// If not JSON, treat as plain text
|
|
516
|
-
nextTaskData = { message: stdout.trim() };
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
res.json({
|
|
521
|
-
projectName,
|
|
522
|
-
projectPath,
|
|
523
|
-
nextTask: nextTaskData,
|
|
524
|
-
timestamp: new Date().toISOString()
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
} catch (cliError) {
|
|
528
|
-
console.warn('Failed to execute task-master CLI:', cliError.message);
|
|
529
|
-
|
|
530
|
-
// Fallback to loading tasks and finding next one locally
|
|
531
|
-
// Use localhost to bypass proxy for internal server-to-server calls
|
|
532
|
-
const tasksResponse = await fetch(`http://localhost:${process.env.SERVER_PORT || process.env.PORT || '3001'}/api/taskmaster/tasks/${encodeURIComponent(projectName)}`, {
|
|
533
|
-
headers: {
|
|
534
|
-
'Authorization': req.headers.authorization
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
if (tasksResponse.ok) {
|
|
539
|
-
const tasksData = await tasksResponse.json();
|
|
540
|
-
const nextTask = tasksData.tasks?.find(task =>
|
|
541
|
-
task.status === 'pending' || task.status === 'in-progress'
|
|
542
|
-
) || null;
|
|
543
|
-
|
|
544
|
-
res.json({
|
|
545
|
-
projectName,
|
|
546
|
-
projectPath,
|
|
547
|
-
nextTask,
|
|
548
|
-
fallback: true,
|
|
549
|
-
message: 'Used fallback method (CLI not available)',
|
|
550
|
-
timestamp: new Date().toISOString()
|
|
551
|
-
});
|
|
552
|
-
} else {
|
|
553
|
-
throw new Error('Failed to load tasks via fallback method');
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
} catch (error) {
|
|
558
|
-
console.error('TaskMaster next task error:', error);
|
|
559
|
-
res.status(500).json({
|
|
560
|
-
error: 'Failed to get next task',
|
|
561
|
-
message: error.message
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
|
|
566
134
|
/**
|
|
567
135
|
* GET /api/taskmaster/tasks/:projectName
|
|
568
136
|
* Load actual tasks from .taskmaster/tasks/tasks.json
|
|
@@ -904,66 +472,6 @@ router.get('/prd/:projectName/:fileName', async (req, res) => {
|
|
|
904
472
|
}
|
|
905
473
|
});
|
|
906
474
|
|
|
907
|
-
/**
|
|
908
|
-
* DELETE /api/taskmaster/prd/:projectName/:fileName
|
|
909
|
-
* Delete a specific PRD file
|
|
910
|
-
*/
|
|
911
|
-
router.delete('/prd/:projectName/:fileName', async (req, res) => {
|
|
912
|
-
try {
|
|
913
|
-
const { projectName, fileName } = req.params;
|
|
914
|
-
|
|
915
|
-
// Get project path
|
|
916
|
-
let projectPath;
|
|
917
|
-
try {
|
|
918
|
-
projectPath = await extractProjectDirectory(projectName);
|
|
919
|
-
} catch (error) {
|
|
920
|
-
return res.status(404).json({
|
|
921
|
-
error: 'Project not found',
|
|
922
|
-
message: `Project "${projectName}" does not exist`
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
const filePath = path.join(projectPath, '.taskmaster', 'docs', fileName);
|
|
927
|
-
|
|
928
|
-
// Check if file exists
|
|
929
|
-
try {
|
|
930
|
-
await fsPromises.access(filePath, fs.constants.F_OK);
|
|
931
|
-
} catch (error) {
|
|
932
|
-
return res.status(404).json({
|
|
933
|
-
error: 'PRD file not found',
|
|
934
|
-
message: `File "${fileName}" does not exist`
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
// Delete the file
|
|
939
|
-
try {
|
|
940
|
-
await fsPromises.unlink(filePath);
|
|
941
|
-
|
|
942
|
-
res.json({
|
|
943
|
-
projectName,
|
|
944
|
-
projectPath,
|
|
945
|
-
fileName,
|
|
946
|
-
message: 'PRD file deleted successfully',
|
|
947
|
-
timestamp: new Date().toISOString()
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
} catch (deleteError) {
|
|
951
|
-
console.error('Failed to delete PRD file:', deleteError);
|
|
952
|
-
return res.status(500).json({
|
|
953
|
-
error: 'Failed to delete PRD file',
|
|
954
|
-
message: deleteError.message
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
} catch (error) {
|
|
959
|
-
console.error('PRD delete error:', error);
|
|
960
|
-
res.status(500).json({
|
|
961
|
-
error: 'Failed to delete PRD file',
|
|
962
|
-
message: error.message
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
});
|
|
966
|
-
|
|
967
475
|
/**
|
|
968
476
|
* POST /api/taskmaster/init/:projectName
|
|
969
477
|
* Initialize TaskMaster in a project
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FetchHistoryOptions,
|
|
3
|
+
FetchHistoryResult,
|
|
4
|
+
LLMProvider,
|
|
5
|
+
McpScope,
|
|
6
|
+
NormalizedMessage,
|
|
7
|
+
ProviderAuthStatus,
|
|
8
|
+
ProviderMcpServer,
|
|
9
|
+
UpsertProviderMcpServerInput,
|
|
10
|
+
} from '@/shared/types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Main provider contract for CLI and SDK integrations.
|
|
14
|
+
*
|
|
15
|
+
* Each concrete provider owns its MCP/auth handlers plus the provider-specific
|
|
16
|
+
* logic for converting native events/history into the app's normalized shape.
|
|
17
|
+
*/
|
|
18
|
+
export interface IProvider {
|
|
19
|
+
readonly id: LLMProvider;
|
|
20
|
+
readonly mcp: IProviderMcp;
|
|
21
|
+
readonly auth: IProviderAuth;
|
|
22
|
+
readonly sessions: IProviderSessions;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Auth contract for one provider.
|
|
28
|
+
*/
|
|
29
|
+
export interface IProviderAuth {
|
|
30
|
+
/**
|
|
31
|
+
* Checks whether the provider is installed and has usable credentials.
|
|
32
|
+
*/
|
|
33
|
+
getStatus(): Promise<ProviderAuthStatus>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* MCP contract for one provider.
|
|
38
|
+
*/
|
|
39
|
+
export interface IProviderMcp {
|
|
40
|
+
listServers(options?: { workspacePath?: string }): Promise<Record<McpScope, ProviderMcpServer[]>>;
|
|
41
|
+
listServersForScope(scope: McpScope, options?: { workspacePath?: string }): Promise<ProviderMcpServer[]>;
|
|
42
|
+
upsertServer(input: UpsertProviderMcpServerInput): Promise<ProviderMcpServer>;
|
|
43
|
+
removeServer(
|
|
44
|
+
input: { name: string; scope?: McpScope; workspacePath?: string },
|
|
45
|
+
): Promise<{ removed: boolean; provider: LLMProvider; name: string; scope: McpScope }>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Session/history contract for one provider.
|
|
50
|
+
*/
|
|
51
|
+
export interface IProviderSessions {
|
|
52
|
+
normalizeMessage(raw: unknown, sessionId: string | null): NormalizedMessage[];
|
|
53
|
+
fetchHistory(sessionId: string, options?: FetchHistoryOptions): Promise<FetchHistoryResult>;
|
|
54
|
+
}
|