@heyamiko/amiko-cli 0.4.11 → 0.4.12
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/dist/cli.js +8 -3
- package/dist/headless.js +55 -46
- package/package.json +1 -1
- package/src/cli.tsx +9 -3
- package/src/headless.ts +64 -43
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,11 @@ function parseArgs(argv) {
|
|
|
15
15
|
case "--headless":
|
|
16
16
|
overrides.headless = true;
|
|
17
17
|
break;
|
|
18
|
+
case "--message":
|
|
19
|
+
case "-m":
|
|
20
|
+
overrides.message = argv[i + 1];
|
|
21
|
+
i += 1;
|
|
22
|
+
break;
|
|
18
23
|
case "--workspace":
|
|
19
24
|
case "-w":
|
|
20
25
|
overrides.workspacePath = argv[i + 1];
|
|
@@ -80,9 +85,9 @@ async function main() {
|
|
|
80
85
|
const persisted = await loadConfigFile(workspace.root);
|
|
81
86
|
const mergedConfig = { ...finalConfig, ...persisted, ...overrides };
|
|
82
87
|
const sessionFile = await createSessionFile(workspace.sessionsDir, finalConfig.sessionId);
|
|
83
|
-
// Auto-detect non-TTY
|
|
84
|
-
if (overrides.headless || !process.stdout.isTTY) {
|
|
85
|
-
await runHeadless(mergedConfig, workspace, sessionFile.path);
|
|
88
|
+
// Auto-detect non-TTY, --headless flag, or -m single-shot
|
|
89
|
+
if (overrides.headless || overrides.message || !process.stdout.isTTY) {
|
|
90
|
+
await runHeadless(mergedConfig, workspace, sessionFile.path, overrides.message);
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
93
|
render(_jsx(App, { config: mergedConfig, workspace: workspace, sessionPath: sessionFile.path }));
|
package/dist/headless.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createPiAgent } from "./agent/piAgent.js";
|
|
|
7
7
|
import { qmdSearch, qmdGet, qmdCollectionAdd, qmdEmbed } from "./memory/qmd.js";
|
|
8
8
|
import { saveConfigFile } from "./configFile.js";
|
|
9
9
|
import { getModels, getProviders } from "@mariozechner/pi-ai";
|
|
10
|
-
export async function runHeadless(config, workspace, sessionPath) {
|
|
10
|
+
export async function runHeadless(config, workspace, sessionPath, singleMessage) {
|
|
11
11
|
const agent = await createPiAgent(config);
|
|
12
12
|
let currentModel = config.model
|
|
13
13
|
?? (config.modelProvider && config.modelId ? `${config.modelProvider}/${config.modelId}` : "auto");
|
|
@@ -33,6 +33,13 @@ export async function runHeadless(config, workspace, sessionPath) {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
+
// Single-shot mode: process one message and exit
|
|
37
|
+
if (singleMessage) {
|
|
38
|
+
const response = await processMessage(singleMessage, { config, workspace, sessionPath, agent, currentModel });
|
|
39
|
+
if (response !== undefined)
|
|
40
|
+
process.stdout.write(response.endsWith("\n") ? response : response + "\n");
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
36
43
|
console.log(`amiko-cli (headless) | model: ${currentModel} | workspace: ${workspace.root}`);
|
|
37
44
|
console.log("Type a message or /command. Press Ctrl+D to exit.");
|
|
38
45
|
const rl = createInterface({
|
|
@@ -57,57 +64,59 @@ export async function runHeadless(config, workspace, sessionPath) {
|
|
|
57
64
|
rl.prompt();
|
|
58
65
|
continue;
|
|
59
66
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let hits = [];
|
|
64
|
-
if (config.qmdEnabled) {
|
|
65
|
-
hits = await qmdSearch({
|
|
66
|
-
qmdBin: config.qmdBin,
|
|
67
|
-
query: trimmed,
|
|
68
|
-
limit: config.qmdLimit,
|
|
69
|
-
collection: config.qmdCollection,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
if (config.qmdFull && hits.length > 0) {
|
|
73
|
-
const expanded = [];
|
|
74
|
-
for (const hit of hits) {
|
|
75
|
-
const docId = hit.path || hit.id;
|
|
76
|
-
if (!docId) {
|
|
77
|
-
expanded.push(hit);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const text = await qmdGet({ qmdBin: config.qmdBin, docId, full: true });
|
|
81
|
-
expanded.push({ ...hit, snippet: text || hit.snippet });
|
|
82
|
-
}
|
|
83
|
-
hits = expanded;
|
|
67
|
+
const response = await processMessage(trimmed, { config, workspace, sessionPath, agent, currentModel });
|
|
68
|
+
if (response) {
|
|
69
|
+
process.stdout.write(response.endsWith("\n") ? response : response + "\n");
|
|
84
70
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
rl.prompt();
|
|
72
|
+
}
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
async function processMessage(message, ctx) {
|
|
76
|
+
await appendSession(ctx.sessionPath, "user", message);
|
|
77
|
+
const workspaceData = await readWorkspaceFiles(ctx.workspace);
|
|
78
|
+
let hits = [];
|
|
79
|
+
if (ctx.config.qmdEnabled) {
|
|
80
|
+
hits = await qmdSearch({
|
|
81
|
+
qmdBin: ctx.config.qmdBin,
|
|
82
|
+
query: message,
|
|
83
|
+
limit: ctx.config.qmdLimit,
|
|
84
|
+
collection: ctx.config.qmdCollection,
|
|
90
85
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
}
|
|
87
|
+
if (ctx.config.qmdFull && hits.length > 0) {
|
|
88
|
+
const expanded = [];
|
|
89
|
+
for (const hit of hits) {
|
|
90
|
+
const docId = hit.path || hit.id;
|
|
91
|
+
if (!docId) {
|
|
92
|
+
expanded.push(hit);
|
|
93
|
+
continue;
|
|
96
94
|
}
|
|
95
|
+
const text = await qmdGet({ qmdBin: ctx.config.qmdBin, docId, full: true });
|
|
96
|
+
expanded.push({ ...hit, snippet: text || hit.snippet });
|
|
97
97
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
hits = expanded;
|
|
99
|
+
}
|
|
100
|
+
const prompt = buildPrompt({
|
|
101
|
+
identity: workspaceData.identity,
|
|
102
|
+
personality: workspaceData.personality,
|
|
103
|
+
memory: workspaceData.memory,
|
|
104
|
+
hits,
|
|
105
|
+
});
|
|
106
|
+
let assistantText = "";
|
|
107
|
+
try {
|
|
108
|
+
for await (const chunk of ctx.agent.streamReply({ prompt, userMessage: message })) {
|
|
109
|
+
assistantText += chunk;
|
|
107
110
|
}
|
|
108
|
-
rl.prompt();
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
catch (error) {
|
|
113
|
+
const msg = error.message || "unknown error";
|
|
114
|
+
assistantText += `\n[error] ${msg}\n`;
|
|
115
|
+
}
|
|
116
|
+
if (assistantText) {
|
|
117
|
+
await appendSession(ctx.sessionPath, "assistant", assistantText);
|
|
118
|
+
}
|
|
119
|
+
return assistantText;
|
|
111
120
|
}
|
|
112
121
|
async function handleCommand(name, args, ctx) {
|
|
113
122
|
switch (name) {
|
package/package.json
CHANGED
package/src/cli.tsx
CHANGED
|
@@ -21,6 +21,7 @@ type ArgOverrides = Partial<{
|
|
|
21
21
|
modelId?: string;
|
|
22
22
|
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
23
23
|
headless?: boolean;
|
|
24
|
+
message?: string;
|
|
24
25
|
}>;
|
|
25
26
|
|
|
26
27
|
function parseArgs(argv: string[]): ArgOverrides {
|
|
@@ -31,6 +32,11 @@ function parseArgs(argv: string[]): ArgOverrides {
|
|
|
31
32
|
case "--headless":
|
|
32
33
|
overrides.headless = true;
|
|
33
34
|
break;
|
|
35
|
+
case "--message":
|
|
36
|
+
case "-m":
|
|
37
|
+
overrides.message = argv[i + 1];
|
|
38
|
+
i += 1;
|
|
39
|
+
break;
|
|
34
40
|
case "--workspace":
|
|
35
41
|
case "-w":
|
|
36
42
|
overrides.workspacePath = argv[i + 1];
|
|
@@ -99,9 +105,9 @@ async function main() {
|
|
|
99
105
|
const mergedConfig = { ...finalConfig, ...persisted, ...overrides };
|
|
100
106
|
const sessionFile = await createSessionFile(workspace.sessionsDir, finalConfig.sessionId);
|
|
101
107
|
|
|
102
|
-
// Auto-detect non-TTY
|
|
103
|
-
if (overrides.headless || !process.stdout.isTTY) {
|
|
104
|
-
await runHeadless(mergedConfig, workspace, sessionFile.path);
|
|
108
|
+
// Auto-detect non-TTY, --headless flag, or -m single-shot
|
|
109
|
+
if (overrides.headless || overrides.message || !process.stdout.isTTY) {
|
|
110
|
+
await runHeadless(mergedConfig, workspace, sessionFile.path, overrides.message);
|
|
105
111
|
return;
|
|
106
112
|
}
|
|
107
113
|
|
package/src/headless.ts
CHANGED
|
@@ -13,6 +13,7 @@ export async function runHeadless(
|
|
|
13
13
|
config: AppConfig,
|
|
14
14
|
workspace: WorkspaceFiles,
|
|
15
15
|
sessionPath: string,
|
|
16
|
+
singleMessage?: string,
|
|
16
17
|
) {
|
|
17
18
|
const agent = await createPiAgent(config);
|
|
18
19
|
let currentModel = config.model
|
|
@@ -40,6 +41,13 @@ export async function runHeadless(
|
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
// Single-shot mode: process one message and exit
|
|
45
|
+
if (singleMessage) {
|
|
46
|
+
const response = await processMessage(singleMessage, { config, workspace, sessionPath, agent, currentModel });
|
|
47
|
+
if (response !== undefined) process.stdout.write(response.endsWith("\n") ? response : response + "\n");
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
console.log(`amiko-cli (headless) | model: ${currentModel} | workspace: ${workspace.root}`);
|
|
44
52
|
console.log("Type a message or /command. Press Ctrl+D to exit.");
|
|
45
53
|
|
|
@@ -69,61 +77,74 @@ export async function runHeadless(
|
|
|
69
77
|
continue;
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const workspaceData = await readWorkspaceFiles(workspace);
|
|
76
|
-
|
|
77
|
-
let hits: MemoryHit[] = [];
|
|
78
|
-
if (config.qmdEnabled) {
|
|
79
|
-
hits = await qmdSearch({
|
|
80
|
-
qmdBin: config.qmdBin,
|
|
81
|
-
query: trimmed,
|
|
82
|
-
limit: config.qmdLimit,
|
|
83
|
-
collection: config.qmdCollection,
|
|
84
|
-
});
|
|
80
|
+
const response = await processMessage(trimmed, { config, workspace, sessionPath, agent, currentModel });
|
|
81
|
+
if (response) {
|
|
82
|
+
process.stdout.write(response.endsWith("\n") ? response : response + "\n");
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const text = await qmdGet({ qmdBin: config.qmdBin, docId, full: true });
|
|
93
|
-
expanded.push({ ...hit, snippet: text || hit.snippet });
|
|
94
|
-
}
|
|
95
|
-
hits = expanded;
|
|
96
|
-
}
|
|
85
|
+
rl.prompt();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
91
|
+
async function processMessage(
|
|
92
|
+
message: string,
|
|
93
|
+
ctx: {
|
|
94
|
+
config: AppConfig;
|
|
95
|
+
workspace: WorkspaceFiles;
|
|
96
|
+
sessionPath: string;
|
|
97
|
+
agent: any;
|
|
98
|
+
currentModel: string;
|
|
99
|
+
},
|
|
100
|
+
): Promise<string> {
|
|
101
|
+
await appendSession(ctx.sessionPath, "user", message);
|
|
102
|
+
|
|
103
|
+
const workspaceData = await readWorkspaceFiles(ctx.workspace);
|
|
104
|
+
|
|
105
|
+
let hits: MemoryHit[] = [];
|
|
106
|
+
if (ctx.config.qmdEnabled) {
|
|
107
|
+
hits = await qmdSearch({
|
|
108
|
+
qmdBin: ctx.config.qmdBin,
|
|
109
|
+
query: message,
|
|
110
|
+
limit: ctx.config.qmdLimit,
|
|
111
|
+
collection: ctx.config.qmdCollection,
|
|
103
112
|
});
|
|
113
|
+
}
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const message = (error as Error).message || "unknown error";
|
|
113
|
-
assistantText += `\n[error] ${message}\n`;
|
|
114
|
-
console.error(`[error] ${message}`);
|
|
115
|
+
if (ctx.config.qmdFull && hits.length > 0) {
|
|
116
|
+
const expanded: MemoryHit[] = [];
|
|
117
|
+
for (const hit of hits) {
|
|
118
|
+
const docId = hit.path || hit.id;
|
|
119
|
+
if (!docId) { expanded.push(hit); continue; }
|
|
120
|
+
const text = await qmdGet({ qmdBin: ctx.config.qmdBin, docId, full: true });
|
|
121
|
+
expanded.push({ ...hit, snippet: text || hit.snippet });
|
|
115
122
|
}
|
|
123
|
+
hits = expanded;
|
|
124
|
+
}
|
|
116
125
|
|
|
117
|
-
|
|
126
|
+
const prompt = buildPrompt({
|
|
127
|
+
identity: workspaceData.identity,
|
|
128
|
+
personality: workspaceData.personality,
|
|
129
|
+
memory: workspaceData.memory,
|
|
130
|
+
hits,
|
|
131
|
+
});
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
let assistantText = "";
|
|
134
|
+
try {
|
|
135
|
+
for await (const chunk of ctx.agent.streamReply({ prompt, userMessage: message })) {
|
|
136
|
+
assistantText += chunk;
|
|
121
137
|
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const msg = (error as Error).message || "unknown error";
|
|
140
|
+
assistantText += `\n[error] ${msg}\n`;
|
|
141
|
+
}
|
|
122
142
|
|
|
123
|
-
|
|
143
|
+
if (assistantText) {
|
|
144
|
+
await appendSession(ctx.sessionPath, "assistant", assistantText);
|
|
124
145
|
}
|
|
125
146
|
|
|
126
|
-
|
|
147
|
+
return assistantText;
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
async function handleCommand(
|