Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@archon-claw/cli

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@archon-claw/cli - npm Package Compare versions

Comparing version
0.9.0
to
0.10.0
+18
-14
dist/cli.js

@@ -64,25 +64,29 @@ #!/usr/bin/env node

}
const agents = await loadAgents(agentDirs, "warn");
if (agents.size === 0) {
throw new Error(`No valid agents found in ${absDir}`);
}
const { agents, errors } = await loadAgents(agentDirs, "warn");
const entry = opts.entry ?? loadProjectConfig(absDir)?.entry;
if (entry && !agents.has(entry)) {
// Only fatal if entry is specified but not found — no fallback
throw new Error(`Entry agent "${entry}" not found. Available agents: ${[...agents.keys()].join(", ")}`);
}
const port = parseInt(opts.port, 10);
console.log("Agents loaded:");
for (const [id, entry] of agents) {
const { config } = entry;
const mcpCount = config.mcpManager ? config.mcpManager.getServerNames().length : 0;
console.log(` [${id}] Model: ${config.model.provider}/${config.model.model}` +
` Tools: ${config.tools.length}` +
(mcpCount > 0 ? ` MCP: ${mcpCount} servers` : "") +
` Skills: ${Object.keys(config.skills).length}`);
if (agents.size > 0) {
console.log("Agents loaded:");
for (const [id, entry] of agents) {
const { config } = entry;
const mcpCount = config.mcpManager ? config.mcpManager.getServerNames().length : 0;
console.log(` [${id}] Model: ${config.model.provider}/${config.model.model}` +
` Tools: ${config.tools.length}` +
(mcpCount > 0 ? ` MCP: ${mcpCount} servers` : "") +
` Skills: ${Object.keys(config.skills).length}`);
}
}
if (errors.length > 0) {
console.warn(`\n${errors.length} agent(s) failed validation (errors exposed via GET /api/health)`);
}
// With --watch: pass getter function so server always sees latest agents
// Without --watch: pass static Map (no file watching overhead)
const serverOpts = { entry, errors };
const server = opts.watch
? createServer(() => agents, port, { entry })
: createServer(agents, port, { entry });
? createServer(() => agents, port, serverOpts)
: createServer(agents, port, serverOpts);
let watchHandle;

@@ -89,0 +93,0 @@ if (opts.watch) {

@@ -149,24 +149,2 @@ import fs from "node:fs/promises";

}
// Build tool template variables (object map by name, no array form)
const toolMap = {};
for (const t of tools) {
toolMap[t.name] = { name: t.name, description: t.description };
}
// Build mcp template variable: mcp.{serverName}.tools (object map) + mcp.servers
const mcpVar = { servers: [] };
if (mcpManager) {
const byServer = mcpManager.getToolsByServer();
const servers = [];
for (const serverName of mcpManager.getServerNames()) {
const serverTools = byServer[serverName] ?? [];
const serverToolMap = {};
for (const t of serverTools) {
serverToolMap[t.name] = { name: t.name, description: t.description };
}
const serverObj = { name: serverName, tools: serverToolMap };
mcpVar[serverName] = serverObj;
servers.push(serverObj);
}
mcpVar.servers = servers;
}
// Inject built-in skill_load tool when skills/ directory has content

@@ -202,16 +180,9 @@ if (Object.keys(skills).length > 0) {

}
// Rebuild tool template variables after potential skill_load injection
const toolMap2 = {};
for (const t of tools) {
toolMap2[t.name] = { name: t.name, description: t.description };
}
// Load and render system prompt (after tools/MCP so they're available in template)
// Load and render system prompt (datasets available as template variables)
const promptRaw = await fs.readFile(path.join(absDir, "system-prompt.md"), "utf-8");
const engine = new Liquid();
const templateVars = { ...datasets, tools: toolMap2, mcp: mcpVar };
const systemPrompt = (await engine.parseAndRender(promptRaw, templateVars)).trim();
const systemPrompt = (await engine.parseAndRender(promptRaw, datasets)).trim();
return {
systemPrompt, model: model, tools, toolImpls, skills, mcpManager, agentDir: absDir, toolUIs,
_promptTemplate: promptRaw, _templateVars: templateVars,
};
}

@@ -12,4 +12,4 @@ import path from "node:path";

// Initial load of all agents
const currentAgents = await loadAgents(agentDirs, "dev");
if (currentAgents.size === 0) {
const { agents: currentAgents, errors } = await loadAgents(agentDirs, "dev");
if (currentAgents.size === 0 && errors.length === 0) {
throw new Error(`No valid agents found in ${absDir}`);

@@ -21,3 +21,3 @@ }

// Start server with getter — every request reads latest agents
const server = createServer(() => currentAgents, opts.port, { entry: opts.entry });
const server = createServer(() => currentAgents, opts.port, { entry: opts.entry, errors });
printBanner(currentAgents, opts.port, absDir);

@@ -24,0 +24,0 @@ // Open browser (skip in SSH)

import http from "node:http";
import type { AgentConfig } from "./types.js";
import type { AgentConfig, AgentLoadError } from "./types.js";
import type { SessionStore } from "./session.js";

@@ -13,4 +13,5 @@ export interface AgentEntry {

entry?: string;
errors?: AgentLoadError[];
}
export declare function createServer(agents: AgentResolver, port: number, options?: ServerOptions): http.Server;
export {};

@@ -128,2 +128,3 @@ import http from "node:http";

const entryAgentId = options?.entry;
const loadErrors = options?.errors ?? [];
const server = http.createServer(async (req, res) => {

@@ -168,3 +169,13 @@ const agentMap = resolveAgents();

if (method === "GET" && url.pathname === "/api/health") {
json(res, 200, { status: "ok" });
const agentIds = [...agentMap.keys()];
if (loadErrors.length > 0) {
json(res, agentIds.length > 0 ? 200 : 503, {
status: agentIds.length > 0 ? "degraded" : "error",
agents: agentIds,
errors: loadErrors,
});
}
else {
json(res, 200, { status: "ok", agents: agentIds });
}
return;

@@ -171,0 +182,0 @@ }

import fs from "node:fs/promises";
import path from "node:path";
import { Liquid } from "liquidjs";
import { runAgentLoop } from "./agent.js";

@@ -150,12 +149,3 @@ /** Check if an agent directory contains team.json */

});
// Re-render system prompt with {{ team.agents }} variable
if (config._promptTemplate && config._templateVars) {
const teamVar = { agents: buildTeamAgentList(agents, agentId) };
const engine = new Liquid();
config.systemPrompt = (await engine.parseAndRender(config._promptTemplate, {
...config._templateVars,
team: teamVar,
})).trim();
}
}
}

@@ -21,6 +21,2 @@ export type { JSONSchemaProperty, JSONSchema, ToolSchema, ModelConfig, } from "./schemas.js";

toolUIs: Set<string>;
/** @internal Raw Liquid template for re-rendering (e.g., after team injection) */
_promptTemplate?: string;
/** @internal Template variables for system prompt re-rendering */
_templateVars?: Record<string, unknown>;
}

@@ -100,2 +96,7 @@ /** OpenAI-compatible chat message */

};
/** Per-agent validation error for health reporting */
export interface AgentLoadError {
agent: string;
messages: string[];
}
/** Chat session state */

@@ -102,0 +103,0 @@ export interface Session {

import { type FSWatcher } from "chokidar";
import type { AgentLoadError } from "./types.js";
import type { AgentEntry } from "./server.js";

@@ -14,4 +15,9 @@ export interface AgentDir {

export declare function scanAgentDirs(agentsDir: string): AgentDir[];
/** Result of loading agents: successfully loaded agents + any validation errors */
export interface LoadAgentsResult {
agents: Map<string, AgentEntry>;
errors: AgentLoadError[];
}
/** Load all agents from scanned directories into a Map */
export declare function loadAgents(agentDirs: AgentDir[], logPrefix: string): Promise<Map<string, AgentEntry>>;
export declare function loadAgents(agentDirs: AgentDir[], logPrefix: string): Promise<LoadAgentsResult>;
/**

@@ -18,0 +24,0 @@ * Setup file watchers for agent directories.

@@ -29,2 +29,3 @@ import path from "node:path";

const agents = new Map();
const errors = [];
for (const { id, path: agentPath } of agentDirs) {

@@ -38,3 +39,9 @@ try {

catch (err) {
console.warn(`[${logPrefix}] Skipping "${id}": ${err instanceof Error ? err.message : err}`);
const message = err instanceof Error ? err.message : String(err);
console.warn(`[${logPrefix}] Skipping "${id}": ${message}`);
// Parse multi-line error messages (e.g. "Invalid agent directory:\n - [model.json] /provider Required")
const lines = message.split("\n").map((l) => l.replace(/^\s*-\s*/, "").trim()).filter(Boolean);
// Skip the header line if it's a wrapper message
const detailLines = lines.length > 1 && lines[0].endsWith(":") ? lines.slice(1) : lines;
errors.push({ agent: id, messages: detailLines });
}

@@ -44,3 +51,3 @@ }

await injectTeamTools(agents);
return agents;
return { agents, errors };
}

@@ -47,0 +54,0 @@ /**

{
"name": "@archon-claw/cli",
"version": "0.9.0",
"version": "0.10.0",
"description": "AI Agent CLI",

@@ -5,0 +5,0 @@ "type": "module",