symbiot 0.23.0 → 0.26.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/index.mjs +42 -9
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -638,12 +638,16 @@ function detectHandoffs() {
|
|
|
638
638
|
// Orca IDE (any OS): register the repo + open a terminal tab. Two variants —
|
|
639
639
|
// one that just opens the repo (use Orca's own agent, e.g. GPT), and one that
|
|
640
640
|
// runs Claude in the tab for Claude users. Not Claude-only.
|
|
641
|
+
// `<orca> open` first: it launches Orca AND blocks until the runtime is
|
|
642
|
+
// reachable, so this works even when Orca is closed (its CLI can't talk to a
|
|
643
|
+
// dead app). Use the full orca-ide path, never bare `orca` (that's a different
|
|
644
|
+
// tool on PATH). If Orca is already up, `open` returns fast.
|
|
641
645
|
const orca = findOrcaCli();
|
|
642
646
|
if (orca) {
|
|
643
647
|
const q = JSON.stringify(orca);
|
|
644
648
|
agents.unshift(
|
|
645
|
-
{ label: "Orca IDE — open repo (use your Orca agent)", tmpl:
|
|
646
|
-
{ label: "Orca IDE — run Claude in a tab", tmpl:
|
|
649
|
+
{ label: "Orca IDE — open repo (use your Orca agent)", tmpl: orcaHandoffCmd(q, ""), kind: "agent" },
|
|
650
|
+
{ label: "Orca IDE — run Claude in a tab", tmpl: orcaHandoffCmd(q, ` --command "claude {prompt}"`), kind: "agent" },
|
|
647
651
|
);
|
|
648
652
|
}
|
|
649
653
|
return { agents, editors };
|
|
@@ -651,7 +655,33 @@ function detectHandoffs() {
|
|
|
651
655
|
const shSingle = (s) => "'" + String(s).replace(/'/g, "'\\''") + "'";
|
|
652
656
|
const escDq = (s) => String(s).replace(/[\\"$`]/g, "\\$&");
|
|
653
657
|
const HANDOFF_PROMPT = "Read .symbiot/TASKS.md and implement the unchecked items in this repo. Confirm before anything destructive.";
|
|
654
|
-
function handoffCmd() { const cfg = loadConfig(); return cfg.agentCmd || (cfg.ide ? `${cfg.ide} {dir}` : ""); } // ide = legacy
|
|
658
|
+
function handoffCmd() { const cfg = loadConfig(); return migrateOrcaCmd(cfg.agentCmd) || (cfg.ide ? `${cfg.ide} {dir}` : ""); } // ide = legacy
|
|
659
|
+
// Build the full Orca handoff, cold-start safe. `bin` is the quoted orca-ide
|
|
660
|
+
// path; `commandPart` is e.g. ` --command "claude {prompt}"` or "" (open only).
|
|
661
|
+
// open launches Orca & waits for the runtime to be REACHABLE, but on a cold
|
|
662
|
+
// start the workspace graph isn't ready yet (runtime.state=graph_not_ready) and
|
|
663
|
+
// `terminal create` times out — so we poll `status` until state=ready, then add
|
|
664
|
+
// the repo and create the terminal (retry: the worktree can lag a beat behind).
|
|
665
|
+
function orcaHandoffCmd(bin, commandPart) {
|
|
666
|
+
const waitReady = `for i in $(seq 1 40); do ${bin} status --json 2>/dev/null | grep -q '"state": *"ready"' && break; sleep 1; done`;
|
|
667
|
+
const mkTerm = `for j in 1 2 3; do ${bin} terminal create --worktree path:{dir}${commandPart} --focus && break; sleep 2; done`;
|
|
668
|
+
return `${bin} open; ${waitReady}; ${bin} repo add --path {dir}; ${mkTerm}`;
|
|
669
|
+
}
|
|
670
|
+
// Normalise an Orca command saved by an older version (no launch / no wait-for-
|
|
671
|
+
// ready) to the current cold-start-safe form, preserving its binary path and any
|
|
672
|
+
// custom `--command`. Idempotent: already-current commands are left untouched.
|
|
673
|
+
function migrateOrcaCmd(cmd) {
|
|
674
|
+
if (!cmd || typeof cmd !== "string") return cmd;
|
|
675
|
+
if (!/orca-ide/.test(cmd) || !/\brepo add\b/.test(cmd)) return cmd;
|
|
676
|
+
if (/status --json/.test(cmd) && /grep -q/.test(cmd)) return cmd; // already the wait-for-ready form
|
|
677
|
+
const bm = cmd.match(/^\s*("[^"]*"|'[^']*'|\S+)/); // leading orca-ide binary token
|
|
678
|
+
const bin = bm ? bm[1] : "";
|
|
679
|
+
const cm = cmd.match(/--command\s+"([^"]*)"/); // preserve a custom agent command
|
|
680
|
+
const commandPart = cm ? ` --command "${cm[1]}"` : "";
|
|
681
|
+
const rebuilt = bin ? orcaHandoffCmd(bin, commandPart) : cmd;
|
|
682
|
+
if (rebuilt !== cmd) { try { const cfg = loadConfig(); if (cfg.agentCmd === cmd) { cfg.agentCmd = rebuilt; saveConfig(cfg); } } catch {} }
|
|
683
|
+
return rebuilt;
|
|
684
|
+
}
|
|
655
685
|
// Run a shell command as a tracked, logged background job that shows up live in
|
|
656
686
|
// the Agents tab. Shared by the agent handoff and the local-model setup.
|
|
657
687
|
function track(name, cmd, cwd, onExit) {
|
|
@@ -1274,6 +1304,7 @@ a{color:var(--green);cursor:pointer}.hidden{display:none}
|
|
|
1274
1304
|
.updatebar.show{display:flex}
|
|
1275
1305
|
.updatebar.reconnect{background:var(--amber)}
|
|
1276
1306
|
.updatebar button{font:inherit;font-weight:700;border:0;border-radius:8px;padding:5px 12px;background:var(--ink);color:var(--bone);cursor:pointer}
|
|
1307
|
+
.ver{font-size:11px;color:var(--faint);background:var(--ink3);border:1px solid var(--line);border-radius:999px;padding:2px 8px;margin-left:2px;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
1277
1308
|
footer{padding:10px 18px;border-top:1px solid var(--line);display:flex}
|
|
1278
1309
|
.profile{font-size:13px;margin-bottom:8px;line-height:1.5}
|
|
1279
1310
|
.profile b{color:var(--bone)}
|
|
@@ -1339,7 +1370,7 @@ footer{padding:10px 18px;border-top:1px solid var(--line);display:flex}
|
|
|
1339
1370
|
@media(max-width:760px){.maprow{flex-direction:column}.detail{width:auto;max-height:none}}
|
|
1340
1371
|
</style></head><body>
|
|
1341
1372
|
<div id="updatebar" class="updatebar"></div>
|
|
1342
|
-
<header><span class="dot"></span><span class="brand">Symbiot</span><span class="status" id="status">...</span></header>
|
|
1373
|
+
<header><span class="dot"></span><span class="brand">Symbiot</span><span class="ver" id="ver"></span><span class="status" id="status">...</span></header>
|
|
1343
1374
|
<div class="tabs">
|
|
1344
1375
|
<button class="tab active" data-tab="map">Map</button>
|
|
1345
1376
|
<button class="tab" data-tab="drift">Drift</button>
|
|
@@ -1640,9 +1671,10 @@ document.getElementById('agentcmd').addEventListener('change',saveAgent);
|
|
|
1640
1671
|
document.getElementById('newtask').addEventListener('keydown',function(e){if(e.key==='Enter')addTaskUI();});
|
|
1641
1672
|
var SRV_STARTED=null,srvDown=false,updBusy=false;
|
|
1642
1673
|
function ubar(){return document.getElementById('updatebar');}
|
|
1643
|
-
function heartbeat(){
|
|
1644
|
-
api('/api/ping').then(function(p){
|
|
1674
|
+
function heartbeat(fresh){
|
|
1675
|
+
api('/api/ping'+(fresh?'?fresh=1':'')).then(function(p){
|
|
1645
1676
|
var b=ubar();
|
|
1677
|
+
var ve=document.getElementById('ver');if(ve&&p.version)ve.textContent='v'+p.version;
|
|
1646
1678
|
if(SRV_STARTED===null){SRV_STARTED=p.started;}
|
|
1647
1679
|
else if(p.started!==SRV_STARTED){location.reload();return;}
|
|
1648
1680
|
if(srvDown){location.reload();return;}
|
|
@@ -1655,7 +1687,8 @@ function heartbeat(){
|
|
|
1655
1687
|
}).catch(function(){srvDown=true;var b=ubar();b.className='updatebar reconnect show';b.textContent='Reconnecting to Symbiot…';});
|
|
1656
1688
|
}
|
|
1657
1689
|
function doUpdate(){updBusy=true;var b=ubar();b.className='updatebar show';b.textContent='Updating & restarting… this page will reload itself when it is back.';api('/api/update',{});}
|
|
1658
|
-
setInterval(heartbeat,4000);heartbeat();
|
|
1690
|
+
setInterval(heartbeat,4000);heartbeat(true);
|
|
1691
|
+
window.addEventListener('focus',function(){heartbeat(true);}); // re-check for updates when you come back to the window
|
|
1659
1692
|
initGraphEvents();syncP();refresh();loadMap();loadAgentCfg();loadScanRoots();
|
|
1660
1693
|
</script></body></html>`;
|
|
1661
1694
|
|
|
@@ -1745,7 +1778,7 @@ async function cmdApp() {
|
|
|
1745
1778
|
if (u.pathname === "/api/agents") return json(res, HANDOFFS.map((e) => { let tail = ""; try { tail = readFileSync(e.log, "utf8").slice(-1200); } catch {} return { id: e.id, name: e.name, path: e.path, status: e.status, elapsed: (e.endedAt || Date.now()) - e.startedAt, exitCode: e.exitCode, tail }; }));
|
|
1746
1779
|
if (u.pathname === "/api/run" && req.method === "POST") { const b = await readBody(req); const cmd = ["week", "standup", "todo"].includes(b.cmd) ? b.cmd : "week"; return json(res, await produce(cmd)); }
|
|
1747
1780
|
if (u.pathname === "/api/connect" && req.method === "POST") { return json(res, await connectProvider(await readBody(req))); }
|
|
1748
|
-
if (u.pathname === "/api/ping") return json(res, { version: VERSION, started: SERVER_STARTED, latest: LATEST_VERSION });
|
|
1781
|
+
if (u.pathname === "/api/ping") { if (u.searchParams.get("fresh") === "1") await checkLatest(); return json(res, { version: VERSION, started: SERVER_STARTED, latest: LATEST_VERSION }); }
|
|
1749
1782
|
if (u.pathname === "/api/update" && req.method === "POST") {
|
|
1750
1783
|
// Install the newest symbiot, then relaunch this same app (same port +
|
|
1751
1784
|
// token => same URL) and exit. The open page's heartbeat reconnects and
|
|
@@ -1778,7 +1811,7 @@ async function cmdApp() {
|
|
|
1778
1811
|
else { try { server.listen(0, "127.0.0.1"); } catch {} }
|
|
1779
1812
|
});
|
|
1780
1813
|
server.listen(PORT, "127.0.0.1");
|
|
1781
|
-
checkLatest(); setInterval(checkLatest,
|
|
1814
|
+
checkLatest(); setInterval(checkLatest, 2 * 60 * 1000).unref(); // background update check (every 2 min)
|
|
1782
1815
|
}
|
|
1783
1816
|
|
|
1784
1817
|
const HELP = `${c.b("symbiot")} — your week, written from your real work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Your week, written from your real work. Reads your local git and writes it up with the AI of your choice — Claude, OpenAI, Gemini, or a local model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|