agenthub/src/core/paths.ts
chahinebrini 07afa717ac feat(messaging): agent messaging — agenthub_message / agenthub_inbox (cross-agent via MCP)
Direct messages between agents through the hub — architect↔agent and agent↔agent
questions, clarifications and pings that handoffs/memories don't cover. Built as
MCP tools so kimi + codex get it too (not Claude-only), plus REST + CLI.

- Message entity (MSG-NNNN): schema, counter, messages/ dir, messageService
  (createMessage / listInbox / listMessages / markMessageRead).
- REST: POST /messages, GET /messages[?agent&unread], POST /messages/:id/read,
  with SSE emit. fsWatch + statusRefresh wired ('message' events; status skips them).
- MCP: agenthub_message {from,to,text,taskId?} + agenthub_inbox {agent,unreadOnly?}.
  agenthub_work now surfaces + drains unread messages and wakes on message events,
  so an agent sees messages inside its work loop. (18 tools total.)
- CLI: `agenthub message <to> <text> --from <agent>` + `agenthub inbox --agent`.
  watch stream shows "Message" events (architect-visible).
- Architect-visible by design: the hub stays the coordination point.

Bump 0.7.5 -> 0.8.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 03:20:38 +02:00

45 lines
1.4 KiB
TypeScript

import { existsSync } from 'fs';
import { dirname, join, parse } from 'path';
export type EntityType = 'tasks' | 'handoffs' | 'decisions' | 'memory' | 'messages' | 'status';
export function getAgentHubDir(cwd: string = process.cwd()): string {
return join(cwd, '.agenthub');
}
/**
* Walk up from `startCwd` to find the nearest directory that contains an
* initialized AgentHub project (`.agenthub/agenthub.config.json`).
* Returns the project root, or `undefined` if none is found.
* This makes the CLI usable from any subdirectory of the project.
*/
export function findProjectRoot(startCwd: string = process.cwd()): string | undefined {
let dir = startCwd;
const { root } = parse(dir);
while (true) {
if (existsSync(join(dir, '.agenthub', 'agenthub.config.json'))) return dir;
if (dir === root) return undefined;
dir = dirname(dir);
}
}
export function getEntityDir(cwd: string, type: EntityType): string {
return join(getAgentHubDir(cwd), type);
}
export function getCounterPath(cwd: string): string {
return join(getAgentHubDir(cwd), '.counter.json');
}
export function getConfigPath(cwd: string): string {
return join(getAgentHubDir(cwd), 'agenthub.config.json');
}
export function getIndexPath(cwd: string): string {
return join(getAgentHubDir(cwd), '.index.sqlite');
}
export function getStatusPath(cwd: string): string {
return join(getEntityDir(cwd, 'status'), 'latest.md');
}