@agentprojectcontext/apx 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,127 @@
1
+ // apx status — show full system status at a glance.
2
+ // Daemon, super-agent, engines, telegram, projects.
3
+
4
+ import { readConfig } from "../../core/config.js";
5
+ import { http } from "../http.js";
6
+
7
+ const R = "\x1b[0m";
8
+ const B = "\x1b[1m";
9
+ const DI = "\x1b[2m";
10
+ const CY = "\x1b[36m";
11
+ const GR = "\x1b[32m";
12
+ const YE = "\x1b[33m";
13
+ const RE = "\x1b[31m";
14
+ const WH = "\x1b[97m";
15
+
16
+ const ok = (s) => `${GR}✓${R} ${s}`;
17
+ const off = (s) => `${DI}–${R} ${s}`;
18
+ const err = (s) => `${RE}✗${R} ${s}`;
19
+ const key = (s) => ` ${DI}${s.padEnd(16)}${R}`;
20
+ const val = (s) => `${WH}${s}${R}`;
21
+ const sec = (s) => `\n${B}${CY} ${s}${R}\n`;
22
+
23
+ export async function cmdStatus() {
24
+ const cfg = readConfig();
25
+
26
+ // ── Daemon ─────────────────────────────────────────────────────────────────
27
+ console.log(sec("Daemon"));
28
+ let daemonOk = false;
29
+ let daemonInfo = {};
30
+ try {
31
+ daemonInfo = await http.get("/health");
32
+ daemonOk = true;
33
+ } catch {}
34
+
35
+ if (daemonOk) {
36
+ console.log(` ${ok("running")} port ${val(daemonInfo.port || cfg.port)} v${val(daemonInfo.version || "?")} uptime ${val(formatUptime(daemonInfo.uptime_s))}`);
37
+ } else {
38
+ console.log(` ${err("stopped")} run: ${CY}apx daemon start${R}`);
39
+ }
40
+
41
+ // ── Super-agent ────────────────────────────────────────────────────────────
42
+ console.log(sec("Super-agent"));
43
+ const sa = cfg.super_agent || {};
44
+ if (sa.enabled) {
45
+ console.log(` ${ok("enabled")}`);
46
+ console.log(`${key("model")}${val(sa.model || "(not set)")}`);
47
+ if (sa.system) {
48
+ const preview = sa.system.length > 60 ? sa.system.slice(0, 60) + "…" : sa.system;
49
+ console.log(`${key("system")}${DI}${preview}${R}`);
50
+ }
51
+ if (cfg.identity?.agent_name) {
52
+ console.log(`${key("identity")}${val(cfg.identity.agent_name)}${cfg.identity.owner_name ? DI + " for " + cfg.identity.owner_name + R : ""}`);
53
+ }
54
+ } else {
55
+ console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
56
+ }
57
+
58
+ // ── Engines ────────────────────────────────────────────────────────────────
59
+ console.log(sec("Engines"));
60
+ const engines = cfg.engines || {};
61
+ const engineList = [
62
+ { id: "anthropic", label: "Anthropic", check: () => !!engines.anthropic?.api_key },
63
+ { id: "openai", label: "OpenAI", check: () => !!engines.openai?.api_key },
64
+ { id: "gemini", label: "Gemini", check: () => !!engines.gemini?.api_key },
65
+ { id: "ollama", label: "Ollama", check: () => true,
66
+ detail: () => engines.ollama?.base_url || "http://localhost:11434" },
67
+ ];
68
+
69
+ for (const e of engineList) {
70
+ const active = sa.model?.startsWith(e.id + ":");
71
+ const configured = e.check();
72
+ const detail = e.detail ? ` ${DI}${e.detail()}${R}` : "";
73
+ const marker = active ? `${YE}← active${R}` : "";
74
+ if (configured) {
75
+ console.log(` ${ok(e.label.padEnd(12))}${detail} ${marker}`);
76
+ } else {
77
+ console.log(` ${off(e.label.padEnd(12))}${DI}(no key)${R} ${marker}`);
78
+ }
79
+ }
80
+
81
+ // ── Telegram ───────────────────────────────────────────────────────────────
82
+ console.log(sec("Telegram"));
83
+ const tg = cfg.telegram || {};
84
+ if (tg.enabled && tg.bot_token) {
85
+ console.log(` ${ok("enabled")}`);
86
+ console.log(`${key("chat_id")}${val(tg.chat_id || "(not set)")}`);
87
+ console.log(`${key("bot_token")}${DI}${maskToken(tg.bot_token)}${R}`);
88
+ if (tg.channels?.length) {
89
+ console.log(`${key("channels")}${val(tg.channels.length + " configured")}`);
90
+ }
91
+ } else {
92
+ console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
93
+ }
94
+
95
+ // ── Projects ───────────────────────────────────────────────────────────────
96
+ console.log(sec("Projects"));
97
+ let projects = [];
98
+ if (daemonOk) {
99
+ try { projects = await http.get("/projects"); } catch {}
100
+ } else {
101
+ projects = (cfg.projects || []).map((p, i) => ({ id: i + 1, path: p.path, name: p.path?.split("/").pop() || "?", agents: "?" }));
102
+ }
103
+
104
+ const registered = projects.filter((p) => p.id !== 0);
105
+ if (registered.length === 0) {
106
+ console.log(` ${off("none registered")} run: ${CY}apx project add .${R}`);
107
+ } else {
108
+ for (const p of registered) {
109
+ const agents = p.agents !== "?" ? ` ${DI}${p.agents} agent${p.agents !== 1 ? "s" : ""}${R}` : "";
110
+ console.log(` ${DI}#${p.id}${R} ${val(p.name)}${agents} ${DI}${p.path}${R}`);
111
+ }
112
+ }
113
+
114
+ console.log();
115
+ }
116
+
117
+ function maskToken(token) {
118
+ if (!token || token.length < 12) return "***";
119
+ return token.slice(0, 6) + "…" + token.slice(-4);
120
+ }
121
+
122
+ function formatUptime(s) {
123
+ if (!s) return "?";
124
+ if (s < 60) return `${s}s`;
125
+ if (s < 3600) return `${Math.floor(s / 60)}m`;
126
+ return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`;
127
+ }
package/src/cli/index.js CHANGED
@@ -73,6 +73,7 @@ import { cmdIdentity } from "./commands/identity.js";
73
73
  import { cmdCommandList, cmdCommandShow } from "./commands/command.js";
74
74
  import { cmdUpdate } from "./commands/update.js";
75
75
  import { cmdSetup } from "./commands/setup.js";
76
+ import { cmdStatus } from "./commands/status.js";
76
77
  import { checkForUpdate } from "../core/update-check.js";
77
78
  import { mascot } from "../core/mascot.js";
78
79
  import {
@@ -91,127 +92,144 @@ const VERSION = JSON.parse(
91
92
  fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")
92
93
  ).version;
93
94
 
94
- const HELP = `apx Agent Project Context
95
-
96
- Usage:
97
- apx <command> [<subcommand>] [args] [--flags]
98
-
99
- Bootstrap:
100
- apx init [path] [--name "<name>"] initialize an APC project
101
- apx project add [path] register a project with the daemon
102
- apx project list
103
- apx project remove <path|id>
104
- apx project rebuild [<path|id>] rebuild project index from filesystem
105
- apx add project [path] alias for: apx project add
106
-
107
- Agents:
108
- apx agent add <slug> [--role R] [--model M] [--skills a,b] [--language es-AR] [--description D]
109
- apx agent list
110
- apx agent get <slug>
111
-
112
- Memory:
113
- apx memory <slug> print memory.md
114
- apx memory <slug> --append "<note>" append under "## Recent context"
115
- apx memory <slug> --replace replace from stdin
116
-
117
- Sessions:
118
- apx session new <slug> --title "<title>" [--task-ref REF] [--body - | --body "<text>"]
119
- apx session list [<slug>] [--last N]
120
- apx session get <id> [--body]
121
- apx session update <id> [--status S] [--result R] [--title T] [--task-ref REF]
122
- apx session close <id> [--result "<text>"]
123
- apx session check anti-collision: exit 1 if active session
124
- apx session close-stale auto-close sessions older than 1h
125
- apx session resume <id> [--summary] [--full] read APC session + external transcript (Claude Code, etc.)
126
- apx session compact <agent> [--conversation <id>] collapse conversation history into a dense summary
127
-
128
- MCPs:
129
- apx mcp list
130
- apx mcp add <name> --command <cmd> [args...] [--env KEY=VAL]
131
- apx mcp remove <name>
132
- apx mcp enable <name>
133
- apx mcp disable <name>
134
- apx mcp run <name> <tool> [<json-args>] call a tool through the daemon
135
- apx mcp tools <name> list tools (v0.2)
136
- apx mcp check audit multi-source merge (v0.2)
137
-
138
- Daemon:
139
- apx daemon start
140
- apx daemon stop
141
- apx daemon status
142
- apx daemon logs [--tail N]
143
-
144
- Telegram:
145
- apx telegram send "<text>" [--chat <id>]
146
- apx telegram status
147
- apx telegram setup
148
-
149
- Messages:
150
- apx messages tail [--agent <slug>] [--channel <ch>] [-n 50] [--global]
151
- global channels (telegram, direct, whatsapp) → ~/.apx/messages/<ch>/
152
- project channels (runtime, a2a, exec) → ~/.apx/projects/<id>/messages/
153
- apx messages search "<query>"
154
-
155
- LLM engines (v0.2):
156
- apx exec <agent> "<prompt>" [--model <id>] [--max-tokens N] [--temperature T]
157
- one-shot LLM call (Anthropic/OpenAI/Ollama/Gemini/mock)
158
- apx chat <agent> [--conversation <id>] [--model <id>] interactive REPL
159
- apx conversations list <agent>
160
- apx conversations get <agent> <id>
161
-
162
- External agent runtimes (v0.3):
163
- apx run <agent> --runtime <id> "<prompt>" [--timeout <s>]
164
- claude-code | codex | opencode | aider
165
- apx env detect which agent CLIs are installed
166
-
167
- Agent-to-agent (v0.4):
168
- apx send <from> <to> "<body>" [--deliver] log a2a message; --deliver runs target's engine
169
- apx connections <agent> mental map: who/how/when this agent talked
170
-
171
- Project config (.apc/config.json):
172
- apx config show [--effective | --only-overrides] show project overrides + effective config
173
- apx config set <key.path> <value> set a key in .apc/config.json (JSON-aware)
174
- apx config unset <key.path> remove a key from .apc/config.json
175
-
176
- Global flags:
177
- --project <name|id|path> pin commands to a specific project
178
- (or use sugar: apx project <name|id> <subcommand...>)
179
-
180
- Plugins:
181
- apx plugins list show loaded plugins and their status
182
- apx plugins status <id> detailed status of one plugin (e.g. telegram)
183
-
184
- Routines (per-project scheduled tasks):
185
- apx routine list list routines + next/last run
186
- apx routine add <name> --kind <K> --schedule <S> [--spec '<json>' | --K=V...]
187
- kinds: heartbeat | exec_agent | telegram | shell
188
- schedule: every:60s | every:5m | every:1h | once:<iso>
189
- apx routine get <name>
190
- apx routine run <name> run now (manual trigger, ignores schedule)
191
- apx routine enable <name> | apx routine disable <name>
192
- apx routine remove <name>
193
-
194
- Commands (.apc/commands/):
195
- apx command list list workflow commands
196
- apx command show <name> print command content
197
-
198
- Skills (IDE integration):
199
- apx skills add [<target>...] install APX skill into project IDE rule files
200
- targets: claude-code cursor windsurf copilot trae
201
- (omit targets to install all)
202
- apx skills add --global install to ~/.claude/skills/ ~/.cursor/skills/ ~/.agents/skills/
203
- picked up by Claude Code, Cursor, Codex, Antigravity globally
204
- apx skills list list skills in .apc/skills/
205
- apx skills status show which IDE targets are installed (project + global)
206
-
207
- Other:
208
- apx setup interactive setup wizard (alias: apx install)
209
- apx update check for updates and upgrade (alias: apx upgrade)
210
- apx --help
211
- apx --version
212
-
213
- Docs: https://agentprojectcontext.com
214
- `;
95
+ // ── ANSI helpers (help only) ─────────────────────────────────────────────────
96
+ const H = {
97
+ R: "\x1b[0m",
98
+ B: "\x1b[1m",
99
+ DI: "\x1b[2m",
100
+ CY: "\x1b[36m",
101
+ GR: "\x1b[32m",
102
+ YE: "\x1b[33m",
103
+ WH: "\x1b[97m",
104
+ MA: "\x1b[35m",
105
+ };
106
+ const hSec = (s) => `\n${H.B}${H.CY} ${s}${H.R}\n`;
107
+ const hCmd = (cmd, pad, desc) => ` ${H.WH}${cmd.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
108
+ const hSub = (sub, pad, desc) => ` ${H.CY}${sub.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
109
+ const hFlag = (f, pad, desc) => ` ${H.YE}${f.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
110
+
111
+ function buildHelp(version) {
112
+ return [
113
+ "",
114
+ ` ${H.B}${H.WH}apx${H.R} ${H.DI}Agent Project Context v${version}${H.R}`,
115
+ ` ${H.DI}Docs: https://agentprojectcontext.com${H.R}`,
116
+ "",
117
+ ` ${H.DI}Usage:${H.R} ${H.WH}apx${H.R} ${H.CY}<command>${H.R} ${H.DI}[subcommand] [args] [--flags]${H.R}`,
118
+ "",
119
+
120
+ hSec("Bootstrap"),
121
+ hCmd("apx init [path]", 36, "--name \"<name>\" initialize a new APC project"),
122
+ hCmd("apx setup", 36, "interactive wizard: provider model channels daemon (alias: install)"),
123
+ hCmd("apx status", 36, "full system status: daemon, super-agent, engines, telegram, projects"),
124
+ hCmd("apx update", 36, "check for updates and upgrade (alias: upgrade)"),
125
+ hCmd("apx project add [path]", 36, "register a project with the daemon"),
126
+ hCmd("apx project list", 36, ""),
127
+ hCmd("apx project remove <id>", 36, ""),
128
+ hCmd("apx project rebuild [id]", 36, "rebuild project index from filesystem"),
129
+ hCmd("apx add project [path]", 36, "alias for: apx project add"),
130
+
131
+ hSec("Agents"),
132
+ hCmd("apx agent add <slug>", 36, "--role R --model M --skills a,b --language es-AR --description D"),
133
+ hCmd("apx agent list", 36, ""),
134
+ hCmd("apx agent get <slug>", 36, ""),
135
+ hCmd("apx agent import", 36, ""),
136
+ hCmd("apx agent vault list", 36, ""),
137
+ hCmd("apx agent vault add", 36, ""),
138
+
139
+ hSec("Identity & Config"),
140
+ hCmd("apx identity show", 36, "print current agent identity (name, owner, personality)"),
141
+ hCmd("apx identity set <k> <v>", 36, "set identity field"),
142
+ hCmd("apx identity wizard", 36, "interactive identity setup"),
143
+ hCmd("apx config show", 36, "--effective --only-overrides"),
144
+ hCmd("apx config set <key> <val>", 36, "set key in .apc/config.json (JSON-aware)"),
145
+ hCmd("apx config unset <key>", 36, "remove key from .apc/config.json"),
146
+
147
+ hSec("Memory"),
148
+ hCmd("apx memory <slug>", 36, "print memory.md"),
149
+ hCmd("apx memory <slug> --append", 36, "\"<note>\" append under '## Recent context'"),
150
+ hCmd("apx memory <slug> --replace",36, "replace from stdin"),
151
+
152
+ hSec("Sessions"),
153
+ hCmd("apx session new <slug>", 36, "--title \"T\" [--task-ref REF] [--body -|\"text\"]"),
154
+ hCmd("apx session list [slug]", 36, "--last N"),
155
+ hCmd("apx session get <id>", 36, "--body"),
156
+ hCmd("apx session update <id>", 36, "--status S --result R --title T"),
157
+ hCmd("apx session close <id>", 36, "--result \"text\""),
158
+ hCmd("apx session check", 36, "anti-collision: exit 1 if active session"),
159
+ hCmd("apx session close-stale", 36, "auto-close sessions older than 1h"),
160
+ hCmd("apx session resume <id>", 36, "--summary --full (APC + Claude Code transcript)"),
161
+ hCmd("apx session compact <slug>", 36, "--conversation <id> collapse history into summary"),
162
+
163
+ hSec("MCPs"),
164
+ hCmd("apx mcp list", 36, ""),
165
+ hCmd("apx mcp add <name>", 36, "--command <cmd> [args] --env KEY=VAL"),
166
+ hCmd("apx mcp remove <name>", 36, ""),
167
+ hCmd("apx mcp enable/disable", 36, "<name>"),
168
+ hCmd("apx mcp run <name> <tool>", 36, "[<json-args>] call a tool through the daemon"),
169
+ hCmd("apx mcp tools <name>", 36, "list available tools"),
170
+ hCmd("apx mcp check", 36, "audit multi-source merge"),
171
+
172
+ hSec("Daemon"),
173
+ hCmd("apx daemon start", 36, ""),
174
+ hCmd("apx daemon stop", 36, ""),
175
+ hCmd("apx daemon status", 36, ""),
176
+ hCmd("apx daemon logs", 36, "--tail N"),
177
+
178
+ hSec("Telegram"),
179
+ hCmd("apx telegram send \"text\"", 36, "--chat <id>"),
180
+ hCmd("apx telegram status", 36, ""),
181
+ hCmd("apx telegram setup", 36, ""),
182
+
183
+ hSec("Messages"),
184
+ hCmd("apx messages tail", 36, "--agent <slug> --channel <ch> -n 50 --global"),
185
+ hCmd("apx messages search \"q\"", 36, ""),
186
+
187
+ hSec("LLM / Chat"),
188
+ hCmd("apx exec <agent> \"prompt\"",36, "--model <id> --max-tokens N --temperature T"),
189
+ hCmd("apx chat <agent>", 36, "--conversation <id> --model <id> (interactive REPL)"),
190
+ hCmd("apx conversations list", 36, "<agent>"),
191
+ hCmd("apx conversations get", 36, "<agent> <id>"),
192
+
193
+ hSec("Runtimes"),
194
+ hCmd("apx run <agent>", 36, "--runtime <id> \"prompt\" --timeout <s>"),
195
+ ` ${H.DI}runtimes: claude-code | codex | opencode | aider${H.R}`,
196
+ hCmd("apx env detect", 36, "which agent CLIs are installed"),
197
+
198
+ hSec("Agent-to-Agent"),
199
+ hCmd("apx send <from> <to> \"msg\"",36, "--deliver log A2A message; --deliver runs target engine"),
200
+ hCmd("apx connections <agent>", 36, "mental map: who/how/when this agent talked"),
201
+ hCmd("apx graph <agent>", 36, "alias for connections"),
202
+
203
+ hSec("Routines"),
204
+ hCmd("apx routine list", 36, "list routines + next/last run"),
205
+ hCmd("apx routine add <name>", 36, "--kind K --schedule S [--spec '{...}']"),
206
+ ` ${H.DI}kinds: heartbeat | exec_agent | telegram | shell${H.R}`,
207
+ ` ${H.DI}schedule: every:60s | every:5m | every:1h | once:<iso>${H.R}`,
208
+ hCmd("apx routine get <name>", 36, ""),
209
+ hCmd("apx routine run <name>", 36, "manual trigger (ignores schedule)"),
210
+ hCmd("apx routine enable <name>", 36, ""),
211
+ hCmd("apx routine disable <name>", 36, ""),
212
+ hCmd("apx routine remove <name>", 36, ""),
213
+
214
+ hSec("Commands & Skills"),
215
+ hCmd("apx command list", 36, "list workflow commands (.apc/commands/)"),
216
+ hCmd("apx command show <name>", 36, "print command content"),
217
+ hCmd("apx skills add [targets]", 36, "install skill into IDE rule files (claude-code cursor windsurf copilot trae)"),
218
+ hCmd("apx skills add --global", 36, "install to ~/.claude/skills/ etc."),
219
+ hCmd("apx skills list", 36, ""),
220
+ hCmd("apx skills status", 36, "show which IDE targets are installed"),
221
+
222
+ hSec("Plugins"),
223
+ hCmd("apx plugins list", 36, "show loaded plugins and their status"),
224
+ hCmd("apx plugins status <id>", 36, "detailed status of one plugin (e.g. telegram)"),
225
+
226
+ hSec("Flags"),
227
+ hFlag("--project <name|id|path>", 36, "pin commands to a specific project"),
228
+ hFlag("--help", 36, "show this help"),
229
+ hFlag("--version", 36, "print version"),
230
+ "",
231
+ ].join("\n") + "\n";
232
+ }
215
233
 
216
234
  function parseArgs(argv) {
217
235
  const args = { _: [], flags: {} };
@@ -255,8 +273,18 @@ function die(msg, code = 1) {
255
273
 
256
274
  const argv = process.argv.slice(2);
257
275
 
276
+ // ── Global error safety net ──────────────────────────────────────────────────
277
+ // Catches any unhandled promise rejection or sync exception that escapes
278
+ // the main try/catch — shows the panda instead of a raw Node.js stack trace.
279
+ process.on("uncaughtException", (err) => {
280
+ die(err && err.message ? err.message : String(err));
281
+ });
282
+ process.on("unhandledRejection", (reason) => {
283
+ die(reason instanceof Error ? reason.message : String(reason));
284
+ });
285
+
258
286
  if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
259
- process.stdout.write(HELP);
287
+ process.stdout.write(buildHelp(VERSION));
260
288
  process.exit(0);
261
289
  }
262
290
 
@@ -483,6 +511,10 @@ async function dispatch(cmd, rest) {
483
511
  break;
484
512
  }
485
513
 
514
+ case "status":
515
+ await cmdStatus();
516
+ return; // skip checkForUpdate after status (avoid noise)
517
+
486
518
  case "setup":
487
519
  case "install":
488
520
  await cmdSetup();