symbiot 0.20.0 → 0.22.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 +56 -12
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -593,21 +593,57 @@ function recommendModels(hw) {
|
|
|
593
593
|
// claude -p "{prompt}" · aider --message "{prompt}" · code {dir}
|
|
594
594
|
// gnome-terminal --working-directory={dir} -- claude "{prompt}"
|
|
595
595
|
// Not tied to any one tool — you decide what runs.
|
|
596
|
-
|
|
596
|
+
// [cmd, label, macAppName] — macApp used to launch GUI editors on macOS where
|
|
597
|
+
// the CLI isn't on PATH (they're .app bundles).
|
|
598
|
+
const IDE_LIST = [["code", "VS Code", "Visual Studio Code"], ["cursor", "Cursor", "Cursor"], ["windsurf", "Windsurf", "Windsurf"], ["zed", "Zed", "Zed"], ["subl", "Sublime Text", "Sublime Text"], ["idea", "IntelliJ IDEA", "IntelliJ IDEA"], ["nvim", "Neovim", ""]];
|
|
597
599
|
const AGENT_LIST = [
|
|
598
600
|
["claude", "Claude Code — make changes", 'claude -p "{prompt}" --permission-mode acceptEdits'],
|
|
599
601
|
["claude", "Claude Code — plan only (asks first)", 'claude -p "{prompt}"'],
|
|
602
|
+
["codex", "Codex (OpenAI) — make changes", 'codex exec --full-auto "{prompt}"'],
|
|
600
603
|
["aider", "Aider — make changes", 'aider --message "{prompt}" --yes'],
|
|
601
604
|
["cursor-agent", "Cursor agent", 'cursor-agent -p "{prompt}"'],
|
|
605
|
+
["gemini", "Gemini CLI", 'gemini -p "{prompt}"'],
|
|
602
606
|
];
|
|
607
|
+
// Cross-platform "is this command available?" (command -v on posix, where on win).
|
|
608
|
+
function hasCmd(cmd) { try { return !!sh(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`).trim(); } catch { return false; } }
|
|
609
|
+
function macApp(name) { if (process.platform !== "darwin" || !name) return ""; for (const base of ["/Applications", join(homedir(), "Applications")]) { try { if (existsSync(join(base, name + ".app"))) return name; } catch {} } return ""; }
|
|
610
|
+
// Find the Orca IDE CLI across OSes (known locations, then a bounded search).
|
|
611
|
+
let ORCA_CLI; // cached per process: undefined=unchecked, ""=none, string=path
|
|
612
|
+
function findOrcaCli() {
|
|
613
|
+
if (ORCA_CLI !== undefined) return ORCA_CLI;
|
|
614
|
+
ORCA_CLI = _findOrcaCli();
|
|
615
|
+
return ORCA_CLI;
|
|
616
|
+
}
|
|
617
|
+
function _findOrcaCli() {
|
|
618
|
+
const home = homedir(); const cands = [];
|
|
619
|
+
if (process.platform === "linux") cands.push(join(home, ".local/share/orca-ide/app/resources/bin/orca-ide"));
|
|
620
|
+
if (process.platform === "darwin") { cands.push(join(home, "Library/Application Support/orca-ide/app/resources/bin/orca-ide"), "/Applications/Orca.app/Contents/Resources/app/resources/bin/orca-ide", join(home, "Applications/Orca.app/Contents/Resources/app/resources/bin/orca-ide")); }
|
|
621
|
+
if (process.platform === "win32") { const la = process.env.LOCALAPPDATA || ""; cands.push(join(la, "orca-ide", "app", "resources", "bin", "orca-ide"), join(la, "Programs", "orca-ide", "resources", "app", "resources", "bin", "orca-ide")); }
|
|
622
|
+
for (const c of cands) { try { if (existsSync(c)) return c; } catch {} }
|
|
623
|
+
const roots = process.platform === "darwin" ? [join(home, "Library/Application Support"), "/Applications", join(home, "Applications")]
|
|
624
|
+
: process.platform === "win32" ? [process.env.LOCALAPPDATA || "", process.env.PROGRAMFILES || ""]
|
|
625
|
+
: [join(home, ".local/share"), "/opt", join(home, ".config")];
|
|
626
|
+
for (const r of roots) { if (!r) continue; const hit = sh(`find ${JSON.stringify(r)} -maxdepth 6 -name orca-ide -type f 2>/dev/null | head -1`).trim(); if (hit) return hit; }
|
|
627
|
+
return "";
|
|
628
|
+
}
|
|
603
629
|
function detectHandoffs() {
|
|
604
|
-
const editors =
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const
|
|
610
|
-
|
|
630
|
+
const editors = [];
|
|
631
|
+
for (const [cmd, label, app] of IDE_LIST) {
|
|
632
|
+
if (hasCmd(cmd)) editors.push({ label, tmpl: `${cmd} {dir}`, kind: "editor" });
|
|
633
|
+
else { const a = macApp(app); if (a) editors.push({ label, tmpl: `open -a ${JSON.stringify(a)} {dir}`, kind: "editor" }); }
|
|
634
|
+
}
|
|
635
|
+
const agents = AGENT_LIST.filter(([cmd]) => hasCmd(cmd)).map(([cmd, label, tmpl]) => ({ label, tmpl, kind: "agent" }));
|
|
636
|
+
// Orca IDE (any OS): register the repo + open a terminal tab. Two variants —
|
|
637
|
+
// one that just opens the repo (use Orca's own agent, e.g. GPT), and one that
|
|
638
|
+
// runs Claude in the tab for Claude users. Not Claude-only.
|
|
639
|
+
const orca = findOrcaCli();
|
|
640
|
+
if (orca) {
|
|
641
|
+
const q = JSON.stringify(orca);
|
|
642
|
+
agents.unshift(
|
|
643
|
+
{ label: "Orca IDE — open repo (use your Orca agent)", tmpl: `${q} repo add --path {dir}; ${q} terminal create --worktree path:{dir} --focus`, kind: "agent" },
|
|
644
|
+
{ label: "Orca IDE — run Claude in a tab", tmpl: `${q} repo add --path {dir}; ${q} terminal create --worktree path:{dir} --command "claude {prompt}" --focus`, kind: "agent" },
|
|
645
|
+
);
|
|
646
|
+
}
|
|
611
647
|
return { agents, editors };
|
|
612
648
|
}
|
|
613
649
|
const shSingle = (s) => "'" + String(s).replace(/'/g, "'\\''") + "'";
|
|
@@ -1049,10 +1085,16 @@ async function repoSuggest(path) {
|
|
|
1049
1085
|
|
|
1050
1086
|
// Build a write-up for a command; returns { text, sub, error? } without printing.
|
|
1051
1087
|
// Shared by the CLI (cmdRun) and the web UI (symbiot app).
|
|
1088
|
+
// The SAME repo set the Map uses — reuse its cached scan when present, else do
|
|
1089
|
+
// the identical discovery. So week/standup/todo/drift all agree with the Map.
|
|
1090
|
+
function discoveredRepos() {
|
|
1091
|
+
if (LAST_MAP && LAST_MAP.nodes) return LAST_MAP.nodes.filter((n) => n.type === "repo" && n.meta && n.meta.path).map((n) => ({ path: n.meta.path, name: n.label, recency: 0 }));
|
|
1092
|
+
return findAllRepos();
|
|
1093
|
+
}
|
|
1052
1094
|
async function produce(cmd) {
|
|
1053
1095
|
if (!resolveProvider()) return { error: "not-connected" };
|
|
1054
1096
|
if (cmd === "todo") {
|
|
1055
|
-
const repos =
|
|
1097
|
+
const repos = discoveredRepos();
|
|
1056
1098
|
const open = openWork(repos);
|
|
1057
1099
|
if (!open.length) return { text: "Nothing outstanding found (no TODOs or uncommitted work).", sub: "0 open items · todo" };
|
|
1058
1100
|
const system =
|
|
@@ -1065,8 +1107,10 @@ async function produce(cmd) {
|
|
|
1065
1107
|
const label = cmd === "standup" ? "standup" : "week";
|
|
1066
1108
|
const days = cmd === "standup" ? 2 : SINCE_WEEK;
|
|
1067
1109
|
const who = me();
|
|
1068
|
-
|
|
1069
|
-
|
|
1110
|
+
// Discover the Map's repo set, then keep only those with commits in the window.
|
|
1111
|
+
const all = discoveredRepos();
|
|
1112
|
+
const repos = all.filter((r) => sh(`git -C ${JSON.stringify(r.path)} log --since="${days} days ago" --oneline -1 2>/dev/null`).trim());
|
|
1113
|
+
if (!repos.length) return { text: `No commits in the last ${days} days across your ${all.length} repos.\nAdd folders to scan in Settings, or check your git identity.`, sub: "no activity" };
|
|
1070
1114
|
let cs = commits(repos, `${days} days ago`, !has("all"));
|
|
1071
1115
|
if (!cs.length) cs = commits(repos, `${days} days ago`, false); // fall back to all if none matched you
|
|
1072
1116
|
const open = label === "week" ? openWork(repos) : [];
|
|
@@ -1085,7 +1129,7 @@ async function produce(cmd) {
|
|
|
1085
1129
|
`Write the ${label === "standup" ? "standup" : "update"}.`;
|
|
1086
1130
|
|
|
1087
1131
|
const text = await write(system, prompt);
|
|
1088
|
-
return { text: text || "(couldn't reach the model)", sub: `${cs.length} commits across ${new Set(cs.map((x) => x.repo)).size} repos · ${label}`, footer: `symbiot ${VERSION} · ${
|
|
1132
|
+
return { text: text || "(couldn't reach the model)", sub: `${cs.length} commits across ${new Set(cs.map((x) => x.repo)).size} repos · ${label}`, footer: `symbiot ${VERSION} · ${all.length} repos (same as the Map) · ${repos.length} active · ${cs.length} commits in last ${days}d` };
|
|
1089
1133
|
}
|
|
1090
1134
|
|
|
1091
1135
|
async function cmdRun(cmd) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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": {
|