Compare commits
2 Commits
c0ac093f46
...
eec2050a32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eec2050a32 | ||
|
|
96fdf0dbd4 |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"description": "Local coordination layer for AI coding agents",
|
"description": "Local coordination layer for AI coding agents",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { watchEvents } from './commands/watch.js';
|
|||||||
import { startAgent } from './commands/start.js';
|
import { startAgent } from './commands/start.js';
|
||||||
import { workAgent } from './commands/work.js';
|
import { workAgent } from './commands/work.js';
|
||||||
import { startMcpServer } from '../mcp/server.js';
|
import { startMcpServer } from '../mcp/server.js';
|
||||||
|
import { printMcpConfig } from '../mcp/install.js';
|
||||||
import { loadConfig, saveConfig } from '../core/config.js';
|
import { loadConfig, saveConfig } from '../core/config.js';
|
||||||
import { findProjectRoot } from '../core/paths.js';
|
import { findProjectRoot } from '../core/paths.js';
|
||||||
import { discoverServer } from '../discovery.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 {
|
export function createProgram(cwd: string): Command {
|
||||||
const program = new Command('agenthub')
|
const program = new Command('agenthub')
|
||||||
.description('Local coordination layer for AI coding agents')
|
.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)');
|
.option('--server <url>', 'AgentHub server URL (env: AGENTHUB_SERVER)');
|
||||||
|
|
||||||
program
|
program
|
||||||
@ -484,9 +485,13 @@ export function createProgram(cwd: string): Command {
|
|||||||
|
|
||||||
// ─── mcp ───────────────────────────────────────────────────────────────────
|
// ─── mcp ───────────────────────────────────────────────────────────────────
|
||||||
program
|
program
|
||||||
.command('mcp')
|
.command('mcp [action]')
|
||||||
.description('Start the MCP server (stdio) — exposes hub tools to MCP-capable agents (Claude/Codex/Kimi)')
|
.description('Start the MCP server (stdio). `agenthub mcp install` prints per-CLI registration config.')
|
||||||
.action(async () => {
|
.action(async (action?: string) => {
|
||||||
|
if (action === 'install') {
|
||||||
|
printMcpConfig(cwd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await startMcpServer(cwd);
|
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 {
|
.board {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, minmax(180px, 1fr));
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
.column {
|
.column {
|
||||||
|
min-width: 0;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
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="done"] .col-label { color: var(--done); }
|
||||||
.column[data-column="cancelled"] .col-label { color: var(--cancelled); }
|
.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 {
|
.card {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
background: var(--raised);
|
background: var(--raised);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-left: 3px solid var(--accent);
|
border-left: 3px solid var(--accent);
|
||||||
@ -320,19 +324,23 @@ export function renderBoardHtml(projectName = 'AgentHub Project'): string {
|
|||||||
|
|
||||||
.timeline {
|
.timeline {
|
||||||
display: none;
|
display: none;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.card.expanded .timeline { display: block; }
|
.card.expanded .timeline { display: block; }
|
||||||
.tl-row {
|
.tl-row {
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: baseline;
|
grid-template-columns: auto auto minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
border-top: 1px solid rgba(48, 54, 61, .5);
|
border-top: 1px solid rgba(48, 54, 61, .5);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.tl-row:first-child { border-top: 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; }
|
.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-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-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-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-actor { color: var(--muted); font: 11px/1.4 var(--mono); min-width: 0; overflow-wrap: anywhere; }
|
||||||
.tl-summary { flex: 1; 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 { display: flex; gap: 4px; flex-wrap: wrap; }
|
.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-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); }
|
.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; }
|
.tl-loading, .tl-empty { color: var(--muted); font-size: 12px; padding: 2px 0; }
|
||||||
@ -625,12 +633,20 @@ ${columnSkeleton()}
|
|||||||
if (!cardsEl) return;
|
if (!cardsEl) return;
|
||||||
var expanded = {};
|
var expanded = {};
|
||||||
cardsEl.querySelectorAll('.card.expanded[data-id]').forEach(function(c) {
|
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>';
|
cardsEl.innerHTML = list.length ? list.map(taskCard).join('') : '<div class="empty">none</div>';
|
||||||
Object.keys(expanded).forEach(function(id) {
|
Object.keys(expanded).forEach(function(id) {
|
||||||
var card = cardsEl.querySelector('.card[data-id="' + 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();
|
updateTimers();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user