@agentprojectcontext/apx 1.10.2 → 1.10.4
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/README.md +5 -1
- package/package.json +1 -1
- package/src/cli/commands/session.js +2 -2
- package/src/cli/commands/sys.js +97 -18
- package/src/cli/terminal-chat/renderer.js +15 -4
- package/src/core/apx-skill.md +16 -0
- package/src/core/runtime-skills/claude-code.md +68 -0
- package/src/core/runtime-skills/codex-cli.md +75 -0
- package/src/core/runtime-skills/opencode-cli.md +59 -0
- package/src/core/runtime-skills/openrouter.md +54 -0
- package/src/core/scaffold.js +16 -1
- package/src/daemon/db.js +13 -4
- package/src/daemon/runtimes/codex.js +2 -2
- package/src/daemon/super-agent-tools/tools/call-runtime.js +1 -1
- package/src/daemon/super-agent.js +5 -5
package/README.md
CHANGED
|
@@ -113,10 +113,14 @@ can distinguish Telegram users from APX agents and future subagents.
|
|
|
113
113
|
| Runtime | Description |
|
|
114
114
|
|---------|-------------|
|
|
115
115
|
| `claude-code` | Spawns Claude Code CLI with the agent's system prompt injected |
|
|
116
|
-
| `codex` | OpenAI Codex CLI |
|
|
116
|
+
| `codex` | OpenAI Codex CLI via non-interactive `codex exec --sandbox workspace-write --skip-git-repo-check` |
|
|
117
117
|
| `opencode` | OpenCode CLI |
|
|
118
118
|
| `aider` | Aider CLI |
|
|
119
119
|
|
|
120
|
+
Global APX skill installation also writes named helper skills for `codex-cli`, `claude-code`,
|
|
121
|
+
`opencode-cli`, and `openrouter`. They are intentionally narrow and should activate only when those
|
|
122
|
+
tools/providers are explicitly mentioned.
|
|
123
|
+
|
|
120
124
|
## Engines (for `apx exec`)
|
|
121
125
|
|
|
122
126
|
Configured in `~/.apx/config.json`:
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { findApfRoot, readAgents } from "../../core/parser.js";
|
|
4
4
|
import { getOrCreateApxId } from "../../core/scaffold.js";
|
|
5
5
|
import { generateSessionId } from "../../core/session-store.js";
|
|
6
|
-
import { ensureProjectStorage } from "../../core/config.js";
|
|
6
|
+
import { projectStorageRoot, ensureProjectStorage } from "../../core/config.js";
|
|
7
7
|
import { http } from "../http.js";
|
|
8
8
|
import { resolveProjectId } from "./project.js";
|
|
9
9
|
|
|
@@ -18,7 +18,7 @@ function requireRoot() {
|
|
|
18
18
|
function requireStorageRoot(root) {
|
|
19
19
|
const apxId = getOrCreateApxId(root);
|
|
20
20
|
if (!apxId) throw new Error("could not resolve APX project storage id");
|
|
21
|
-
return
|
|
21
|
+
return projectStorageRoot(apxId);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const nowIso = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
package/src/cli/commands/sys.js
CHANGED
|
@@ -32,10 +32,12 @@ export async function cmdSys(args) {
|
|
|
32
32
|
hasStarted: false,
|
|
33
33
|
sessionTitle: "",
|
|
34
34
|
usage: { input: 0, output: 0, percent: 0 },
|
|
35
|
+
chatScrollOffset: 0,
|
|
35
36
|
transcript: [],
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
const previousMessages = [];
|
|
40
|
+
const pendingPrompts = [];
|
|
39
41
|
let restored = false;
|
|
40
42
|
let isRequesting = false;
|
|
41
43
|
|
|
@@ -74,8 +76,6 @@ export async function cmdSys(args) {
|
|
|
74
76
|
renderScreen();
|
|
75
77
|
|
|
76
78
|
process.stdin.on("keypress", async (str, key) => {
|
|
77
|
-
if (isRequesting) return;
|
|
78
|
-
|
|
79
79
|
if (key.ctrl && key.name === "c") {
|
|
80
80
|
close();
|
|
81
81
|
}
|
|
@@ -102,9 +102,16 @@ export async function cmdSys(args) {
|
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
if (handleScrollKey(key, state, renderScreen)) return;
|
|
106
|
+
|
|
105
107
|
if (isReturnKey(key)) {
|
|
108
|
+
if (isRequesting) {
|
|
109
|
+
queuePrompt(state, pendingPrompts, renderScreen);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
106
113
|
isRequesting = true;
|
|
107
|
-
await
|
|
114
|
+
await submitPromptQueue(pid, state, previousMessages, pendingPrompts, renderScreen, close);
|
|
108
115
|
isRequesting = false;
|
|
109
116
|
return;
|
|
110
117
|
}
|
|
@@ -117,6 +124,37 @@ export function isReturnKey(key) {
|
|
|
117
124
|
return key?.name === "return" || key?.name === "enter";
|
|
118
125
|
}
|
|
119
126
|
|
|
127
|
+
export function handleScrollKey(key, state, renderScreen) {
|
|
128
|
+
if (!state.hasStarted || !key) return false;
|
|
129
|
+
const pageSize = key.name === "pageup" || key.name === "pagedown" ? 8 : 3;
|
|
130
|
+
|
|
131
|
+
if (key.name === "pageup" || key.name === "up" || (key.ctrl && key.name === "up")) {
|
|
132
|
+
state.chatScrollOffset = Math.min(100000, (state.chatScrollOffset || 0) + pageSize);
|
|
133
|
+
renderScreen();
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (key.name === "pagedown" || key.name === "down" || (key.ctrl && key.name === "down")) {
|
|
138
|
+
state.chatScrollOffset = Math.max(0, (state.chatScrollOffset || 0) - pageSize);
|
|
139
|
+
renderScreen();
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (key.meta && key.name === "up") {
|
|
144
|
+
state.chatScrollOffset = Math.min(100000, (state.chatScrollOffset || 0) + 20);
|
|
145
|
+
renderScreen();
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (key.meta && key.name === "down") {
|
|
150
|
+
state.chatScrollOffset = 0;
|
|
151
|
+
renderScreen();
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
120
158
|
async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
121
159
|
if (key.name === "up") {
|
|
122
160
|
state.paletteSelection = Math.max(0, state.paletteSelection - 1);
|
|
@@ -276,18 +314,46 @@ export function handleEditingKey(str, key, state, renderScreen) {
|
|
|
276
314
|
return false;
|
|
277
315
|
}
|
|
278
316
|
|
|
279
|
-
|
|
317
|
+
function queuePrompt(state, pendingPrompts, renderScreen) {
|
|
280
318
|
const text = state.inputText.trim();
|
|
281
319
|
if (!text) return;
|
|
282
|
-
|
|
320
|
+
|
|
321
|
+
state.hasStarted = true;
|
|
322
|
+
state.inputText = "";
|
|
323
|
+
state.cursorIndex = 0;
|
|
324
|
+
state.chatScrollOffset = 0;
|
|
325
|
+
|
|
326
|
+
const item = { type: "user", text, meta: "queued" };
|
|
327
|
+
pendingPrompts.push({ text, item });
|
|
328
|
+
state.transcript.push(item);
|
|
329
|
+
renderScreen();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function submitPromptQueue(pid, state, previousMessages, pendingPrompts, renderScreen, close) {
|
|
333
|
+
const firstText = state.inputText.trim();
|
|
334
|
+
if (!firstText) return;
|
|
335
|
+
if (firstText.toLowerCase() === "exit" || firstText.toLowerCase() === "quit") {
|
|
283
336
|
close();
|
|
284
337
|
}
|
|
285
338
|
|
|
286
339
|
state.hasStarted = true;
|
|
287
340
|
state.inputText = "";
|
|
288
341
|
state.cursorIndex = 0;
|
|
289
|
-
state.
|
|
290
|
-
|
|
342
|
+
state.chatScrollOffset = 0;
|
|
343
|
+
|
|
344
|
+
const firstItem = { type: "user", text: firstText };
|
|
345
|
+
state.transcript.push(firstItem);
|
|
346
|
+
await runPrompt(pid, state, previousMessages, renderScreen, firstText, firstItem);
|
|
347
|
+
|
|
348
|
+
while (pendingPrompts.length > 0) {
|
|
349
|
+
const queued = pendingPrompts.shift();
|
|
350
|
+
delete queued.item.meta;
|
|
351
|
+
await runPrompt(pid, state, previousMessages, renderScreen, queued.text, queued.item);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function runPrompt(pid, state, previousMessages, renderScreen, text, userItem) {
|
|
356
|
+
appendLiveItem(state, { type: "status", text: "Thinking...", active: true });
|
|
291
357
|
renderScreen();
|
|
292
358
|
|
|
293
359
|
const startTime = Date.now();
|
|
@@ -312,29 +378,42 @@ async function submitPrompt(pid, state, previousMessages, renderScreen, close) {
|
|
|
312
378
|
result = await http.post(`/projects/${pid}/super-agent/chat`, body);
|
|
313
379
|
removeStatus(state);
|
|
314
380
|
for (const trace of result.trace || []) {
|
|
315
|
-
state
|
|
381
|
+
appendLiveItem(state, { type: "tool", trace });
|
|
316
382
|
}
|
|
317
383
|
}
|
|
318
384
|
|
|
319
385
|
completeSuperAgentResult(result, text, startTime, state, previousMessages);
|
|
320
386
|
} catch (e) {
|
|
321
387
|
removeStatus(state);
|
|
322
|
-
state
|
|
388
|
+
appendLiveItem(state, { type: "error", text: e.message });
|
|
323
389
|
}
|
|
324
390
|
|
|
391
|
+
if (userItem) delete userItem.meta;
|
|
325
392
|
renderScreen();
|
|
326
393
|
}
|
|
327
394
|
|
|
328
395
|
function removeStatus(state) {
|
|
329
|
-
|
|
330
|
-
|
|
396
|
+
for (let i = state.transcript.length - 1; i >= 0; i--) {
|
|
397
|
+
if (state.transcript[i]?.type === "status" && state.transcript[i]?.active) {
|
|
398
|
+
state.transcript.splice(i, 1);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function appendLiveItem(state, item) {
|
|
405
|
+
const queuedIndex = state.transcript.findIndex(
|
|
406
|
+
(entry) => entry?.type === "user" && entry?.meta === "queued"
|
|
407
|
+
);
|
|
408
|
+
if (queuedIndex >= 0) state.transcript.splice(queuedIndex, 0, item);
|
|
409
|
+
else state.transcript.push(item);
|
|
331
410
|
}
|
|
332
411
|
|
|
333
412
|
function handleProgressEvent(event, state, renderScreen) {
|
|
334
413
|
if (event.type === "model_start") {
|
|
335
|
-
const
|
|
336
|
-
if (
|
|
337
|
-
|
|
414
|
+
const status = [...state.transcript].reverse().find((item) => item?.type === "status" && item?.active);
|
|
415
|
+
if (status) {
|
|
416
|
+
status.text = event.iteration > 1 ? `Thinking... step ${event.iteration}` : "Thinking...";
|
|
338
417
|
renderScreen();
|
|
339
418
|
}
|
|
340
419
|
return;
|
|
@@ -342,7 +421,7 @@ function handleProgressEvent(event, state, renderScreen) {
|
|
|
342
421
|
|
|
343
422
|
if (event.type === "assistant_text" && event.text) {
|
|
344
423
|
removeStatus(state);
|
|
345
|
-
state
|
|
424
|
+
appendLiveItem(state, {
|
|
346
425
|
type: "assistant",
|
|
347
426
|
name: state.activeAgent,
|
|
348
427
|
text: event.text,
|
|
@@ -354,7 +433,7 @@ function handleProgressEvent(event, state, renderScreen) {
|
|
|
354
433
|
|
|
355
434
|
if (event.type === "tool_start" && event.trace) {
|
|
356
435
|
removeStatus(state);
|
|
357
|
-
state
|
|
436
|
+
appendLiveItem(state, { type: "tool", trace: event.trace });
|
|
358
437
|
renderScreen();
|
|
359
438
|
return;
|
|
360
439
|
}
|
|
@@ -365,7 +444,7 @@ function handleProgressEvent(event, state, renderScreen) {
|
|
|
365
444
|
(item) => item.type === "tool" && item.trace?.id && item.trace.id === event.trace.id
|
|
366
445
|
);
|
|
367
446
|
if (idx >= 0) state.transcript[idx] = { type: "tool", trace: event.trace };
|
|
368
|
-
else state
|
|
447
|
+
else appendLiveItem(state, { type: "tool", trace: event.trace });
|
|
369
448
|
renderScreen();
|
|
370
449
|
}
|
|
371
450
|
}
|
|
@@ -383,7 +462,7 @@ function completeSuperAgentResult(result, userText, startTime, state, previousMe
|
|
|
383
462
|
state.usage.output += result.usage?.output_tokens || 0;
|
|
384
463
|
state.usage.percent = Math.min(99, Math.round((state.usage.input / 200000) * 100));
|
|
385
464
|
|
|
386
|
-
state
|
|
465
|
+
appendLiveItem(state, {
|
|
387
466
|
type: "assistant",
|
|
388
467
|
name: state.activeAgent,
|
|
389
468
|
text: result.text,
|
|
@@ -275,6 +275,9 @@ function transcriptLines(transcript, width) {
|
|
|
275
275
|
for (const chunk of chunks) {
|
|
276
276
|
addLine(lines, margin + C.primary + "┃" + C.panel + " " + C.text + padAnsi(chunk, inner), C.bg);
|
|
277
277
|
}
|
|
278
|
+
if (item.meta) {
|
|
279
|
+
addLine(lines, margin + C.primary + "┃" + C.panel + " " + C.muted + padAnsi(item.meta, inner), C.bg);
|
|
280
|
+
}
|
|
278
281
|
addLine(lines, margin + C.primary + "┃" + C.panel + " " + " ".repeat(inner), C.bg);
|
|
279
282
|
continue;
|
|
280
283
|
}
|
|
@@ -315,14 +318,22 @@ function transcriptLines(transcript, width) {
|
|
|
315
318
|
return lines;
|
|
316
319
|
}
|
|
317
320
|
|
|
318
|
-
function renderChat(
|
|
321
|
+
function renderChat(state, chatWidth, height, promptTop) {
|
|
319
322
|
const maxRows = Math.max(1, promptTop - 1);
|
|
320
|
-
const lines = transcriptLines(transcript, chatWidth - 2);
|
|
321
|
-
const
|
|
323
|
+
const lines = transcriptLines(state.transcript, chatWidth - 2);
|
|
324
|
+
const maxOffset = Math.max(0, lines.length - maxRows);
|
|
325
|
+
const offset = Math.min(Math.max(0, state.chatScrollOffset || 0), maxOffset);
|
|
326
|
+
state.chatScrollOffset = offset;
|
|
327
|
+
const start = Math.max(0, lines.length - maxRows - offset);
|
|
328
|
+
const slice = lines.slice(start, start + maxRows);
|
|
322
329
|
|
|
323
330
|
for (let i = 0; i < slice.length && i < maxRows; i++) {
|
|
324
331
|
writeAt(i, 0, slice[i].text, chatWidth - 1, slice[i].bg);
|
|
325
332
|
}
|
|
333
|
+
|
|
334
|
+
if (offset > 0 && maxRows > 1) {
|
|
335
|
+
writeAt(0, 0, C.muted + `↑ ${offset} lines above bottom`, chatWidth - 1, C.bg);
|
|
336
|
+
}
|
|
326
337
|
}
|
|
327
338
|
|
|
328
339
|
function renderSidebar(state) {
|
|
@@ -407,7 +418,7 @@ export function renderTerminalChat(state) {
|
|
|
407
418
|
renderLogo(chatWidth, Math.max(1, Math.floor(height / 2) - 8));
|
|
408
419
|
} else {
|
|
409
420
|
const prompt = promptGeometry(false, true, chatWidth);
|
|
410
|
-
renderChat(state
|
|
421
|
+
renderChat(state, chatWidth, height, prompt.top);
|
|
411
422
|
}
|
|
412
423
|
|
|
413
424
|
const cursor = renderPromptBlock(state, chatWidth);
|
package/src/core/apx-skill.md
CHANGED
|
@@ -42,6 +42,22 @@ The output is the agent's full stdout. If it printed `APC_RESULT: <value>`, that
|
|
|
42
42
|
apx exec <slug> "<prompt>"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Command accuracy
|
|
46
|
+
|
|
47
|
+
Do not invent APX subcommands. Before telling another runtime to call APX, verify the exact CLI
|
|
48
|
+
form with `apx --help` or `apx <command> --help`.
|
|
49
|
+
|
|
50
|
+
Known Telegram form:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
apx telegram status
|
|
54
|
+
apx telegram send "message"
|
|
55
|
+
apx telegram send "message" --chat 123456
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Do not use guessed aliases such as `apx send-telegram` or `apx telegram "message"` unless current
|
|
59
|
+
`apx --help` shows that exact form.
|
|
60
|
+
|
|
45
61
|
## MCP tools
|
|
46
62
|
|
|
47
63
|
MCPs declared in `.apc/mcps.json` are proxied through the APX daemon. Use `apx mcp` only for MCPs registered there — not for MCPs that are already running locally in your IDE session.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-code
|
|
3
|
+
description: "Activate ONLY when the user explicitly mentions Claude Code, Claude CLI, claude command, Anthropic Claude Code, installing Claude Code, using Claude Code, or APX runtime claude-code. Do not activate for generic Claude model discussion."
|
|
4
|
+
homepage: https://docs.anthropic.com/en/docs/claude-code
|
|
5
|
+
---
|
|
6
|
+
# Claude Code CLI
|
|
7
|
+
|
|
8
|
+
Use this skill only for Claude Code CLI install, auth, usage, or APX runtime dispatch.
|
|
9
|
+
|
|
10
|
+
## Verify before acting
|
|
11
|
+
|
|
12
|
+
Check the local CLI first:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
claude --version
|
|
16
|
+
claude --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Do not invent flags. If a command is uncertain, inspect help for the exact subcommand before
|
|
20
|
+
running it.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
Common install/update path:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g @anthropic-ai/claude-code
|
|
28
|
+
claude --version
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Claude Code also exposes:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
claude install
|
|
35
|
+
claude update
|
|
36
|
+
claude auth
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `claude --help` to confirm current syntax.
|
|
40
|
+
|
|
41
|
+
## Non-interactive use
|
|
42
|
+
|
|
43
|
+
Prefer headless print mode:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
claude -p "task" --output-format json
|
|
47
|
+
claude -p "task" --append-system-prompt "system instructions" --output-format json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For high-trust automation in an already sandboxed environment:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude -p "task" --permission-mode bypassPermissions --output-format json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## APX runtime
|
|
57
|
+
|
|
58
|
+
Run a project agent through Claude Code:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
apx run <agent> --runtime claude-code "task"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If the task needs Telegram, tell Claude Code the exact APX command:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
apx telegram send "message"
|
|
68
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codex-cli
|
|
3
|
+
description: "Activate ONLY when the user explicitly mentions Codex CLI, OpenAI Codex, @openai/codex, codex command, codex exec, installing Codex, using Codex, ~/.codex, or APX runtime codex."
|
|
4
|
+
homepage: https://developers.openai.com/codex
|
|
5
|
+
---
|
|
6
|
+
# Codex CLI
|
|
7
|
+
|
|
8
|
+
Use this skill only for Codex CLI install, auth, usage, or APX runtime dispatch.
|
|
9
|
+
|
|
10
|
+
## Verify before acting
|
|
11
|
+
|
|
12
|
+
Check local CLI and exact flags first:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
codex --version
|
|
16
|
+
codex --help
|
|
17
|
+
codex exec --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Do not invent flags. Current Codex CLI may reject older flags such as `--approval-mode` or
|
|
21
|
+
`--full-auto`.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
Common install/update path:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @openai/codex
|
|
29
|
+
codex --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Codex also exposes:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
codex login
|
|
36
|
+
codex update
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Auth check:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
test -f ~/.codex/auth.json && echo "codex auth present"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Non-interactive use
|
|
46
|
+
|
|
47
|
+
Use `exec`, not interactive TUI, for automation:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
codex exec --sandbox workspace-write --skip-git-repo-check "task"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Useful options:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
codex exec --sandbox workspace-write --skip-git-repo-check --output-last-message /tmp/codex-last.txt "task"
|
|
57
|
+
codex exec --json --sandbox workspace-write --skip-git-repo-check "task"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`--skip-git-repo-check` matters for APX default runtime dirs such as `~/.apx/projects/default`,
|
|
61
|
+
which may not be Git repositories.
|
|
62
|
+
|
|
63
|
+
## APX runtime
|
|
64
|
+
|
|
65
|
+
Run a project agent through Codex:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
apx run <agent> --runtime codex "task"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If the task needs Telegram, tell Codex the exact APX command:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
apx telegram send "message"
|
|
75
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opencode-cli
|
|
3
|
+
description: "Activate ONLY when the user explicitly mentions OpenCode, opencode command, installing OpenCode, using OpenCode, OpenCode provider setup, or APX runtime opencode."
|
|
4
|
+
homepage: https://opencode.ai/docs
|
|
5
|
+
---
|
|
6
|
+
# OpenCode CLI
|
|
7
|
+
|
|
8
|
+
Use this skill only for OpenCode CLI install, auth/provider setup, usage, or APX runtime dispatch.
|
|
9
|
+
|
|
10
|
+
## Verify before acting
|
|
11
|
+
|
|
12
|
+
Check local CLI and exact flags first:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
opencode --version
|
|
16
|
+
opencode --help
|
|
17
|
+
opencode run --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Do not invent flags. Inspect help for the exact subcommand before running uncertain commands.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
Use the official install method for the target machine, then verify:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
opencode --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Provider/auth management:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
opencode providers
|
|
34
|
+
opencode models
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Non-interactive use
|
|
38
|
+
|
|
39
|
+
Use headless run:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
opencode run "task"
|
|
43
|
+
opencode run --model provider/model "task"
|
|
44
|
+
opencode run --dangerously-skip-permissions "task"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## APX runtime
|
|
48
|
+
|
|
49
|
+
Run a project agent through OpenCode:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
apx run <agent> --runtime opencode "task"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If the task needs Telegram, tell OpenCode the exact APX command:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
apx telegram send "message"
|
|
59
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openrouter
|
|
3
|
+
description: "Activate ONLY when the user explicitly mentions OpenRouter, OPENROUTER_API_KEY, OpenRouter models, installing OpenRouter provider config, or using OpenRouter with APX, OpenCode, LiteLLM, or an OpenAI-compatible client."
|
|
4
|
+
homepage: https://openrouter.ai/docs
|
|
5
|
+
---
|
|
6
|
+
# OpenRouter
|
|
7
|
+
|
|
8
|
+
Use this skill only for OpenRouter install/config, model listing, or usage through APX/OpenCode or
|
|
9
|
+
OpenAI-compatible clients.
|
|
10
|
+
|
|
11
|
+
## Verify before acting
|
|
12
|
+
|
|
13
|
+
OpenRouter is an API/provider, not a local coding runtime by itself. First identify which client
|
|
14
|
+
will use it: APX engine, OpenCode provider, LiteLLM, OpenAI SDK, or another OpenAI-compatible tool.
|
|
15
|
+
|
|
16
|
+
Do not expose keys. Check only presence:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
test -n "$OPENROUTER_API_KEY" && echo "OPENROUTER_API_KEY present"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## OpenAI-compatible base URL
|
|
23
|
+
|
|
24
|
+
Typical API settings:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
OPENAI_BASE_URL=https://openrouter.ai/api/v1
|
|
28
|
+
OPENROUTER_API_KEY=...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use the selected client's current docs/help before writing config.
|
|
32
|
+
|
|
33
|
+
## APX guidance
|
|
34
|
+
|
|
35
|
+
If configuring APX engine/provider, inspect current config schema first:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
apx config --help
|
|
39
|
+
apx status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then update only non-secret project-safe settings. Keep API keys in user config or environment, not
|
|
43
|
+
in `.apc/` or git.
|
|
44
|
+
|
|
45
|
+
## OpenCode guidance
|
|
46
|
+
|
|
47
|
+
If using OpenRouter via OpenCode, inspect provider commands first:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
opencode providers
|
|
51
|
+
opencode models
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then configure OpenRouter through OpenCode's current provider flow.
|
package/src/core/scaffold.js
CHANGED
|
@@ -87,7 +87,7 @@ function buildSkillMd(content) {
|
|
|
87
87
|
const frontmatter = [
|
|
88
88
|
"---",
|
|
89
89
|
"name: apx",
|
|
90
|
-
"description: APX CLI skill. Activate when: user asks to run or coordinate agents, use MCP tools from .apc/mcps.json, install agents from a team workspace, or explicitly mentions apx commands. Do NOT activate just because .apc/ exists — that is handled by the apc-context skill. Activate on: 'apx run', 'apx exec', 'run an agent', 'coordinate agents', 'MCP not working', 'install agent', 'team agents', 'apx memory', 'daemon'
|
|
90
|
+
"description: \"APX CLI skill. Activate when: user asks to run or coordinate agents, use MCP tools from .apc/mcps.json, install agents from a team workspace, or explicitly mentions apx commands. Do NOT activate just because .apc/ exists — that is handled by the apc-context skill. Activate on: 'apx run', 'apx exec', 'run an agent', 'coordinate agents', 'MCP not working', 'install agent', 'team agents', 'apx memory', 'daemon'.\"",
|
|
91
91
|
"homepage: https://github.com/agentprojectcontext/apx",
|
|
92
92
|
"---",
|
|
93
93
|
"",
|
|
@@ -95,6 +95,19 @@ function buildSkillMd(content) {
|
|
|
95
95
|
return frontmatter + content;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function readRuntimeSkillFiles() {
|
|
99
|
+
const skillsDir = path.join(__dirname, "runtime-skills");
|
|
100
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
101
|
+
|
|
102
|
+
return fs.readdirSync(skillsDir)
|
|
103
|
+
.filter((name) => name.endsWith(".md"))
|
|
104
|
+
.sort()
|
|
105
|
+
.map((name) => ({
|
|
106
|
+
slug: path.basename(name, ".md"),
|
|
107
|
+
md: fs.readFileSync(path.join(skillsDir, name), "utf8").trim(),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
|
|
98
111
|
// Install APX + APC context skills into IDE rule files. Returns an array of result objects.
|
|
99
112
|
// targetIds: array of target ids to install; null = all project targets.
|
|
100
113
|
export function installIdeSkills(root, targetIds = null) {
|
|
@@ -159,6 +172,7 @@ export function installGlobalSkills() {
|
|
|
159
172
|
skills.push({ slug: "apx", md: buildSkillMd(fs.readFileSync(apxSrc, "utf8").trim()) });
|
|
160
173
|
if (fs.existsSync(apcSrc))
|
|
161
174
|
skills.push({ slug: "apc-context", md: buildApcContextSkillMd(fs.readFileSync(apcSrc, "utf8").trim()) });
|
|
175
|
+
skills.push(...readRuntimeSkillFiles());
|
|
162
176
|
|
|
163
177
|
for (const base of GLOBAL_SKILL_DIRS) {
|
|
164
178
|
for (const { slug, md } of skills) {
|
|
@@ -256,6 +270,7 @@ export function getOrCreateApxId(root) {
|
|
|
256
270
|
try { cfg = JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
|
|
257
271
|
if (cfg.apx_id) return cfg.apx_id;
|
|
258
272
|
const apxId = crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
273
|
+
console.log(`[apx] Generating new stable ID ${apxId} for project at ${root}`);
|
|
259
274
|
cfg.apx_id = apxId;
|
|
260
275
|
fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + "\n");
|
|
261
276
|
return apxId;
|
package/src/daemon/db.js
CHANGED
|
@@ -37,9 +37,11 @@ export class ProjectManager {
|
|
|
37
37
|
// Ensure directories exist for projects initialized before they were added.
|
|
38
38
|
fs.mkdirSync(path.join(abs, ".apc", "commands"), { recursive: true });
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Resolve stable APX storage ID (read from .apc/project.json).
|
|
41
41
|
const apxId = getOrCreateApxId(abs);
|
|
42
|
-
|
|
42
|
+
// Don't create the physical storage folder yet.
|
|
43
|
+
// Just resolve where it SHOULD be.
|
|
44
|
+
const storagePath = path.join(path.dirname(DEFAULT_PROJECT_STORE), apxId || "null");
|
|
43
45
|
|
|
44
46
|
const entry = {
|
|
45
47
|
id: this._nextId++,
|
|
@@ -48,8 +50,15 @@ export class ProjectManager {
|
|
|
48
50
|
apxId,
|
|
49
51
|
config: effectiveConfig(this.globalConfig, abs),
|
|
50
52
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
|
|
54
|
+
// Lazy message logger: ensure directory exists ONLY when writing.
|
|
55
|
+
entry.logMessage = (payload) => {
|
|
56
|
+
if (entry.apxId) {
|
|
57
|
+
ensureProjectStorage(entry.apxId);
|
|
58
|
+
}
|
|
59
|
+
return appendMessageToFs({ projectRoot: entry.storagePath, ...payload });
|
|
60
|
+
};
|
|
61
|
+
|
|
53
62
|
this.byId.set(entry.id, entry);
|
|
54
63
|
this.byPath.set(abs, entry);
|
|
55
64
|
return entry;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// OpenAI Codex CLI runtime adapter.
|
|
2
|
-
// codex exec "<prompt>"
|
|
2
|
+
// codex exec --sandbox workspace-write --skip-git-repo-check "<prompt>"
|
|
3
3
|
// System prompt is prepended to the prompt body since Codex doesn't have a
|
|
4
4
|
// dedicated --system flag in `exec` mode.
|
|
5
5
|
// Reference: https://github.com/openai/codex
|
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
const fullPrompt = system ? `${system}\n\n---\n\n${prompt}` : prompt;
|
|
16
16
|
const r = await runProcess({
|
|
17
17
|
command: "codex",
|
|
18
|
-
args: ["exec", fullPrompt],
|
|
18
|
+
args: ["exec", "--sandbox", "workspace-write", "--skip-git-repo-check", fullPrompt],
|
|
19
19
|
cwd,
|
|
20
20
|
env,
|
|
21
21
|
timeoutMs,
|
|
@@ -99,7 +99,7 @@ export default {
|
|
|
99
99
|
type: "object",
|
|
100
100
|
properties: {
|
|
101
101
|
project: { type: "string" },
|
|
102
|
-
agent: { type: "string", description: "
|
|
102
|
+
agent: { type: "string", description: "APC agent slug. MANDATORY OMIT if you are acting as yourself (APX/vos mismo/default). Use ONLY if the user named a specific agent from AGENTS.md." },
|
|
103
103
|
runtime: {
|
|
104
104
|
type: "string",
|
|
105
105
|
enum: RUNTIME_IDS,
|
|
@@ -53,11 +53,11 @@ HARD RULES (do not deviate):
|
|
|
53
53
|
7. Stay brief: under 6 sentences unless asked for detail.
|
|
54
54
|
8. You DO see recent prior turns of this chat as previous messages when applicable. **Use them ONLY to disambiguate references** (e.g. "el primero" → first project mentioned earlier). For ANY factual data — agent details, MCP details, file contents, memory — RE-CALL the tool. Past turns are context, not a cache. Models change, agents change, files change.
|
|
55
55
|
9. /reset or /new from the user means "forget previous turns and answer this one fresh" — if you see those prefixes the operator already cleared the context for you.
|
|
56
|
-
10. SELF-RUN RULE
|
|
57
|
-
11. DELEGATION RULE:
|
|
58
|
-
12. DISPATCH RULE
|
|
59
|
-
13. PROJECT RULE:
|
|
60
|
-
14. VAULT RULE:
|
|
56
|
+
10. **SELF-RUN RULE**: If the user says "vos mismo", "tu mismo", "same", "base", "default", "sin agente", or does not explicitly name an agent slug, act as APX. **DO NOT** call list_agents. **DO NOT** pass an 'agent' argument to tools.
|
|
57
|
+
11. DELEGATION RULE: When the user asks a named APC agent to do a task, use call_agent (unless they specify opening it in a runtime, then see rule 12).
|
|
58
|
+
12. **DISPATCH RULE**: Use call_runtime for external runtimes. If the user named an agent, pass it. If they didn't, **DO NOT PASS ANY AGENT**. Running with an empty agent field is how you run as yourself.
|
|
59
|
+
13. PROJECT RULE: When the user gives no project, use project "default". Do not infer a non-default project from old chat history unless the user references it. If they mention a path or project name, look it up or add it with add_project.
|
|
60
|
+
14. VAULT RULE: When the user wants a new existing agent/template, call list_vault_agents first. If a suitable vault agent exists, import_agent into the chosen project. If none fits, say briefly what is missing.
|
|
61
61
|
15. NO-PENDING RULE: never say "give me a second", "I will do it", or "I will try later" as a final answer. Either call the tool in this same turn or say what blocks you.
|
|
62
62
|
16. IDENTITY RULE: when the user asks you to change your name, call yourself something, or update your personality/language, call set_identity and persist the change. Then confirm with your new name.
|
|
63
63
|
17. ROUTINES RULE: NEVER create a routine in the default project (id=0). Routines MUST be tied to a specific registered project. Before adding a routine, call list_projects to find the correct project id or name. Then pass --project <id|name> to apx routine add. If no project fits, ask the user which project to use. Creating routines in project 0/default mixes unrelated projects' schedules and corrupts state.`;
|