Compare commits
2 Commits
c0ac093f46
...
eec2050a32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eec2050a32 | ||
|
|
96fdf0dbd4 |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "agenthub",
|
||||
"version": "0.7.2",
|
||||
"version": "0.7.3",
|
||||
"description": "Local coordination layer for AI coding agents",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -12,6 +12,7 @@ import { watchEvents } from './commands/watch.js';
|
||||
import { startAgent } from './commands/start.js';
|
||||
import { workAgent } from './commands/work.js';
|
||||
import { startMcpServer } from '../mcp/server.js';
|
||||
import { printMcpConfig } from '../mcp/install.js';
|
||||
import { loadConfig, saveConfig } from '../core/config.js';
|
||||
import { findProjectRoot } from '../core/paths.js';
|
||||
import { discoverServer } from '../discovery.js';
|
||||
@ -115,7 +116,7 @@ async function runRemote(serverUrl: string, fn: () => Promise<void>): Promise<vo
|
||||
export function createProgram(cwd: string): Command {
|
||||
const program = new Command('agenthub')
|
||||
.description('Local coordination layer for AI coding agents')
|
||||
.version('0.7.2')
|
||||
.version('0.7.3')
|
||||
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
||||
|
||||
program
|
||||
@ -484,9 +485,13 @@ export function createProgram(cwd: string): Command {
|
||||
|
||||
// ─── mcp ───────────────────────────────────────────────────────────────────
|
||||
program
|
||||
.command('mcp')
|
||||
.description('Start the MCP server (stdio) — exposes hub tools to MCP-capable agents (Claude/Codex/Kimi)')
|
||||
.action(async () => {
|
||||
.command('mcp [action]')
|
||||
.description('Start the MCP server (stdio). `agenthub mcp install` prints per-CLI registration config.')
|
||||
.action(async (action?: string) => {
|
||||
if (action === 'install') {
|
||||
printMcpConfig(cwd);
|
||||
return;
|
||||
}
|
||||
await startMcpServer(cwd);
|
||||
});
|
||||
|
||||
|
||||
59
src/mcp/install.ts
Normal file
59
src/mcp/install.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { findProjectRoot } from '../core/paths.js';
|
||||
import { loadConfig } from '../core/config.js';
|
||||
|
||||
/**
|
||||
* Print ready-to-paste MCP registration config for each agent CLI, with the
|
||||
* hub URL filled in. The MCP server itself is `agenthub mcp` (stdio) — every
|
||||
* client just needs to know to spawn it + which hub to talk to.
|
||||
*/
|
||||
export function printMcpConfig(cwd: string): void {
|
||||
const root = findProjectRoot(cwd) ?? cwd;
|
||||
let serverUrl = process.env.AGENTHUB_SERVER || undefined;
|
||||
if (!serverUrl) {
|
||||
try {
|
||||
serverUrl = loadConfig(root).serverUrl;
|
||||
} catch {
|
||||
/* no project config */
|
||||
}
|
||||
}
|
||||
const url = serverUrl ?? 'http://127.0.0.1:3377';
|
||||
|
||||
const out = (s: string) => process.stdout.write(s + '\n');
|
||||
|
||||
out('');
|
||||
out('AgentHub MCP — registration');
|
||||
out('═══════════════════════════');
|
||||
out(`Hub URL: ${url}`);
|
||||
out('(On the Windows machine use the Mac\'s LAN IP, e.g. http://192.168.178.30:3377)');
|
||||
out('');
|
||||
out('The MCP server is `agenthub mcp` (stdio). Register it once per agent CLI:');
|
||||
out('');
|
||||
|
||||
out('── Claude Code ──────────────────────────────────────────────');
|
||||
out(` claude mcp add agenthub --env AGENTHUB_SERVER=${url} -- agenthub mcp`);
|
||||
out(' (or add the .mcp.json block below to the project)');
|
||||
out('');
|
||||
|
||||
out('── .mcp.json (project root — Claude Code / generic) ─────────');
|
||||
out(JSON.stringify(
|
||||
{ mcpServers: { agenthub: { command: 'agenthub', args: ['mcp'], env: { AGENTHUB_SERVER: url } } } },
|
||||
null,
|
||||
2,
|
||||
));
|
||||
out('');
|
||||
|
||||
out('── Codex CLI (~/.codex/config.toml) ─────────────────────────');
|
||||
out(' [mcp_servers.agenthub]');
|
||||
out(' command = "agenthub"');
|
||||
out(' args = ["mcp"]');
|
||||
out(` env = { AGENTHUB_SERVER = "${url}" }`);
|
||||
out('');
|
||||
|
||||
out('── Kimi CLI ─────────────────────────────────────────────────');
|
||||
out(' Add a stdio MCP server via Kimi\'s MCP config with the same shape:');
|
||||
out(' command: agenthub args: ["mcp"] env: { AGENTHUB_SERVER: ' + url + ' }');
|
||||
out('');
|
||||
out('After registering, the agent calls the tools (agenthub_work, agenthub_task_review, …)');
|
||||
out('instead of CLI strings. Requires agenthub >= 0.7.0 on each machine.');
|
||||
out('');
|
||||
}
|
||||
@ -183,11 +183,12 @@ export function renderBoardHtml(projectName = 'AgentHub Project'): string {
|
||||
|
||||
.board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(180px, 1fr));
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
}
|
||||
.column {
|
||||
min-width: 0;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
@ -222,8 +223,11 @@ export function renderBoardHtml(projectName = 'AgentHub Project'): string {
|
||||
.column[data-column="done"] .col-label { color: var(--done); }
|
||||
.column[data-column="cancelled"] .col-label { color: var(--cancelled); }
|
||||
|
||||
.cards { display: flex; flex-direction: column; gap: 8px; }
|
||||
.cards { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
|
||||
.card {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
background: var(--raised);
|
||||
border: 1px solid var(--border);
|
||||
border-left: 3px solid var(--accent);
|
||||
@ -320,19 +324,23 @@ export function renderBoardHtml(projectName = 'AgentHub Project'): string {
|
||||
|
||||
.timeline {
|
||||
display: none;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
margin-top: 9px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.card.expanded .timeline { display: block; }
|
||||
.tl-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto minmax(0, 1fr);
|
||||
align-items: start;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 0;
|
||||
border-top: 1px solid rgba(48, 54, 61, .5);
|
||||
font-size: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.tl-row:first-child { border-top: 0; }
|
||||
.tl-when { color: var(--muted); font: 11px/1.4 var(--mono); white-space: nowrap; min-width: 52px; }
|
||||
@ -347,9 +355,9 @@ export function renderBoardHtml(projectName = 'AgentHub Project'): string {
|
||||
.tl-kind-handoff { background: rgba(88, 166, 255, .12); color: var(--accent); border-color: rgba(88, 166, 255, .25); }
|
||||
.tl-kind-result { background: rgba(34, 197, 94, .12); color: var(--done); border-color: rgba(34, 197, 94, .25); }
|
||||
.tl-kind-status { background: rgba(210, 153, 34, .12); color: var(--review); border-color: rgba(210, 153, 34, .25); }
|
||||
.tl-actor { color: var(--muted); font: 11px/1.4 var(--mono); white-space: nowrap; }
|
||||
.tl-summary { flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
||||
.tl-meta { display: flex; gap: 4px; flex-wrap: wrap; }
|
||||
.tl-actor { color: var(--muted); font: 11px/1.4 var(--mono); min-width: 0; overflow-wrap: anywhere; }
|
||||
.tl-summary { grid-column: 1 / -1; min-width: 0; max-width: 100%; overflow-wrap: anywhere; word-break: break-word; white-space: normal; }
|
||||
.tl-meta { grid-column: 1 / -1; display: flex; gap: 4px; flex-wrap: wrap; min-width: 0; }
|
||||
.badge.tl-tokens { color: #A371F7; border-color: rgba(163, 113, 247, .25); background: rgba(163, 113, 247, .12); }
|
||||
.badge.tl-dur { color: #FFA657; border-color: rgba(255, 166, 87, .25); background: rgba(255, 166, 87, .12); }
|
||||
.tl-loading, .tl-empty { color: var(--muted); font-size: 12px; padding: 2px 0; }
|
||||
@ -625,12 +633,20 @@ ${columnSkeleton()}
|
||||
if (!cardsEl) return;
|
||||
var expanded = {};
|
||||
cardsEl.querySelectorAll('.card.expanded[data-id]').forEach(function(c) {
|
||||
expanded[c.dataset.id] = true;
|
||||
var timeline = c.querySelector('.timeline');
|
||||
expanded[c.dataset.id] = timeline ? timeline.innerHTML : '';
|
||||
});
|
||||
cardsEl.innerHTML = list.length ? list.map(taskCard).join('') : '<div class="empty">none</div>';
|
||||
Object.keys(expanded).forEach(function(id) {
|
||||
var card = cardsEl.querySelector('.card[data-id="' + id + '"]');
|
||||
if (card) card.classList.add('expanded');
|
||||
if (!card) return;
|
||||
card.classList.add('expanded');
|
||||
var timeline = card.querySelector('.timeline');
|
||||
if (timeline && expanded[id]) {
|
||||
timeline.innerHTML = expanded[id];
|
||||
} else {
|
||||
loadTimeline(card, id);
|
||||
}
|
||||
});
|
||||
});
|
||||
updateTimers();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user