@ouro.bot/cli 0.1.0-alpha.247 → 0.1.0-alpha.249
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/changelog.json +14 -0
- package/dist/heart/daemon/agent-discovery.js +25 -2
- package/dist/heart/daemon/cli-exec.js +29 -1
- package/dist/heart/daemon/cli-parse.js +1 -1
- package/dist/heart/daemon/cli-render.js +8 -2
- package/dist/heart/sync.js +32 -0
- package/dist/senses/cli/ouro-tui.js +5 -2
- package/dist/senses/cli/tui-store.js +2 -1
- package/dist/senses/cli.js +2 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.249",
|
|
6
|
+
"changes": [
|
|
7
|
+
"feat(cli): `ouro chat` without agent name shows agent selection (auto-selects if 1 agent, numbered list if multiple).",
|
|
8
|
+
"fix(tui): session history user messages get `) ` prefix, separator line after history, reduced to last 2 exchanges."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.1.0-alpha.248",
|
|
13
|
+
"changes": [
|
|
14
|
+
"feat(daemon): `ouro status` Git Sync now resolves and shows the actual remote URL via `git remote get-url`. Three states: `origin → git@github.com:me/foo.git` when the remote resolves, `local only` when sync is enabled but no remote is configured, `disabled` when sync is off. Previously you had to `cd` into the bundle and run `git remote -v` to find out where it pushes.",
|
|
15
|
+
"fix(heart/sync): `preTurnPull` now skips the pull when no git remote is configured, mirroring the existing `postTurnPush` behavior. Closes the half-implemented 'no-remote sync' (local-only commit log) story \u2014 previously, enabling sync without a remote produced a `syncFailure` on every turn from the failing `git pull` call."
|
|
16
|
+
]
|
|
17
|
+
},
|
|
4
18
|
{
|
|
5
19
|
"version": "0.1.0-alpha.247",
|
|
6
20
|
"changes": [
|
|
@@ -37,6 +37,7 @@ exports.listEnabledBundleAgents = listEnabledBundleAgents;
|
|
|
37
37
|
exports.listBundleSyncRows = listBundleSyncRows;
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
40
41
|
const identity_1 = require("../identity");
|
|
41
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
42
43
|
function listEnabledBundleAgents(options = {}) {
|
|
@@ -85,14 +86,21 @@ function listEnabledBundleAgents(options = {}) {
|
|
|
85
86
|
* Used by the daemon (and stopped-state status renderer) to build per-agent
|
|
86
87
|
* sync rows without depending on argv-derived global identity. Bundles that
|
|
87
88
|
* cannot be read are skipped silently.
|
|
89
|
+
*
|
|
90
|
+
* For rows with sync enabled, also resolves the remote URL by shelling out to
|
|
91
|
+
* `git remote get-url <remote>` in the bundle directory. On any error (no
|
|
92
|
+
* remote, not a git repo, git missing) the URL is left undefined and the
|
|
93
|
+
* status renderer treats the bundle as "local only".
|
|
88
94
|
*/
|
|
89
95
|
function listBundleSyncRows(options = {}) {
|
|
90
96
|
const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
91
97
|
const readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
98
|
+
const execFileSync = options.execFileSync ?? child_process_1.execFileSync;
|
|
92
99
|
const agents = listEnabledBundleAgents(options);
|
|
93
100
|
const rows = [];
|
|
94
101
|
for (const agent of agents) {
|
|
95
|
-
const
|
|
102
|
+
const bundleRoot = path.join(bundlesRoot, `${agent}.ouro`);
|
|
103
|
+
const configPath = path.join(bundleRoot, "agent.json");
|
|
96
104
|
let enabled = false;
|
|
97
105
|
let remote = "origin";
|
|
98
106
|
try {
|
|
@@ -109,7 +117,22 @@ function listBundleSyncRows(options = {}) {
|
|
|
109
117
|
catch {
|
|
110
118
|
// Best-effort: bundle without readable config still gets a row with defaults
|
|
111
119
|
}
|
|
112
|
-
|
|
120
|
+
let remoteUrl;
|
|
121
|
+
if (enabled) {
|
|
122
|
+
try {
|
|
123
|
+
const out = execFileSync("git", ["remote", "get-url", remote], {
|
|
124
|
+
cwd: bundleRoot,
|
|
125
|
+
stdio: "pipe",
|
|
126
|
+
timeout: 5000,
|
|
127
|
+
}).toString().trim();
|
|
128
|
+
if (out.length > 0)
|
|
129
|
+
remoteUrl = out;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// No remote configured, not a git repo, or git missing — leave undefined.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
rows.push(remoteUrl !== undefined ? { agent, enabled, remote, remoteUrl } : { agent, enabled, remote });
|
|
113
136
|
}
|
|
114
137
|
return rows;
|
|
115
138
|
}
|
|
@@ -1775,8 +1775,36 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1775
1775
|
return message;
|
|
1776
1776
|
}
|
|
1777
1777
|
if (command.kind === "chat.connect" && deps.startChat) {
|
|
1778
|
+
let agent = command.agent;
|
|
1779
|
+
// No agent specified — show selection
|
|
1780
|
+
/* v8 ignore start -- interactive agent selection: requires real promptInput + discovered agents @preserve */
|
|
1781
|
+
if (!agent) {
|
|
1782
|
+
const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
|
|
1783
|
+
if (discovered.length === 0) {
|
|
1784
|
+
deps.writeStdout("no agents found — run `ouro` to hatch one");
|
|
1785
|
+
return "no agents found";
|
|
1786
|
+
}
|
|
1787
|
+
if (discovered.length === 1) {
|
|
1788
|
+
agent = discovered[0];
|
|
1789
|
+
}
|
|
1790
|
+
else if (deps.promptInput) {
|
|
1791
|
+
const prompt = `who do you want to talk to?\n${discovered.map((a, i) => `${i + 1}. ${a}`).join("\n")}\n`;
|
|
1792
|
+
const answer = await deps.promptInput(prompt);
|
|
1793
|
+
agent = discovered.includes(answer) ? answer : discovered[parseInt(answer, 10) - 1];
|
|
1794
|
+
if (!agent) {
|
|
1795
|
+
deps.writeStdout("invalid selection");
|
|
1796
|
+
return "invalid selection";
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
else {
|
|
1800
|
+
const message = `who do you want to talk to? ${discovered.join(", ")} (use: ouro chat <agent>)`;
|
|
1801
|
+
deps.writeStdout(message);
|
|
1802
|
+
return message;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
/* v8 ignore stop */
|
|
1778
1806
|
await ensureDaemonRunning(deps);
|
|
1779
|
-
await deps.startChat(
|
|
1807
|
+
await deps.startChat(agent);
|
|
1780
1808
|
return "";
|
|
1781
1809
|
}
|
|
1782
1810
|
if (command.kind === "hatch.start") {
|
|
@@ -742,7 +742,7 @@ function parseOuroCommand(args) {
|
|
|
742
742
|
}
|
|
743
743
|
if (head === "chat") {
|
|
744
744
|
if (!second)
|
|
745
|
-
|
|
745
|
+
return { kind: "chat.connect", agent: "" };
|
|
746
746
|
return { kind: "chat.connect", agent: second };
|
|
747
747
|
}
|
|
748
748
|
if (head === "msg")
|
|
@@ -147,7 +147,10 @@ function parseStatusPayload(data) {
|
|
|
147
147
|
const remote = stringField(row.remote);
|
|
148
148
|
if (!agent || enabled === null || !remote)
|
|
149
149
|
return null;
|
|
150
|
-
|
|
150
|
+
const remoteUrl = stringField(row.remoteUrl) ?? undefined;
|
|
151
|
+
return remoteUrl !== undefined
|
|
152
|
+
? { agent, enabled, remote, remoteUrl }
|
|
153
|
+
: { agent, enabled, remote };
|
|
151
154
|
});
|
|
152
155
|
if (parsedSenses.some((row) => row === null) ||
|
|
153
156
|
parsedWorkers.some((row) => row === null) ||
|
|
@@ -317,7 +320,10 @@ function formatDaemonStatusOutput(response, fallback) {
|
|
|
317
320
|
const name = row.agent.padEnd(agentNameWidth);
|
|
318
321
|
const dot = row.enabled ? green("●") : dim("○");
|
|
319
322
|
const stateText = (row.enabled ? "enabled " : "disabled").padEnd(10);
|
|
320
|
-
|
|
323
|
+
let detail = "";
|
|
324
|
+
if (row.enabled) {
|
|
325
|
+
detail = row.remoteUrl !== undefined ? `${row.remote} → ${row.remoteUrl}` : "local only";
|
|
326
|
+
}
|
|
321
327
|
lines.push(` ${name} ${dot} ${stateText} ${dim(detail)}`);
|
|
322
328
|
}
|
|
323
329
|
lines.push("");
|
package/dist/heart/sync.js
CHANGED
|
@@ -41,6 +41,11 @@ const path = __importStar(require("path"));
|
|
|
41
41
|
const runtime_1 = require("../nerves/runtime");
|
|
42
42
|
/**
|
|
43
43
|
* Pre-turn pull: sync the agent bundle from remote before assembling the start-of-turn packet.
|
|
44
|
+
*
|
|
45
|
+
* If the bundle has no git remote configured, the pull is skipped and the function
|
|
46
|
+
* returns ok — matching the no-remote behavior of postTurnPush. This supports the
|
|
47
|
+
* "local-only sync" mode where the bundle accumulates a commit log without ever
|
|
48
|
+
* pushing or pulling from a remote.
|
|
44
49
|
*/
|
|
45
50
|
function preTurnPull(agentRoot, config) {
|
|
46
51
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -49,6 +54,33 @@ function preTurnPull(agentRoot, config) {
|
|
|
49
54
|
message: "pre-turn pull starting",
|
|
50
55
|
meta: { agentRoot, remote: config.remote },
|
|
51
56
|
});
|
|
57
|
+
// Check if any remote is configured. If not, skip the pull (local-only mode).
|
|
58
|
+
try {
|
|
59
|
+
const remoteOutput = (0, child_process_1.execFileSync)("git", ["remote"], {
|
|
60
|
+
cwd: agentRoot,
|
|
61
|
+
stdio: "pipe",
|
|
62
|
+
timeout: 5000,
|
|
63
|
+
}).toString().trim();
|
|
64
|
+
if (remoteOutput.length === 0) {
|
|
65
|
+
(0, runtime_1.emitNervesEvent)({
|
|
66
|
+
component: "heart",
|
|
67
|
+
event: "heart.sync_pull_end",
|
|
68
|
+
message: "pre-turn pull skipped: no remote configured",
|
|
69
|
+
meta: { agentRoot },
|
|
70
|
+
});
|
|
71
|
+
return { ok: true };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({
|
|
77
|
+
component: "heart",
|
|
78
|
+
event: "heart.sync_pull_error",
|
|
79
|
+
message: "pre-turn pull failed: git remote check failed",
|
|
80
|
+
meta: { agentRoot, error },
|
|
81
|
+
});
|
|
82
|
+
return { ok: false, error };
|
|
83
|
+
}
|
|
52
84
|
try {
|
|
53
85
|
(0, child_process_1.execFileSync)("git", ["pull", config.remote], {
|
|
54
86
|
cwd: agentRoot,
|
|
@@ -86,10 +86,13 @@ function MessageBlock({ msg }) {
|
|
|
86
86
|
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: msg.content }) }));
|
|
87
87
|
}
|
|
88
88
|
if (msg.role === "history-user") {
|
|
89
|
-
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, bold: true, children: msg.content }) }));
|
|
89
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, bold: true, children: ") " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, bold: true, children: msg.content })] }) }));
|
|
90
90
|
}
|
|
91
91
|
if (msg.role === "history-assistant") {
|
|
92
|
-
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: msg.content ? (0, jsx_runtime_1.jsx)(
|
|
92
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: msg.content ? (0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: msg.content, maxWidth: safeWidth() }) : null }));
|
|
93
|
+
}
|
|
94
|
+
if (msg.role === "history-end") {
|
|
95
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(safeWidth() + 2) }) }));
|
|
93
96
|
}
|
|
94
97
|
if (msg.role === "user") {
|
|
95
98
|
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [msg.content ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, bold: true, children: msg.content }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) })] }));
|
|
@@ -173,7 +173,7 @@ class TuiStore {
|
|
|
173
173
|
seedHistory(texts) {
|
|
174
174
|
this._inputHistory.push(...texts);
|
|
175
175
|
}
|
|
176
|
-
/** Show session resume context: summary line + last N exchanges,
|
|
176
|
+
/** Show session resume context: summary line + last N exchanges (dimmed), then separator */
|
|
177
177
|
addSessionHistory(summary, exchanges) {
|
|
178
178
|
const msgs = [
|
|
179
179
|
{ id: "history-summary", role: "history-summary", content: summary },
|
|
@@ -182,6 +182,7 @@ class TuiStore {
|
|
|
182
182
|
role: (ex.role === "user" ? "history-user" : "history-assistant"),
|
|
183
183
|
content: ex.content,
|
|
184
184
|
})),
|
|
185
|
+
{ id: "history-end", role: "history-end", content: "" },
|
|
185
186
|
];
|
|
186
187
|
this._completed = [...msgs, ...this._completed];
|
|
187
188
|
this.notify();
|
package/dist/senses/cli.js
CHANGED
|
@@ -539,9 +539,9 @@ async function runCliSession(options) {
|
|
|
539
539
|
/* v8 ignore start -- session resume display: integration-only, tested visually @preserve */
|
|
540
540
|
if (messages.length > 1) {
|
|
541
541
|
const userAssistantMsgs = messages.filter((m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string" && m.content.trim().length > 0);
|
|
542
|
-
// Extract last
|
|
542
|
+
// Extract last 2 exchanges (up to 4 messages)
|
|
543
543
|
const lastExchanges = [];
|
|
544
|
-
for (let i = userAssistantMsgs.length - 1; i >= 0 && lastExchanges.length <
|
|
544
|
+
for (let i = userAssistantMsgs.length - 1; i >= 0 && lastExchanges.length < 4; i--) {
|
|
545
545
|
lastExchanges.unshift({ role: userAssistantMsgs[i].role, content: userAssistantMsgs[i].content });
|
|
546
546
|
}
|
|
547
547
|
const msgCount = messages.filter(m => m.role === "user" || m.role === "assistant").length;
|