@agentprojectcontext/apx 1.10.3 → 1.11.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/README.md +5 -1
- package/package.json +5 -1
- package/src/cli/commands/search.js +62 -0
- package/src/cli/commands/sys.js +97 -18
- package/src/cli/index.js +21 -0
- package/src/cli/terminal-chat/renderer.js +15 -4
- package/src/core/agent-system.js +15 -0
- 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 +15 -1
- package/src/daemon/api.js +229 -0
- package/src/daemon/engines/anthropic.js +19 -1
- package/src/daemon/engines/index.js +2 -1
- package/src/daemon/engines/openai.js +22 -2
- package/src/daemon/plugins/telegram.js +248 -2
- package/src/daemon/runtimes/codex.js +2 -2
- package/src/daemon/super-agent.js +42 -1
- package/src/daemon/tools/browser.js +424 -0
- package/src/daemon/tools/fetch.js +138 -0
- package/src/daemon/tools/glob.js +165 -0
- package/src/daemon/tools/grep.js +218 -0
- package/src/daemon/tools/registry.js +729 -0
- package/src/daemon/tools/search.js +290 -0
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentprojectcontext/apx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"express": "^4.21.0",
|
|
36
36
|
"node-fetch": "^3.3.2"
|
|
37
37
|
},
|
|
38
|
+
"optionalDependencies": {
|
|
39
|
+
"fast-glob": "^3.3.2",
|
|
40
|
+
"puppeteer": "^22.0.0"
|
|
41
|
+
},
|
|
38
42
|
"devDependencies": {
|
|
39
43
|
"@semantic-release/changelog": "^6.0.3",
|
|
40
44
|
"@semantic-release/git": "^10.0.1",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// cli/commands/search.js
|
|
2
|
+
// `apx search "query"` — web search from the terminal.
|
|
3
|
+
// Uses the daemon's tools/search.js module directly (no HTTP roundtrip,
|
|
4
|
+
// no need to have `apx daemon start` running).
|
|
5
|
+
|
|
6
|
+
import { webSearch } from "../../daemon/tools/search.js";
|
|
7
|
+
|
|
8
|
+
const DIM = "\x1b[2m";
|
|
9
|
+
const BOLD = "\x1b[1m";
|
|
10
|
+
const BLUE = "\x1b[34m";
|
|
11
|
+
const CYAN = "\x1b[36m";
|
|
12
|
+
const RESET = "\x1b[0m";
|
|
13
|
+
|
|
14
|
+
export async function cmdSearch(args) {
|
|
15
|
+
const query = (args._ || []).join(" ").trim();
|
|
16
|
+
if (!query) {
|
|
17
|
+
console.error("apx search: missing <query>");
|
|
18
|
+
console.error("usage: apx search \"<query>\" [--mode auto|ddg|brave|browser] [-n N]");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const mode = args.flags?.mode || "auto";
|
|
23
|
+
const limit = parseInt(args.flags?.n || args.flags?.limit || "5", 10) || 5;
|
|
24
|
+
const json = !!args.flags?.json;
|
|
25
|
+
|
|
26
|
+
process.stderr.write(`${DIM}Searching... (${mode})${RESET}\n`);
|
|
27
|
+
|
|
28
|
+
let r;
|
|
29
|
+
try {
|
|
30
|
+
r = await webSearch({ query, mode, limit });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error(`${DIM}error:${RESET} ${e.message}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (json) {
|
|
37
|
+
console.log(JSON.stringify(r, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const results = r.results || [];
|
|
42
|
+
if (results.length === 0) {
|
|
43
|
+
console.log(`${DIM}(no results from ${r.mode})${RESET}`);
|
|
44
|
+
if (r.raw_excerpt) console.log(`${DIM}excerpt:${RESET} ${r.raw_excerpt.slice(0, 400)}…`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(`${DIM}${results.length} result${results.length === 1 ? "" : "s"} via ${r.mode}${RESET}\n`);
|
|
49
|
+
results.forEach((item, i) => {
|
|
50
|
+
const num = `[${i + 1}]`;
|
|
51
|
+
const title = item.title || "(no title)";
|
|
52
|
+
console.log(`${BOLD}${num} ${title}${RESET} ${DIM}— ${hostname(item.url)}${RESET}`);
|
|
53
|
+
if (item.snippet) console.log(` ${item.snippet}`);
|
|
54
|
+
console.log(` ${BLUE}${item.url}${RESET}`);
|
|
55
|
+
if (i < results.length - 1) console.log("");
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function hostname(url) {
|
|
60
|
+
try { return new URL(url).hostname.replace(/^www\./, ""); }
|
|
61
|
+
catch { return url; }
|
|
62
|
+
}
|
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,
|
package/src/cli/index.js
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
cmdTelegramSetup,
|
|
55
55
|
} from "./commands/telegram.js";
|
|
56
56
|
import { cmdMessagesTail, cmdMessagesSearch, cmdMessagesChat } from "./commands/messages.js";
|
|
57
|
+
import { cmdSearch } from "./commands/search.js";
|
|
57
58
|
import { cmdExec } from "./commands/exec.js";
|
|
58
59
|
import {
|
|
59
60
|
cmdChat,
|
|
@@ -653,6 +654,21 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
653
654
|
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
654
655
|
examples: ["apx messages search \"deploy\""],
|
|
655
656
|
}),
|
|
657
|
+
search: topic({
|
|
658
|
+
title: "apx search",
|
|
659
|
+
summary: "Web search from the terminal (DuckDuckGo / Brave / Puppeteer fallback).",
|
|
660
|
+
usage: ["apx search \"<query>\" [--mode auto|ddg|brave|browser] [-n N] [--json]"],
|
|
661
|
+
options: [
|
|
662
|
+
["--mode <m>", "auto (default) | ddg | brave | browser. Brave needs BRAVE_API_KEY."],
|
|
663
|
+
["-n N", "Number of results (default 5, max 20)."],
|
|
664
|
+
["--json", "Output raw JSON instead of the formatted list."],
|
|
665
|
+
],
|
|
666
|
+
examples: [
|
|
667
|
+
"apx search \"agent project context\"",
|
|
668
|
+
"apx search \"node 22 release notes\" --mode ddg -n 10",
|
|
669
|
+
"apx search \"weather buenos aires\" --json",
|
|
670
|
+
],
|
|
671
|
+
}),
|
|
656
672
|
"messages chat": topic({
|
|
657
673
|
title: "apx messages chat",
|
|
658
674
|
summary: "Print APX messages as a chat transcript with user, agent, tool, or system type.",
|
|
@@ -1129,6 +1145,7 @@ function buildHelp(version) {
|
|
|
1129
1145
|
hCmd("apx code", 36, "APX terminal coding assistant"),
|
|
1130
1146
|
hCmd("apx exec <agent> \"prompt\"",36, "one-shot agent call --model <id> --max-tokens N"),
|
|
1131
1147
|
hCmd("apx chat <agent>", 36, "interactive agent REPL --conversation <id>"),
|
|
1148
|
+
hCmd("apx search \"query\"", 36, "web search (ddg | brave | browser) --mode <m> -n N"),
|
|
1132
1149
|
hCmd("apx conversations list", 36, "stored exec/chat conversations for <agent>"),
|
|
1133
1150
|
hCmd("apx conversations get", 36, "<agent> <id>"),
|
|
1134
1151
|
|
|
@@ -1433,6 +1450,10 @@ async function dispatch(cmd, rest) {
|
|
|
1433
1450
|
await cmdExec(parseArgs(rest));
|
|
1434
1451
|
break;
|
|
1435
1452
|
|
|
1453
|
+
case "search":
|
|
1454
|
+
await cmdSearch(parseArgs(rest));
|
|
1455
|
+
break;
|
|
1456
|
+
|
|
1436
1457
|
case "chat":
|
|
1437
1458
|
await cmdChat(parseArgs(rest));
|
|
1438
1459
|
break;
|
|
@@ -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/agent-system.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Anti-ghost-response rules injected into every agent system prompt.
|
|
6
|
+
// Prevents agents from saying "Ok, I'll do that" and then doing nothing.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const ACTION_DISCIPLINE_RULES = `## Action Discipline (mandatory)
|
|
9
|
+
- NEVER acknowledge an action without executing it in the same turn. If you are going to do something, call the tool FIRST, then report the result.
|
|
10
|
+
- NEVER use empty acknowledgments like "Ok", "Got it", "Sure", "Understood", "On it", "Give me a moment", "I'll do that now" as standalone responses when a tool call is expected. These are invalid responses.
|
|
11
|
+
- Action first, report after. Produce the tool call in the same response as your acknowledgment.
|
|
12
|
+
- If you cannot execute the action (missing permission, unclear params, tool not available), explain WHY — do not promise and disappear.
|
|
13
|
+
- If the user asks you to do multiple things, do them all in the same turn using sequential tool calls if needed.`;
|
|
14
|
+
|
|
4
15
|
function listField(value) {
|
|
5
16
|
if (Array.isArray(value)) return value.map(String).map((s) => s.trim()).filter(Boolean);
|
|
6
17
|
return String(value || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -66,6 +77,10 @@ export function buildAgentSystem(project, agent, {
|
|
|
66
77
|
if (ep) parts.push(ep);
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
// Always append action discipline rules last so they are close to the end
|
|
81
|
+
// of the system prompt and harder for the model to "forget".
|
|
82
|
+
parts.push(ACTION_DISCIPLINE_RULES);
|
|
83
|
+
|
|
69
84
|
return parts.join("\n\n");
|
|
70
85
|
}
|
|
71
86
|
|
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
|
+
```
|