feat: add Cobra CLI, LSP/MCP registries, workflow engine, and enriched dashboard
All checks were successful
Beta Release / beta (push) Successful in 2m24s
All checks were successful
Beta Release / beta (push) Successful in 2m24s
Major changes: - Refactor CLI entry point to Cobra commands (root, setup, scan, doctor, install, update, lsp, mcp, skills, config, version) - Add LSP registry with health checks, auto-install, and editor config generation - Add MCP registry with editor detection, status tracking, and per-editor configuration - Add workflow engine with planner and step execution for automated task chains - Add conversation search, export (Markdown/JSON), and detailed token counting - Add streaming shell chat handler with tool call/result events - Add skill validation, dry-run testing, and export endpoints - Enrich dashboard with Tools/Activity/Status tabs and tool cards grid - Add PRD documentation - Complete i18n for both EN and FR 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
@@ -26,6 +26,17 @@ const api = {
|
||||
runScan: () => request('/scan', { method: 'POST' }),
|
||||
installTools: (tools) => request('/install', { method: 'POST', body: JSON.stringify({ tools }) }),
|
||||
configureMCP: () => request('/mcp/configure', { method: 'POST' }),
|
||||
configureMCPForEditor: (editor) => request('/mcp/configure', { method: 'POST', body: JSON.stringify({ editor }) }),
|
||||
getMCPStatus: () => request('/mcp/status'),
|
||||
getMCPRegistry: () => request('/mcp/registry'),
|
||||
getLSPHealth: () => request('/lsp/health'),
|
||||
autoInstallLSP: (projectDir) => request('/lsp/auto-install', { method: 'POST', body: JSON.stringify({ project_dir: projectDir || '' }) }),
|
||||
generateLSPConfig: (editor, names) => request('/lsp/editor-config', { method: 'POST', body: JSON.stringify({ editor, names }) }),
|
||||
validateSkill: (name) => request('/skills/validate', { method: 'POST', body: JSON.stringify({ name }) }),
|
||||
testSkill: (name, sampleTask) => request('/skills/test', { method: 'POST', body: JSON.stringify({ name, sample_task: sampleTask || '' }) }),
|
||||
exportSkill: (name) => request('/skills/export', { method: 'POST', body: JSON.stringify({ name }) }),
|
||||
importSkill: (path) => request('/skills/import', { method: 'POST', body: JSON.stringify({ import_path: path }) }),
|
||||
getDashboardStatus: () => request('/dashboard/status'),
|
||||
savePreferences: (prefs) => request('/preferences', { method: 'PUT', body: JSON.stringify(prefs) }),
|
||||
saveProfile: (profile) => request('/config/profile', { method: 'PUT', body: JSON.stringify(profile) }),
|
||||
saveProvider: (provider) => request('/config/provider', { method: 'PUT', body: JSON.stringify(provider) }),
|
||||
@@ -84,6 +95,66 @@ const api = {
|
||||
}).catch(reject)
|
||||
})
|
||||
},
|
||||
sendShellChat: (message, context = {}, stream = true, onChunk) => {
|
||||
const payload = {
|
||||
message,
|
||||
context: context.context || '',
|
||||
history: context.history || [],
|
||||
cwd: context.cwd || '',
|
||||
platform: context.platform || '',
|
||||
stream,
|
||||
}
|
||||
if (!stream) {
|
||||
return request('/shell/chat', { method: 'POST', body: JSON.stringify(payload) })
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`${API_BASE}/shell/chat`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
}).then(async (res) => {
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({ error: res.statusText }))
|
||||
reject(new Error(err.error || res.statusText))
|
||||
return
|
||||
}
|
||||
const reader = res.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let full = ''
|
||||
let toolCalls = []
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
const text = decoder.decode(value, { stream: true })
|
||||
for (const line of text.split('\n')) {
|
||||
if (!line.startsWith('data: ')) continue
|
||||
try {
|
||||
const data = JSON.parse(line.slice(6))
|
||||
if (data.error) { reject(new Error(data.error)); return }
|
||||
if (data.done) {
|
||||
resolve({ content: full, tool_calls: toolCalls })
|
||||
return
|
||||
}
|
||||
if (data.content) {
|
||||
full += data.content
|
||||
if (onChunk) onChunk(full, data)
|
||||
} else if (data.tool_call) {
|
||||
toolCalls.push(data.tool_call)
|
||||
if (onChunk) onChunk(full, data, toolCalls)
|
||||
} else if (data.tool_result) {
|
||||
const idx = toolCalls.findIndex(tc => tc.tool_call_id === data.tool_result.id)
|
||||
if (idx >= 0) {
|
||||
toolCalls[idx].result = data.tool_result
|
||||
}
|
||||
if (onChunk) onChunk(full, data, toolCalls)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
resolve({ content: full, tool_calls: toolCalls })
|
||||
}).catch(reject)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export default api
|
||||
|
||||
Reference in New Issue
Block a user