@agentprojectcontext/apx 1.8.2 → 1.10.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 +2 -1
- package/package.json +4 -1
- package/skills/apx/SKILL.md +5 -0
- package/src/cli/commands/artifact.js +45 -0
- package/src/cli/commands/routine.js +15 -1
- package/src/cli/commands/runtime.js +1 -1
- package/src/cli/commands/sys.js +330 -0
- package/src/cli/index.js +100 -6
- package/src/cli/terminal-chat/renderer.js +412 -0
- package/src/core/apc-context-skill.md +2 -2
- package/src/core/apx-skill.md +4 -0
- package/src/core/artifacts-store.js +59 -0
- package/src/core/routines-store.js +40 -7
- package/src/daemon/apc-runtime-context.js +3 -2
- package/src/daemon/api.js +80 -2
- package/src/daemon/env-detect.js +1 -0
- package/src/daemon/routines.js +141 -13
- package/src/daemon/runtimes/claude-code.js +24 -6
- package/src/daemon/runtimes/cursor-agent.js +34 -0
- package/src/daemon/runtimes/gemini-cli.js +32 -0
- package/src/daemon/runtimes/index.js +8 -1
- package/src/daemon/runtimes/qwen-code.js +36 -0
- package/src/daemon/super-agent-tools/index.js +2 -0
- package/src/daemon/super-agent-tools/tools/call-runtime.js +112 -42
- package/src/daemon/super-agent-tools/tools/search-files.js +66 -0
- package/src/daemon/super-agent.js +6 -17
- package/src/mcp/index.js +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm install -g apx
|
|
|
26
26
|
# In any directory with an AGENTS.md
|
|
27
27
|
apx init
|
|
28
28
|
|
|
29
|
-
# Spawn an agent with a full
|
|
29
|
+
# Spawn an agent with a full external runtime
|
|
30
30
|
apx run sofia --runtime claude-code "Review the open PRs and summarize them"
|
|
31
31
|
|
|
32
32
|
# Or a quick one-shot LLM exec
|
|
@@ -84,6 +84,7 @@ apx memory <slug> # read agent memory
|
|
|
84
84
|
apx memory <slug> --append "<note>" # append to memory
|
|
85
85
|
|
|
86
86
|
apx run <slug> --runtime claude-code "<prompt>" # full runtime session
|
|
87
|
+
apx run <slug> --runtime cursor-agent "<prompt>" # Cursor Agent runtime
|
|
87
88
|
apx exec <slug> "<prompt>" # quick LLM call
|
|
88
89
|
|
|
89
90
|
apx session list <slug> # list past sessions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentprojectcontext/apx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
|
+
"chalk": "^5.6.2",
|
|
33
|
+
"cli-cursor": "^5.0.0",
|
|
34
|
+
"cron-parser": "^5.5.0",
|
|
32
35
|
"express": "^4.21.0",
|
|
33
36
|
"node-fetch": "^3.3.2"
|
|
34
37
|
},
|
package/skills/apx/SKILL.md
CHANGED
|
@@ -26,6 +26,11 @@ apx mcp list # MCP servers available to this project
|
|
|
26
26
|
# Full external session (best for complex, multi-step tasks)
|
|
27
27
|
apx run <slug> --runtime claude-code "<prompt>"
|
|
28
28
|
apx run <slug> --runtime codex "<prompt>"
|
|
29
|
+
apx run <slug> --runtime opencode "<prompt>"
|
|
30
|
+
apx run <slug> --runtime aider "<prompt>"
|
|
31
|
+
apx run <slug> --runtime cursor-agent "<prompt>"
|
|
32
|
+
apx run <slug> --runtime gemini-cli "<prompt>"
|
|
33
|
+
apx run <slug> --runtime qwen-code "<prompt>"
|
|
29
34
|
|
|
30
35
|
# Quick one-shot LLM call (requires engine API key in ~/.apx/config.json)
|
|
31
36
|
apx exec <slug> "<prompt>"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { http } from "../http.js";
|
|
2
|
+
import { resolveProjectId } from "./project.js";
|
|
3
|
+
|
|
4
|
+
export async function cmdArtifactCreate(args) {
|
|
5
|
+
const name = args._[0];
|
|
6
|
+
if (!name) throw new Error("apx artifact create: missing <name>");
|
|
7
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
8
|
+
const content = args.flags.content && args.flags.content !== true ? String(args.flags.content) : "";
|
|
9
|
+
const r = await http.post(`/projects/${pid}/artifacts`, { name, content });
|
|
10
|
+
console.log(r.path);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function cmdArtifactList(args = {}) {
|
|
14
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
15
|
+
const rows = await http.get(`/projects/${pid}/artifacts`);
|
|
16
|
+
if (rows.length === 0) {
|
|
17
|
+
console.log(`(no artifacts in project #${pid})`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
console.log(`project #${pid} artifacts:`);
|
|
21
|
+
console.log("NAME".padEnd(30) + " SIZE MODIFIED");
|
|
22
|
+
for (const a of rows) {
|
|
23
|
+
console.log(
|
|
24
|
+
a.name.padEnd(30) + " " +
|
|
25
|
+
String(a.size).padEnd(6) + " " +
|
|
26
|
+
(a.modified || "—")
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function cmdArtifactShow(args) {
|
|
32
|
+
const name = args._[0];
|
|
33
|
+
if (!name) throw new Error("apx artifact show: missing <name>");
|
|
34
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
35
|
+
const r = await http.get(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
|
|
36
|
+
process.stdout.write(r.content);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function cmdArtifactRemove(args) {
|
|
40
|
+
const name = args._[0];
|
|
41
|
+
if (!name) throw new Error("apx artifact remove: missing <name>");
|
|
42
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
43
|
+
await http.delete(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
|
|
44
|
+
console.log(`removed artifact "${name}"`);
|
|
45
|
+
}
|
|
@@ -67,9 +67,23 @@ export async function cmdRoutineAdd(args) {
|
|
|
67
67
|
const allowed_tools = args.flags["allowed-tools"] && args.flags["allowed-tools"] !== true
|
|
68
68
|
? String(args.flags["allowed-tools"]).split(",").map((s) => s.trim()).filter(Boolean)
|
|
69
69
|
: undefined;
|
|
70
|
+
// Pipeline: pre/post commands as comma-separated list or repeated --pre-commands flags.
|
|
71
|
+
const parseCmdList = (val) => {
|
|
72
|
+
if (!val || val === true) return undefined;
|
|
73
|
+
if (Array.isArray(val)) return val.flatMap((v) => String(v).split(",")).map((s) => s.trim()).filter(Boolean);
|
|
74
|
+
return String(val).split(",").map((s) => s.trim()).filter(Boolean);
|
|
75
|
+
};
|
|
76
|
+
const pre_commands = parseCmdList(args.flags["pre-commands"]);
|
|
77
|
+
const post_commands = parseCmdList(args.flags["post-commands"]);
|
|
78
|
+
const skip_prompt_on = args.flags["skip-prompt-on"] && args.flags["skip-prompt-on"] !== true
|
|
79
|
+
? args.flags["skip-prompt-on"]
|
|
80
|
+
: undefined;
|
|
70
81
|
const pid = await resolveProjectId(args?.flags?.project);
|
|
71
|
-
const r = await http.post(`/projects/${pid}/routines`, { name, kind, schedule, spec, permission_mode, allowed_tools });
|
|
82
|
+
const r = await http.post(`/projects/${pid}/routines`, { name, kind, schedule, spec, permission_mode, allowed_tools, pre_commands, post_commands, skip_prompt_on });
|
|
72
83
|
console.log(`added routine "${r.name}" (${r.kind}, ${r.schedule}) → next ${r.next_run_at}`);
|
|
84
|
+
if (r.pre_commands?.length) console.log(` pre: ${r.pre_commands.join(", ")}`);
|
|
85
|
+
if (r.post_commands?.length) console.log(` post: ${r.post_commands.join(", ")}`);
|
|
86
|
+
if (r.skip_prompt_on && r.skip_prompt_on !== "signal") console.log(` skip_prompt_on: ${r.skip_prompt_on}`);
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
export async function cmdRoutineRemove(args) {
|
|
@@ -5,7 +5,7 @@ export async function cmdRun(args) {
|
|
|
5
5
|
const slug = args._[0];
|
|
6
6
|
if (!slug) throw new Error("apx run: usage: apx run <agent> --runtime <id> \"<prompt>\"");
|
|
7
7
|
const runtime = args.flags.runtime === true ? null : args.flags.runtime;
|
|
8
|
-
if (!runtime) throw new Error("apx run: --runtime required (claude-code | codex | opencode | aider)");
|
|
8
|
+
if (!runtime) throw new Error("apx run: --runtime required (claude-code | codex | opencode | aider | cursor-agent | gemini-cli | qwen-code)");
|
|
9
9
|
|
|
10
10
|
let prompt = args._.slice(1).join(" ").trim();
|
|
11
11
|
if (!prompt || prompt === "-") {
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import readline from "node:readline";
|
|
2
|
+
import { http } from "../http.js";
|
|
3
|
+
import { resolveProjectId } from "./project.js";
|
|
4
|
+
import { readConfig } from "../../core/config.js";
|
|
5
|
+
import { readIdentity } from "../../core/identity.js";
|
|
6
|
+
import {
|
|
7
|
+
C,
|
|
8
|
+
MODES,
|
|
9
|
+
readPackageVersion,
|
|
10
|
+
renderTerminalChat,
|
|
11
|
+
titlecase,
|
|
12
|
+
} from "../terminal-chat/renderer.js";
|
|
13
|
+
|
|
14
|
+
const MAIN_PALETTE_OPTIONS = ["Switch model", "Connect provider", "Open editor", "Exit"];
|
|
15
|
+
|
|
16
|
+
export async function cmdSys(args) {
|
|
17
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
18
|
+
const cfg = readConfig();
|
|
19
|
+
const id = readIdentity();
|
|
20
|
+
|
|
21
|
+
const state = {
|
|
22
|
+
currentModeIdx: 0,
|
|
23
|
+
inputText: "",
|
|
24
|
+
cursorIndex: 0,
|
|
25
|
+
inCommandPalette: false,
|
|
26
|
+
paletteSelection: 0,
|
|
27
|
+
paletteState: "main",
|
|
28
|
+
paletteOptions: [...MAIN_PALETTE_OPTIONS],
|
|
29
|
+
activeAgent: titlecase(id?.agent_name || cfg.super_agent?.name || "SuperAgent"),
|
|
30
|
+
activeModel: cfg.super_agent?.model || "Claude 3.5 Sonnet",
|
|
31
|
+
version: readPackageVersion(),
|
|
32
|
+
hasStarted: false,
|
|
33
|
+
sessionTitle: "",
|
|
34
|
+
usage: { input: 0, output: 0, percent: 0 },
|
|
35
|
+
transcript: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const previousMessages = [];
|
|
39
|
+
let restored = false;
|
|
40
|
+
let isRequesting = false;
|
|
41
|
+
|
|
42
|
+
function restoreTerminal() {
|
|
43
|
+
if (restored) return;
|
|
44
|
+
restored = true;
|
|
45
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
46
|
+
process.stdout.write(C.reset + C.showCursor + C.resetBg + C.altOff);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderScreen() {
|
|
50
|
+
state.sessionTitle = state.transcript.find((item) => item.type === "user")?.text || "";
|
|
51
|
+
renderTerminalChat(state);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resetPalette() {
|
|
55
|
+
state.paletteState = "main";
|
|
56
|
+
state.paletteOptions = [...MAIN_PALETTE_OPTIONS];
|
|
57
|
+
state.paletteSelection = 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function close() {
|
|
61
|
+
restoreTerminal();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
readline.emitKeypressEvents(process.stdin);
|
|
66
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
67
|
+
process.stdout.write(C.altOn + C.setBgBlack + C.showCursor + C.bg);
|
|
68
|
+
process.once("exit", restoreTerminal);
|
|
69
|
+
process.once("SIGINT", close);
|
|
70
|
+
process.once("SIGTERM", close);
|
|
71
|
+
process.stdout.on?.("resize", renderScreen);
|
|
72
|
+
process.on("SIGWINCH", renderScreen);
|
|
73
|
+
|
|
74
|
+
renderScreen();
|
|
75
|
+
|
|
76
|
+
process.stdin.on("keypress", async (str, key) => {
|
|
77
|
+
if (isRequesting) return;
|
|
78
|
+
|
|
79
|
+
if (key.ctrl && key.name === "c") {
|
|
80
|
+
close();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (key.ctrl && key.name === "p") {
|
|
84
|
+
state.inCommandPalette = !state.inCommandPalette;
|
|
85
|
+
resetPalette();
|
|
86
|
+
renderScreen();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (key.name === "escape" && state.inCommandPalette) {
|
|
91
|
+
if (state.paletteState !== "main") {
|
|
92
|
+
resetPalette();
|
|
93
|
+
} else {
|
|
94
|
+
state.inCommandPalette = false;
|
|
95
|
+
}
|
|
96
|
+
renderScreen();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (state.inCommandPalette) {
|
|
101
|
+
await handlePaletteKey(key, cfg, state, renderScreen, close);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isReturnKey(key)) {
|
|
106
|
+
isRequesting = true;
|
|
107
|
+
await submitPrompt(pid, state, previousMessages, renderScreen, close);
|
|
108
|
+
isRequesting = false;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (handleEditingKey(str, key, state, renderScreen)) return;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isReturnKey(key) {
|
|
117
|
+
return key?.name === "return" || key?.name === "enter";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function handlePaletteKey(key, cfg, state, renderScreen, close) {
|
|
121
|
+
if (key.name === "up") {
|
|
122
|
+
state.paletteSelection = Math.max(0, state.paletteSelection - 1);
|
|
123
|
+
renderScreen();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (key.name === "down") {
|
|
128
|
+
state.paletteSelection = Math.min(state.paletteOptions.length - 1, state.paletteSelection + 1);
|
|
129
|
+
renderScreen();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (key.name !== "return") {
|
|
134
|
+
renderScreen();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const selected = state.paletteOptions[state.paletteSelection];
|
|
139
|
+
|
|
140
|
+
if (state.paletteState === "main") {
|
|
141
|
+
if (selected === "Exit") close();
|
|
142
|
+
|
|
143
|
+
if (selected === "Switch model") {
|
|
144
|
+
state.paletteState = "switch_model";
|
|
145
|
+
state.paletteOptions = ["Loading models..."];
|
|
146
|
+
state.paletteSelection = 0;
|
|
147
|
+
renderScreen();
|
|
148
|
+
loadModelOptions(cfg, state, renderScreen);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
state.inCommandPalette = false;
|
|
153
|
+
state.transcript.push({ type: "status", text: `Executing command: ${selected} (not implemented yet)` });
|
|
154
|
+
renderScreen();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
state.paletteState === "switch_model" &&
|
|
160
|
+
!selected.startsWith("Loading") &&
|
|
161
|
+
!selected.startsWith("Failed") &&
|
|
162
|
+
!selected.startsWith("No ")
|
|
163
|
+
) {
|
|
164
|
+
state.activeModel = selected;
|
|
165
|
+
const configModule = await import("../../core/config.js");
|
|
166
|
+
const currentCfg = configModule.readConfig();
|
|
167
|
+
if (!currentCfg.super_agent) currentCfg.super_agent = {};
|
|
168
|
+
currentCfg.super_agent.model = selected;
|
|
169
|
+
configModule.writeConfig(currentCfg);
|
|
170
|
+
|
|
171
|
+
state.inCommandPalette = false;
|
|
172
|
+
state.transcript.push({ type: "status", text: `Model updated globally to ${selected}` });
|
|
173
|
+
renderScreen();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
renderScreen();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function loadModelOptions(cfg, state, renderScreen) {
|
|
181
|
+
const baseUrl = cfg.engines?.ollama?.base_url || "http://127.0.0.1:11434";
|
|
182
|
+
fetch(`${baseUrl}/api/tags`)
|
|
183
|
+
.then((r) => r.json())
|
|
184
|
+
.then((data) => {
|
|
185
|
+
state.paletteOptions = data.models?.length
|
|
186
|
+
? data.models.map((m) => "ollama:" + m.name)
|
|
187
|
+
: ["No Ollama models found"];
|
|
188
|
+
state.paletteOptions.push("openai:gpt-4o", "anthropic:claude-3-5-sonnet-20240620");
|
|
189
|
+
if (state.paletteState === "switch_model") renderScreen();
|
|
190
|
+
})
|
|
191
|
+
.catch(() => {
|
|
192
|
+
state.paletteOptions = ["Failed to load from Ollama", "openai:gpt-4o", "anthropic:claude-3-5-sonnet-20240620"];
|
|
193
|
+
if (state.paletteState === "switch_model") renderScreen();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function handleEditingKey(str, key, state, renderScreen) {
|
|
198
|
+
if (key.name === "tab") {
|
|
199
|
+
state.currentModeIdx = (state.currentModeIdx + 1) % MODES.length;
|
|
200
|
+
renderScreen();
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (key.ctrl && key.name === "a") {
|
|
205
|
+
state.cursorIndex = 0;
|
|
206
|
+
renderScreen();
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (key.ctrl && key.name === "e") {
|
|
211
|
+
state.cursorIndex = state.inputText.length;
|
|
212
|
+
renderScreen();
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (key.ctrl && key.name === "u") {
|
|
217
|
+
state.inputText = state.inputText.slice(state.cursorIndex);
|
|
218
|
+
state.cursorIndex = 0;
|
|
219
|
+
renderScreen();
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (key.ctrl && key.name === "w") {
|
|
224
|
+
const before = state.inputText.slice(0, state.cursorIndex).replace(/\s*\S+\s*$/, "");
|
|
225
|
+
state.inputText = before + state.inputText.slice(state.cursorIndex);
|
|
226
|
+
state.cursorIndex = before.length;
|
|
227
|
+
renderScreen();
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (key.name === "left") {
|
|
232
|
+
state.cursorIndex = Math.max(0, state.cursorIndex - 1);
|
|
233
|
+
renderScreen();
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (key.name === "right") {
|
|
238
|
+
state.cursorIndex = Math.min(state.inputText.length, state.cursorIndex + 1);
|
|
239
|
+
renderScreen();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (key.name === "home") {
|
|
244
|
+
state.cursorIndex = 0;
|
|
245
|
+
renderScreen();
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (key.name === "end") {
|
|
250
|
+
state.cursorIndex = state.inputText.length;
|
|
251
|
+
renderScreen();
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (key.name === "delete") {
|
|
256
|
+
state.inputText = state.inputText.slice(0, state.cursorIndex) + state.inputText.slice(state.cursorIndex + 1);
|
|
257
|
+
renderScreen();
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (key.name === "backspace") {
|
|
262
|
+
if (state.cursorIndex === 0) return true;
|
|
263
|
+
state.inputText = state.inputText.slice(0, state.cursorIndex - 1) + state.inputText.slice(state.cursorIndex);
|
|
264
|
+
state.cursorIndex -= 1;
|
|
265
|
+
renderScreen();
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (str && str.length === 1 && !key.ctrl && !key.meta && str >= " ") {
|
|
270
|
+
state.inputText = state.inputText.slice(0, state.cursorIndex) + str + state.inputText.slice(state.cursorIndex);
|
|
271
|
+
state.cursorIndex += str.length;
|
|
272
|
+
renderScreen();
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function submitPrompt(pid, state, previousMessages, renderScreen, close) {
|
|
280
|
+
const text = state.inputText.trim();
|
|
281
|
+
if (!text) return;
|
|
282
|
+
if (text.toLowerCase() === "exit" || text.toLowerCase() === "quit") {
|
|
283
|
+
close();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
state.hasStarted = true;
|
|
287
|
+
state.inputText = "";
|
|
288
|
+
state.cursorIndex = 0;
|
|
289
|
+
state.transcript.push({ type: "user", text });
|
|
290
|
+
state.transcript.push({ type: "status", text: "Thinking..." });
|
|
291
|
+
renderScreen();
|
|
292
|
+
|
|
293
|
+
const startTime = Date.now();
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const body = {
|
|
297
|
+
prompt: `[Mode: ${MODES[state.currentModeIdx]}]\n${text}`,
|
|
298
|
+
contextNote: "Channel: terminal. Format freely using markdown, but keep it readable. Use code diffs when editing.",
|
|
299
|
+
previousMessages,
|
|
300
|
+
model: state.activeModel,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const result = await http.post(`/projects/${pid}/super-agent/chat`, body);
|
|
304
|
+
state.transcript.pop();
|
|
305
|
+
for (const trace of result.trace || []) {
|
|
306
|
+
state.transcript.push({ type: "tool", trace });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
previousMessages.push({ role: "user", content: text });
|
|
310
|
+
previousMessages.push({ role: "assistant", content: result.text });
|
|
311
|
+
if (previousMessages.length > 20) previousMessages.splice(0, previousMessages.length - 20);
|
|
312
|
+
|
|
313
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
314
|
+
state.usage.input += result.usage?.input_tokens || 0;
|
|
315
|
+
state.usage.output += result.usage?.output_tokens || 0;
|
|
316
|
+
state.usage.percent = Math.min(99, Math.round((state.usage.input / 200000) * 100));
|
|
317
|
+
|
|
318
|
+
state.transcript.push({
|
|
319
|
+
type: "assistant",
|
|
320
|
+
name: state.activeAgent,
|
|
321
|
+
text: result.text,
|
|
322
|
+
meta: `${elapsed}s · In: ${result.usage?.input_tokens || 0} Out: ${result.usage?.output_tokens || 0}`,
|
|
323
|
+
});
|
|
324
|
+
} catch (e) {
|
|
325
|
+
state.transcript.pop();
|
|
326
|
+
state.transcript.push({ type: "error", text: e.message });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
renderScreen();
|
|
330
|
+
}
|
package/src/cli/index.js
CHANGED
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
cmdConversationsList,
|
|
61
61
|
cmdConversationsGet,
|
|
62
62
|
} from "./commands/chat.js";
|
|
63
|
+
import { cmdSys } from "./commands/sys.js";
|
|
63
64
|
import { cmdRun, cmdEnvDetect } from "./commands/runtime.js";
|
|
64
65
|
import { cmdSend, cmdConnections } from "./commands/a2a.js";
|
|
65
66
|
import {
|
|
@@ -87,6 +88,12 @@ import {
|
|
|
87
88
|
cmdRoutineRun,
|
|
88
89
|
cmdRoutineHistory,
|
|
89
90
|
} from "./commands/routine.js";
|
|
91
|
+
import {
|
|
92
|
+
cmdArtifactCreate,
|
|
93
|
+
cmdArtifactList,
|
|
94
|
+
cmdArtifactShow,
|
|
95
|
+
cmdArtifactRemove,
|
|
96
|
+
} from "./commands/artifact.js";
|
|
90
97
|
|
|
91
98
|
const __filename = fileURLToPath(import.meta.url);
|
|
92
99
|
const __dirname = path.dirname(__filename);
|
|
@@ -695,6 +702,15 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
695
702
|
],
|
|
696
703
|
examples: ["apx chat reviewer", "apx chat reviewer --conversation abc123"],
|
|
697
704
|
}),
|
|
705
|
+
sys: topic({
|
|
706
|
+
title: "apx sys",
|
|
707
|
+
summary: "Start an interactive super-agent chat REPL with system and workspace context.",
|
|
708
|
+
usage: ["apx sys [--project <name|id|path>]"],
|
|
709
|
+
options: [
|
|
710
|
+
["--project <name|id|path>", "Pin command to a specific project."],
|
|
711
|
+
],
|
|
712
|
+
examples: ["apx sys", "apx sys --project default"],
|
|
713
|
+
}),
|
|
698
714
|
conversations: topic({
|
|
699
715
|
title: "apx conversations",
|
|
700
716
|
summary: "List or read stored agent conversations.",
|
|
@@ -729,9 +745,9 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
729
745
|
run: topic({
|
|
730
746
|
title: "apx run",
|
|
731
747
|
summary: "Launch a full external runtime session for an agent.",
|
|
732
|
-
usage: ["apx run <agent> --runtime <claude-code|codex|opencode|aider> \"<prompt>\" [--timeout <seconds>] [--project <name|id|path>]"],
|
|
748
|
+
usage: ["apx run <agent> --runtime <claude-code|codex|opencode|aider|cursor-agent|gemini-cli|qwen-code> \"<prompt>\" [--timeout <seconds>] [--project <name|id|path>]"],
|
|
733
749
|
options: [
|
|
734
|
-
["--runtime <id>", "Runtime CLI to launch: claude-code, codex, opencode, aider."],
|
|
750
|
+
["--runtime <id>", "Runtime CLI to launch: claude-code, codex, opencode, aider, cursor-agent, gemini-cli, qwen-code."],
|
|
735
751
|
["--timeout <seconds>", "Runtime timeout."],
|
|
736
752
|
["--project <name|id|path>", "Pin command to a specific project."],
|
|
737
753
|
],
|
|
@@ -806,16 +822,22 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
806
822
|
"routine add": topic({
|
|
807
823
|
title: "apx routine add",
|
|
808
824
|
summary: "Create a scheduled routine.",
|
|
809
|
-
usage: ["apx routine add <name> --kind <kind> --schedule <schedule> [--spec '<json>'] [--
|
|
825
|
+
usage: ["apx routine add <name> --kind <kind> --schedule <schedule> [--spec '<json>'] [--pre-commands 'cmd1,cmd2'] [--post-commands 'cmd'] [--skip-prompt-on signal|pre_failure|pre_success|always|never] [--project <name|id|path>]"],
|
|
810
826
|
options: [
|
|
811
827
|
["--kind <kind>", "heartbeat, exec_agent, super_agent, telegram, or shell."],
|
|
812
828
|
["--schedule <schedule>", "every:60s, every:5m, every:1h, or once:<iso>."],
|
|
813
829
|
["--spec '<json>'", "Routine-specific JSON config."],
|
|
830
|
+
["--pre-commands 'cmd'", "Comma-separated shell commands to run BEFORE the LLM. Use 'artifact:<name>' shorthand."],
|
|
831
|
+
["--post-commands 'cmd'", "Comma-separated shell commands to run AFTER the LLM. Env: APX_LLM_OUTPUT, APX_PRE_OUTPUT, APX_STATUS."],
|
|
832
|
+
["--skip-prompt-on <mode>", "signal (default), pre_failure, pre_success, always, never."],
|
|
814
833
|
["--permission-mode <mode>", "total, automatico, or permiso."],
|
|
815
834
|
["--allowed-tools a,b", "Comma-separated allowed tool names."],
|
|
816
835
|
["--project <name|id|path>", "Pin command to a specific project."],
|
|
817
836
|
],
|
|
818
|
-
examples: [
|
|
837
|
+
examples: [
|
|
838
|
+
"apx routine add check --kind shell --schedule every:1h --spec '{\"cmd\":\"npm test\"}'",
|
|
839
|
+
"apx routine add asana-check --kind super_agent --schedule '*/5 * * * *' --pre-commands 'artifact:check_asana.sh' --skip-prompt-on pre_success --project 0",
|
|
840
|
+
],
|
|
819
841
|
}),
|
|
820
842
|
"routine get": topic({
|
|
821
843
|
title: "apx routine get",
|
|
@@ -859,6 +881,53 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
859
881
|
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
860
882
|
examples: ["apx routine remove check"],
|
|
861
883
|
}),
|
|
884
|
+
artifact: topic({
|
|
885
|
+
title: "apx artifact",
|
|
886
|
+
summary: "Managed files stored in project storage. Used as pre/post-commands scripts or data files for routines.",
|
|
887
|
+
usage: ["apx artifact <subcommand> [args] [--flags]"],
|
|
888
|
+
commands: [
|
|
889
|
+
["create <name>", "Create a new empty artifact. Prints its absolute path."],
|
|
890
|
+
["list | ls", "List artifacts in the project."],
|
|
891
|
+
["show <name>", "Print artifact content."],
|
|
892
|
+
["remove | rm <name>", "Delete an artifact."],
|
|
893
|
+
],
|
|
894
|
+
examples: [
|
|
895
|
+
"apx artifact create check_asana.sh --project 0",
|
|
896
|
+
"apx artifact list",
|
|
897
|
+
"apx artifact show check_asana.sh",
|
|
898
|
+
],
|
|
899
|
+
}),
|
|
900
|
+
"artifact create": topic({
|
|
901
|
+
title: "apx artifact create",
|
|
902
|
+
summary: "Create a new managed artifact file.",
|
|
903
|
+
usage: ["apx artifact create <name> [--content 'text'] [--project <name|id|path>]"],
|
|
904
|
+
options: [
|
|
905
|
+
["--content 'text'", "Initial file content (default: empty)."],
|
|
906
|
+
["--project <name|id|path>", "Pin command to a specific project."],
|
|
907
|
+
],
|
|
908
|
+
examples: ["apx artifact create check_asana.sh --project 0"],
|
|
909
|
+
}),
|
|
910
|
+
"artifact list": topic({
|
|
911
|
+
title: "apx artifact list",
|
|
912
|
+
summary: "List artifacts in the project storage.",
|
|
913
|
+
usage: ["apx artifact list [--project <name|id|path>]"],
|
|
914
|
+
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
915
|
+
examples: ["apx artifact list"],
|
|
916
|
+
}),
|
|
917
|
+
"artifact show": topic({
|
|
918
|
+
title: "apx artifact show",
|
|
919
|
+
summary: "Print artifact content.",
|
|
920
|
+
usage: ["apx artifact show <name> [--project <name|id|path>]"],
|
|
921
|
+
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
922
|
+
examples: ["apx artifact show check_asana.sh"],
|
|
923
|
+
}),
|
|
924
|
+
"artifact remove": topic({
|
|
925
|
+
title: "apx artifact remove",
|
|
926
|
+
summary: "Delete an artifact.",
|
|
927
|
+
usage: ["apx artifact remove <name> [--project <name|id|path>]", "apx artifact rm <name>"],
|
|
928
|
+
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
929
|
+
examples: ["apx artifact remove check_asana.sh"],
|
|
930
|
+
}),
|
|
862
931
|
command: topic({
|
|
863
932
|
title: "apx command",
|
|
864
933
|
summary: "List or show workflow commands from .apc/commands/.",
|
|
@@ -1073,12 +1142,13 @@ function buildHelp(version) {
|
|
|
1073
1142
|
hSec("LLM / Chat"),
|
|
1074
1143
|
hCmd("apx exec <agent> \"prompt\"",36, "--model <id> --max-tokens N --temperature T"),
|
|
1075
1144
|
hCmd("apx chat <agent>", 36, "--conversation <id> --model <id> (interactive REPL)"),
|
|
1145
|
+
hCmd("apx sys", 36, "super-agent chat w/ system+workspace context"),
|
|
1076
1146
|
hCmd("apx conversations list", 36, "<agent>"),
|
|
1077
1147
|
hCmd("apx conversations get", 36, "<agent> <id>"),
|
|
1078
1148
|
|
|
1079
1149
|
hSec("Runtimes"),
|
|
1080
1150
|
hCmd("apx run <agent>", 36, "--runtime <id> \"prompt\" --timeout <s>"),
|
|
1081
|
-
` ${H.DI}runtimes: claude-code | codex | opencode | aider${H.R}`,
|
|
1151
|
+
` ${H.DI}runtimes: claude-code | codex | opencode | aider | cursor-agent | gemini-cli | qwen-code${H.R}`,
|
|
1082
1152
|
hCmd("apx env detect", 36, "which agent CLIs are installed"),
|
|
1083
1153
|
|
|
1084
1154
|
hSec("Agent-to-Agent"),
|
|
@@ -1086,11 +1156,13 @@ function buildHelp(version) {
|
|
|
1086
1156
|
hCmd("apx connections <agent>", 36, "mental map: who/how/when this agent talked"),
|
|
1087
1157
|
hCmd("apx graph <agent>", 36, "alias for connections"),
|
|
1088
1158
|
|
|
1089
|
-
hSec("Routines"),
|
|
1159
|
+
hSec("Routines & Pipeline"),
|
|
1090
1160
|
hCmd("apx routine list", 36, "list routines + next/last run"),
|
|
1091
1161
|
hCmd("apx routine add <name>", 36, "--kind K --schedule S [--spec '{...}']"),
|
|
1092
1162
|
` ${H.DI}kinds: heartbeat | exec_agent | super_agent | telegram | shell${H.R}`,
|
|
1093
1163
|
` ${H.DI}flags: --permission-mode total|automatico|permiso --allowed-tools a,b${H.R}`,
|
|
1164
|
+
` ${H.DI}pipeline: --pre-commands 'cmd1,cmd2' --post-commands 'cmd'${H.R}`,
|
|
1165
|
+
` ${H.DI} --skip-prompt-on signal|pre_failure|pre_success|always|never${H.R}`,
|
|
1094
1166
|
` ${H.DI}schedule: every:60s | every:5m | every:1h | once:<iso>${H.R}`,
|
|
1095
1167
|
hCmd("apx routine get <name>", 36, ""),
|
|
1096
1168
|
hCmd("apx routine history <name>", 36, "show routine execution history"),
|
|
@@ -1099,6 +1171,12 @@ function buildHelp(version) {
|
|
|
1099
1171
|
hCmd("apx routine disable <name>", 36, ""),
|
|
1100
1172
|
hCmd("apx routine remove <name>", 36, ""),
|
|
1101
1173
|
|
|
1174
|
+
hSec("Artifacts"),
|
|
1175
|
+
hCmd("apx artifact create <name>", 36, "create managed file in project storage [--content '...'] [--project 0]"),
|
|
1176
|
+
hCmd("apx artifact list", 36, "list artifacts"),
|
|
1177
|
+
hCmd("apx artifact show <name>", 36, "print artifact content"),
|
|
1178
|
+
hCmd("apx artifact remove <name>", 36, ""),
|
|
1179
|
+
|
|
1102
1180
|
hSec("Commands & Skills"),
|
|
1103
1181
|
hCmd("apx command list", 36, "list workflow commands (.apc/commands/)"),
|
|
1104
1182
|
hCmd("apx command show <name>", 36, "print command content"),
|
|
@@ -1374,6 +1452,10 @@ async function dispatch(cmd, rest) {
|
|
|
1374
1452
|
await cmdChat(parseArgs(rest));
|
|
1375
1453
|
break;
|
|
1376
1454
|
|
|
1455
|
+
case "sys":
|
|
1456
|
+
await cmdSys(parseArgs(rest));
|
|
1457
|
+
break;
|
|
1458
|
+
|
|
1377
1459
|
case "conversations":
|
|
1378
1460
|
case "conv": {
|
|
1379
1461
|
const sub = rest[0];
|
|
@@ -1445,6 +1527,18 @@ async function dispatch(cmd, rest) {
|
|
|
1445
1527
|
break;
|
|
1446
1528
|
}
|
|
1447
1529
|
|
|
1530
|
+
case "artifact":
|
|
1531
|
+
case "artifacts": {
|
|
1532
|
+
const sub = rest[0];
|
|
1533
|
+
const a = parseArgs(rest.slice(1));
|
|
1534
|
+
if (!sub || sub === "list" || sub === "ls") await cmdArtifactList(a);
|
|
1535
|
+
else if (sub === "create" || sub === "new") await cmdArtifactCreate(a);
|
|
1536
|
+
else if (sub === "show" || sub === "get") await cmdArtifactShow(a);
|
|
1537
|
+
else if (sub === "remove" || sub === "rm") await cmdArtifactRemove(a);
|
|
1538
|
+
else die(`unknown artifact subcommand: ${sub}`);
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1448
1542
|
case "command":
|
|
1449
1543
|
case "commands": {
|
|
1450
1544
|
const sub = rest[0];
|