@matterailab/orbcode 0.2.3 → 0.2.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/dist/core/agent.js +10 -4
- package/dist/headless.js +2 -1
- package/dist/index.js +36 -2
- package/dist/ui/App.js +15 -14
- package/package.json +1 -1
package/dist/core/agent.js
CHANGED
|
@@ -114,10 +114,13 @@ export class Agent {
|
|
|
114
114
|
this.mcp = options.mcp;
|
|
115
115
|
// Build the system prompt with AGENTS.md memory files and the skills
|
|
116
116
|
// catalog injected, so the model sees project/user instructions and
|
|
117
|
-
// knows which skills it can invoke.
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
117
|
+
// knows which skills it can invoke. An explicit override (from
|
|
118
|
+
// `orbcode -s <text>`) bypasses the default entirely.
|
|
119
|
+
const memoryFiles = options.systemPromptOverride ? [] : loadMemoryFiles(options.cwd);
|
|
120
|
+
const skills = options.systemPromptOverride ? new Map() : loadSkills(options.cwd);
|
|
121
|
+
this.systemPrompt = options.systemPromptOverride
|
|
122
|
+
? options.systemPromptOverride
|
|
123
|
+
: buildSystemPrompt(options.cwd, { memoryFiles, skills });
|
|
121
124
|
this.client = createLLMClient({
|
|
122
125
|
token: options.token,
|
|
123
126
|
modelId: options.modelId,
|
|
@@ -684,6 +687,9 @@ User time zone: ${timeZone}, UTC${timeZoneOffsetStr}`;
|
|
|
684
687
|
this.sessionApproveCommands = true;
|
|
685
688
|
}
|
|
686
689
|
}
|
|
690
|
+
// Yield so the UI can paint the "Working" indicator before a
|
|
691
|
+
// potentially long synchronous tool call blocks the event loop.
|
|
692
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
687
693
|
const result = await executeTool(toolCall.name, args, this.toolContext(), this.mcp);
|
|
688
694
|
// PostToolUse can feed extra context (or a block reason) back to the
|
|
689
695
|
// model. PreToolUse additionalContext is delivered here too.
|
package/dist/headless.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getAuthToken, getPendingProjectHooks, loadSettings } from "./config/set
|
|
|
3
3
|
import { Agent } from "./core/agent.js";
|
|
4
4
|
import { McpManager } from "./mcp/manager.js";
|
|
5
5
|
/** Non-interactive `orbcode -p "prompt"` mode: prints the final response to stdout. */
|
|
6
|
-
export async function runHeadless(prompt, yolo) {
|
|
6
|
+
export async function runHeadless(prompt, yolo, systemPromptOverride) {
|
|
7
7
|
const settings = loadSettings();
|
|
8
8
|
// An unknown --model (or MATTERAI_MODEL) silently resolves to the default; say
|
|
9
9
|
// so on stderr instead of quietly running a different model than requested.
|
|
@@ -63,6 +63,7 @@ export async function runHeadless(prompt, yolo) {
|
|
|
63
63
|
autoApproveSafeCommands: yolo,
|
|
64
64
|
hooks: settings.hooks,
|
|
65
65
|
mcp,
|
|
66
|
+
systemPromptOverride,
|
|
66
67
|
callbacks: {
|
|
67
68
|
onEvent: (event) => {
|
|
68
69
|
switch (event.type) {
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,9 @@ Usage:
|
|
|
23
23
|
orbcode -p "…" --yolo non-interactive with auto-approved edits/commands
|
|
24
24
|
orbcode --model <id> use a specific model for this run
|
|
25
25
|
orbcode --resume <id> resume a previous session by id
|
|
26
|
+
orbcode -s "<prompt>" override the default system prompt (replaces it entirely;
|
|
27
|
+
accepts -s <text>, --system-prompt <text>, and
|
|
28
|
+
--system-prompt=<text> for values that start with '-')
|
|
26
29
|
orbcode --version print version
|
|
27
30
|
orbcode --help show this help
|
|
28
31
|
`);
|
|
@@ -71,6 +74,33 @@ function takeFlagValue(args, name) {
|
|
|
71
74
|
args.splice(index, 2);
|
|
72
75
|
return value;
|
|
73
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Pop the `-s` / `--system-prompt` flag (and its value) out of `args`.
|
|
79
|
+
* Unlike `takeFlagValue` this accepts a value that starts with `-`, because
|
|
80
|
+
* a system-prompt override is free-form text. Supports three forms:
|
|
81
|
+
* -s "<text>" --value in next arg
|
|
82
|
+
* --system-prompt "<text>"
|
|
83
|
+
* --system-prompt="<text>"
|
|
84
|
+
* --system-prompt=-<text>
|
|
85
|
+
*/
|
|
86
|
+
function takeSystemPrompt(args) {
|
|
87
|
+
const longWithEq = args.findIndex((a) => a.startsWith("--system-prompt="));
|
|
88
|
+
if (longWithEq !== -1) {
|
|
89
|
+
const value = args[longWithEq].slice("--system-prompt=".length);
|
|
90
|
+
args.splice(longWithEq, 1);
|
|
91
|
+
return value.length > 0 ? value : undefined;
|
|
92
|
+
}
|
|
93
|
+
const index = args.findIndex((a) => a === "-s" || a === "--system-prompt");
|
|
94
|
+
if (index === -1)
|
|
95
|
+
return undefined;
|
|
96
|
+
const value = args[index + 1];
|
|
97
|
+
if (value === undefined) {
|
|
98
|
+
console.error("Missing value after -s / --system-prompt");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
args.splice(index, 2);
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
74
104
|
async function main() {
|
|
75
105
|
// Override Node's default process title so terminals (iTerm2 "current job
|
|
76
106
|
// name", VSCode terminal status, etc.) don't append " (node)" next to our
|
|
@@ -110,6 +140,10 @@ async function main() {
|
|
|
110
140
|
process.exit(1);
|
|
111
141
|
}
|
|
112
142
|
}
|
|
143
|
+
// `-s` / `--system-prompt` lets the user replace the default prompt
|
|
144
|
+
// entirely. Accepts values starting with `-` (unlike other flags), so
|
|
145
|
+
// `orbcode -s '- you are a code reviewer'` works.
|
|
146
|
+
const systemPromptOverride = takeSystemPrompt(args);
|
|
113
147
|
const printIndex = args.findIndex((a) => a === "-p" || a === "--print");
|
|
114
148
|
if (printIndex !== -1) {
|
|
115
149
|
const prompt = args[printIndex + 1];
|
|
@@ -117,7 +151,7 @@ async function main() {
|
|
|
117
151
|
console.error("Missing prompt after -p");
|
|
118
152
|
process.exit(1);
|
|
119
153
|
}
|
|
120
|
-
await runHeadless(prompt, args.includes("--yolo"));
|
|
154
|
+
await runHeadless(prompt, args.includes("--yolo"), systemPromptOverride);
|
|
121
155
|
return;
|
|
122
156
|
}
|
|
123
157
|
const initialView = args[0] === "login" ? "login" : undefined;
|
|
@@ -132,7 +166,7 @@ async function main() {
|
|
|
132
166
|
// Fire-and-forget: a stale or no-network state just means the header shows
|
|
133
167
|
// the "current version" line instead of an upgrade prompt.
|
|
134
168
|
const updateCheckPromise = getUpdateInfo(PACKAGE_NAME, VERSION);
|
|
135
|
-
render(_jsx(App, { initialView: initialView, initialPrompt: initialPrompt, initialSession: initialSession, updateCheck: updateCheckPromise }));
|
|
169
|
+
render(_jsx(App, { initialView: initialView, initialPrompt: initialPrompt, initialSession: initialSession, systemPromptOverride: systemPromptOverride, updateCheck: updateCheckPromise }));
|
|
136
170
|
}
|
|
137
171
|
main().catch((error) => {
|
|
138
172
|
console.error(error);
|
package/dist/ui/App.js
CHANGED
|
@@ -20,7 +20,7 @@ import { StatusBar } from "./components/StatusBar.js";
|
|
|
20
20
|
import { ModelPicker } from "./components/ModelPicker.js";
|
|
21
21
|
import { SessionPicker } from "./components/SessionPicker.js";
|
|
22
22
|
import { listSessions } from "../core/sessions.js";
|
|
23
|
-
import { RowView
|
|
23
|
+
import { RowView } from "./components/rows.js";
|
|
24
24
|
const SLASH_COMMANDS = [
|
|
25
25
|
{ name: "/help", description: "show available commands" },
|
|
26
26
|
{ name: "/model", description: "select the Axon model to use" },
|
|
@@ -161,7 +161,7 @@ let rowCounter = 0;
|
|
|
161
161
|
function rowId() {
|
|
162
162
|
return `row-${rowCounter++}`;
|
|
163
163
|
}
|
|
164
|
-
export function App({ initialView, initialPrompt, initialSession, updateCheck, }) {
|
|
164
|
+
export function App({ initialView, initialPrompt, initialSession, systemPromptOverride, updateCheck, }) {
|
|
165
165
|
const { exit } = useApp();
|
|
166
166
|
const [settings, setSettings] = useState(() => loadSettings());
|
|
167
167
|
const [view, setView] = useState(initialView ?? (getAuthToken(settings) ? "chat" : "login"));
|
|
@@ -178,7 +178,6 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
178
178
|
const [busyLabel, setBusyLabel] = useState("Thinking");
|
|
179
179
|
const [streamingReasoning, setStreamingReasoning] = useState("");
|
|
180
180
|
const [streamingText, setStreamingText] = useState("");
|
|
181
|
-
const [runningTool, setRunningTool] = useState(null);
|
|
182
181
|
const [pendingApproval, setPendingApproval] = useState(null);
|
|
183
182
|
const [pendingFollowup, setPendingFollowup] = useState(null);
|
|
184
183
|
// Set when the current project defines hooks that haven't been trusted yet;
|
|
@@ -234,6 +233,9 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
234
233
|
const deferredPromptRef = useRef(null);
|
|
235
234
|
// Guards endAndExit against double-invocation (Ctrl+D spam).
|
|
236
235
|
const exitingRef = useRef(false);
|
|
236
|
+
// Mirror of the `-s` override so a fresh agent (created mid-session via
|
|
237
|
+
// /new or /resume) keeps the override instead of falling back to default.
|
|
238
|
+
const systemPromptOverrideRef = useRef(systemPromptOverride);
|
|
237
239
|
const enqueueMessage = useCallback((text) => {
|
|
238
240
|
queueRef.current = [...queueRef.current, text];
|
|
239
241
|
setQueuedMessages(queueRef.current);
|
|
@@ -301,6 +303,7 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
301
303
|
});
|
|
302
304
|
reasoningBufferRef.current = "";
|
|
303
305
|
setStreamingReasoning("");
|
|
306
|
+
setBusyLabel("Working");
|
|
304
307
|
break;
|
|
305
308
|
case "text-delta":
|
|
306
309
|
textBufferRef.current += event.text;
|
|
@@ -311,18 +314,13 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
311
314
|
pushRow({ kind: "assistant", text: textBufferRef.current });
|
|
312
315
|
textBufferRef.current = "";
|
|
313
316
|
setStreamingText("");
|
|
317
|
+
setBusyLabel("Working");
|
|
314
318
|
break;
|
|
315
319
|
case "tool-start":
|
|
316
|
-
|
|
317
|
-
id: event.id,
|
|
318
|
-
name: event.name,
|
|
319
|
-
summary: event.summary,
|
|
320
|
-
});
|
|
321
|
-
setBusyLabel(`Running ${event.name}`);
|
|
320
|
+
setBusyLabel("Working");
|
|
322
321
|
break;
|
|
323
322
|
case "tool-end":
|
|
324
|
-
|
|
325
|
-
setBusyLabel("Thinking");
|
|
323
|
+
setBusyLabel("Working");
|
|
326
324
|
pushRow({
|
|
327
325
|
kind: "tool",
|
|
328
326
|
name: event.name,
|
|
@@ -366,7 +364,6 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
366
364
|
reasoningBufferRef.current = "";
|
|
367
365
|
setStreamingReasoning("");
|
|
368
366
|
}
|
|
369
|
-
setRunningTool(null);
|
|
370
367
|
setBusy(false);
|
|
371
368
|
maybeFetchTitle();
|
|
372
369
|
refreshUsage();
|
|
@@ -409,6 +406,9 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
409
406
|
hooks: current.hooks,
|
|
410
407
|
mcp: mcpManagerRef.current,
|
|
411
408
|
resume,
|
|
409
|
+
// The override (from `-s`) is captured in a ref so a /new or /resume
|
|
410
|
+
// mid-session still applies it to the new agent.
|
|
411
|
+
systemPromptOverride: systemPromptOverrideRef.current,
|
|
412
412
|
callbacks: {
|
|
413
413
|
onEvent: handleEvent,
|
|
414
414
|
requestApproval: (request) => new Promise((resolve) => setPendingApproval({ request, resolve })),
|
|
@@ -902,7 +902,7 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
902
902
|
!modelPickerOpen &&
|
|
903
903
|
!mcpPickerOpen &&
|
|
904
904
|
!resumableSessions;
|
|
905
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: rows, children: (row) => _jsx(RowView, { row: row }, row.id) }, staticKey), view === "login" ? (_jsx(LoginSection, { onLogin: handleLogin })) : (_jsxs(Box, { flexDirection: "column", children: [updateInfo?.updateAvailable && updateInfo.latest && (_jsxs(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, alignSelf: "flex-start", children: [_jsxs(Text, { color: COLORS.warning, bold: true, children: ["\u2191 Update available: v", updateInfo.current, " \u2192 v", updateInfo.latest] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: COLORS.accent, children: "orbcode update" }), " to install the latest version, then relaunch."] })] })), streamingReasoning && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: COLORS.thinking, italic: true, children: "\u2726 Thinking\u2026" }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { dimColor: true, italic: true, children: tail(streamingReasoning, expandReasoningRef.current ? 30 : 3) }) })] })), streamingText && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: COLORS.primary, children: "\u25CF " }), streamingText] }) })),
|
|
905
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: rows, children: (row) => _jsx(RowView, { row: row }, row.id) }, staticKey), view === "login" ? (_jsx(LoginSection, { onLogin: handleLogin })) : (_jsxs(Box, { flexDirection: "column", children: [updateInfo?.updateAvailable && updateInfo.latest && (_jsxs(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, alignSelf: "flex-start", children: [_jsxs(Text, { color: COLORS.warning, bold: true, children: ["\u2191 Update available: v", updateInfo.current, " \u2192 v", updateInfo.latest] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: COLORS.accent, children: "orbcode update" }), " to install the latest version, then relaunch."] })] })), streamingReasoning && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: COLORS.thinking, italic: true, children: "\u2726 Thinking\u2026" }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { dimColor: true, italic: true, children: tail(streamingReasoning, expandReasoningRef.current ? 30 : 3) }) })] })), streamingText && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: COLORS.primary, children: "\u25CF " }), streamingText] }) })), taskLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: "Tasks" }), taskLines.slice(0, 10).map((line, i) => (_jsx(Text, { dimColor: /^[-*]\s*\[x\]/i.test(line), children: line
|
|
906
906
|
.replace(/^[-*]\s*\[x\]/i, " ■")
|
|
907
907
|
.replace(/^[-*]\s*\[-\]/, " ◧")
|
|
908
908
|
.replace(/^[-*]\s*\[ \]/, " □") }, i))), taskLines.length > 10 && (_jsxs(Text, { dimColor: true, children: [" \u2026 ", taskLines.length - 10, " more"] }))] })), modelPickerOpen && (_jsx(Box, { marginTop: 1, children: _jsx(ModelPicker, { currentId: settings.model, onSelect: (modelId) => {
|
|
@@ -925,7 +925,8 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
925
925
|
!pendingHookTrust &&
|
|
926
926
|
!pendingMcpApproval &&
|
|
927
927
|
!mcpPickerOpen &&
|
|
928
|
-
!streamingText &&
|
|
928
|
+
!streamingText &&
|
|
929
|
+
!streamingReasoning && (_jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: busyLabel }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [queuedMessages.length > 0 && (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, children: [_jsxs(Text, { dimColor: true, bold: true, children: ["Queue (", queuedMessages.length, ")"] }), queuedMessages.slice(0, 5).map((msg, i) => (_jsxs(Text, { dimColor: true, children: [i + 1, ". ", truncateForQueue(msg).replace(/\n/g, "↵")] }, i))), queuedMessages.length > 5 && (_jsxs(Text, { dimColor: true, children: [" \u2026 ", queuedMessages.length - 5, " more"] }))] })), _jsx(InputBox, { active: inputActive, slashCommands: SLASH_COMMANDS, onSubmit: handleSubmit }), _jsx(StatusBar, { modelId: settings.model, contextTokens: contextTokens, totalCost: totalCost, state: busy ? busyLabel : "", approvalMode: approvalMode, busy: busy, title: sessionTitle, plan: usage?.plan, usagePercentage: usage?.usagePercentage, tieredUsage: usage?.tieredUsage })] })] }))] }));
|
|
929
930
|
}
|
|
930
931
|
function tail(text, lines) {
|
|
931
932
|
const all = text.split("\n").filter((l) => l.trim());
|