symbiot 0.57.0 → 0.57.1
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.md +6 -0
- package/ai.mjs +31 -7
- package/index.mjs +4 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
What each Symbiot release brought, newest first. When Approve bumps the version, it writes that release here from the tasks it approved, worded as release notes by your connected AI (or in the tasks' own words without one). The app shows the releases newer than yours under "What's new" when it offers an update, and once more after you update.
|
|
4
4
|
|
|
5
|
+
## 0.57.1
|
|
6
|
+
|
|
7
|
+
- **Windows is tested on every change.** A Windows job runs on GitHub with each pull request: Symbiot has to install, start, open into setup, find your Claude subscription and uninstall cleanly there, or the change doesn't go in. The whole suite runs there too and reports what isn't Windows-ready yet.
|
|
8
|
+
- **Your Claude subscription on Windows.** Claude Code installs as `claude.cmd` on Windows, which Symbiot didn't find, so it fell back to asking for a key. And Symbiot's instructions now reach Claude Code in a file instead of on the command line, which Windows caps at about 8,000 characters.
|
|
9
|
+
- **`symbiot --version` and `symbiot --help` work.** Both ran the weekly write-up instead.
|
|
10
|
+
|
|
5
11
|
## 0.57.0
|
|
6
12
|
|
|
7
13
|
- **Setup, before anything else.** A new Symbiot opens straight into setup: what it is (it watches, it works, it asks), your AI (your Claude subscription, found by itself), your work (your projects, found while you read), your coding agent (one click), every app you use (each connected, or marked "I don't use it"; you can't go on until they're all decided), and an optional folder of your documents. Then a short "finding your way" and Home. Where you are is kept, so closing it resumes there; Settings → Setup → Run setup again walks it again.
|
package/ai.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
4
4
|
import { spawn, spawnSync } from "node:child_process";
|
|
5
5
|
import { homedir, totalmem, cpus as oscpus } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
8
8
|
import { loadConfig, saveConfig, sh, hasCmd, CONFIG_DIR } from "./core.mjs";
|
|
9
9
|
|
|
10
10
|
const MAX_TOKENS = 1600;
|
|
@@ -26,13 +26,33 @@ const PROVIDERS = {
|
|
|
26
26
|
// agents. Whether it's there and signed in: `claude auth status` (a quarter of a
|
|
27
27
|
// second), kept 5 minutes. SYMBIOT_CLAUDE_CMD replaces the command (the tests' stand-in).
|
|
28
28
|
const CLAUDE_BIN = () => process.env.SYMBIOT_CLAUDE_CMD || "claude";
|
|
29
|
+
// How to run it. Windows: npm installs `claude` as claude.cmd, which Node can only
|
|
30
|
+
// start through cmd.exe (a .exe, from Claude's own installer, it starts directly), so
|
|
31
|
+
// it's found with `where` and its arguments are quoted for cmd.exe.
|
|
32
|
+
const winQuote = (a) => '"' + String(a).replace(/"/g, '""') + '"';
|
|
33
|
+
function claudeCommand(platform = process.platform) {
|
|
34
|
+
const bin = CLAUDE_BIN();
|
|
35
|
+
if (platform !== "win32") return { file: bin, shell: false };
|
|
36
|
+
if (/\.exe$/i.test(bin)) return { file: bin, shell: false };
|
|
37
|
+
if (/\.(cmd|bat)$/i.test(bin)) return { file: bin, shell: true };
|
|
38
|
+
try {
|
|
39
|
+
const hits = String(spawnSync("where", [bin], { encoding: "utf8", timeout: 5000 }).stdout || "").split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
|
|
40
|
+
const exe = hits.find((h) => /\.exe$/i.test(h)); if (exe) return { file: exe, shell: false };
|
|
41
|
+
const cmd = hits.find((h) => /\.(cmd|bat)$/i.test(h)); if (cmd) return { file: cmd, shell: true };
|
|
42
|
+
} catch {}
|
|
43
|
+
return { file: bin, shell: true };
|
|
44
|
+
}
|
|
45
|
+
function claudeSpawn(args, opts = {}, sync = false) {
|
|
46
|
+
const c = claudeCommand(), run = sync ? spawnSync : spawn;
|
|
47
|
+
return c.shell ? run([c.file, ...args].map(winQuote).join(" "), [], { ...opts, shell: true, windowsHide: true }) : run(c.file, args, { ...opts, windowsHide: true });
|
|
48
|
+
}
|
|
29
49
|
let CLAUDE_SEEN = null;
|
|
30
50
|
function claudeState(fresh = false) {
|
|
31
51
|
if (!fresh && CLAUDE_SEEN && Date.now() - CLAUDE_SEEN.at < 300000) return CLAUDE_SEEN;
|
|
32
52
|
let installed = !!process.env.SYMBIOT_CLAUDE_CMD || hasCmd("claude"), signedIn = false;
|
|
33
53
|
if (installed) {
|
|
34
54
|
try {
|
|
35
|
-
const r =
|
|
55
|
+
const r = claudeSpawn(["auth", "status"], { encoding: "utf8", timeout: 8000, env: process.env }, true);
|
|
36
56
|
if (r.error && r.error.code === "ENOENT") installed = false;
|
|
37
57
|
else { const j = JSON.parse(String(r.stdout || "").trim() || "{}"); signedIn = !!j.loggedIn && j.authMethod !== "apiKey"; }
|
|
38
58
|
} catch {}
|
|
@@ -83,7 +103,8 @@ function resolveProvider() {
|
|
|
83
103
|
|
|
84
104
|
// ---- model calls (one per provider, same in/out) --------------------------
|
|
85
105
|
// Claude Code, headless, on your subscription: Symbiot's own instructions as the
|
|
86
|
-
// system prompt
|
|
106
|
+
// system prompt (from a file: Windows' command line holds only ~8,000 characters,
|
|
107
|
+
// and they run longer), the prompt on stdin, no tools (Read only, for screenshots you
|
|
87
108
|
// attached), none of your settings, plugins or servers, no session kept. Not --bare:
|
|
88
109
|
// that ignores the subscription sign-in.
|
|
89
110
|
function callClaude(r, system, prompt, images = []) {
|
|
@@ -93,15 +114,18 @@ function callClaude(r, system, prompt, images = []) {
|
|
|
93
114
|
try { const dir = join(CONFIG_DIR, "uploads"); mkdirSync(dir, { recursive: true, mode: 0o700 }); const f = join(dir, `${Date.now()}-${shots.length}.${/png/.test(i.mime) ? "png" : /webp/.test(i.mime) ? "webp" : /gif/.test(i.mime) ? "gif" : "jpg"}`); writeFileSync(f, Buffer.from(i.data, "base64"), { mode: 0o600 }); shots.push(f); } catch {}
|
|
94
115
|
}
|
|
95
116
|
const input = shots.length ? `${prompt}\n\nScreenshots they attached (look at them with your Read tool): ${shots.join(", ")}` : prompt;
|
|
96
|
-
|
|
117
|
+
let sysFile = ""; try { mkdirSync(CONFIG_DIR, { recursive: true }); sysFile = join(CONFIG_DIR, `.system-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`); writeFileSync(sysFile, system, { mode: 0o600 }); } catch { sysFile = ""; }
|
|
118
|
+
const drop = () => { if (sysFile) { try { unlinkSync(sysFile); } catch {} } };
|
|
119
|
+
const args = ["-p", "--output-format", "json", ...(sysFile ? ["--system-prompt-file", sysFile] : ["--system-prompt", system]), "--tools", shots.length ? "Read" : "", "--setting-sources", "",
|
|
97
120
|
"--strict-mcp-config", "--no-session-persistence", "--disable-slash-commands", ...(r.model ? ["--model", r.model] : [])];
|
|
98
121
|
return new Promise((resolve, reject) => {
|
|
99
122
|
let out = "", err = "", done = false;
|
|
100
|
-
let ch; try { ch =
|
|
123
|
+
let ch; try { ch = claudeSpawn(args, { cwd: CONFIG_DIR, stdio: ["pipe", "pipe", "pipe"], env: process.env }); } catch (e) { drop(); reject(e); return; }
|
|
101
124
|
const t = setTimeout(() => { if (!done) { done = true; try { ch.kill("SIGTERM"); } catch {} reject(new Error("Claude Code didn't answer within 3 minutes")); } }, 180000);
|
|
102
125
|
ch.stdout.on("data", (d) => { out += d; }); ch.stderr.on("data", (d) => { err += d; });
|
|
103
|
-
ch.on("error", (e) => { if (!done) { done = true; clearTimeout(t); reject(e); } });
|
|
126
|
+
ch.on("error", (e) => { drop(); if (!done) { done = true; clearTimeout(t); reject(e); } });
|
|
104
127
|
ch.on("close", () => {
|
|
128
|
+
drop();
|
|
105
129
|
if (done) return; done = true; clearTimeout(t);
|
|
106
130
|
let j = null; try { j = JSON.parse(out.trim().split("\n").filter(Boolean).pop() || "{}"); } catch {}
|
|
107
131
|
if (j && j.is_error === false && typeof j.result === "string") { resolve(j.result.trim()); return; }
|
|
@@ -284,4 +308,4 @@ async function connectProvider(b) {
|
|
|
284
308
|
return saveConfig(cfg) ? { ok: true, message: `Connected: ${PROVIDERS[provider].label} · ${model}` } : { ok: false, message: "Couldn't write the config file." };
|
|
285
309
|
}
|
|
286
310
|
|
|
287
|
-
export { PROVIDERS, AI_UI, resolveProvider, write, validate, connectProvider, detectHardware, recommendModels, hasOllama, ollamaInstall, ensureOllama, useOllamaModel , claudeState, claudeReady, claudeHelp };
|
|
311
|
+
export { PROVIDERS, AI_UI, resolveProvider, write, validate, connectProvider, detectHardware, recommendModels, hasOllama, ollamaInstall, ensureOllama, useOllamaModel , claudeState, claudeReady, claudeHelp , claudeCommand };
|
package/index.mjs
CHANGED
|
@@ -49,7 +49,8 @@ import { knowledgeState, addKnowledgeFolder, removeKnowledgeFolder, indexKnowled
|
|
|
49
49
|
|
|
50
50
|
// ---- tiny arg parse --------------------------------------------------------
|
|
51
51
|
const argv = process.argv.slice(2);
|
|
52
|
-
|
|
52
|
+
// a first word that's a flag means the default (week), except asking for help or the version
|
|
53
|
+
const cmd = (argv[0] && (!argv[0].startsWith("-") || ["--help", "-h", "--version", "-v"].includes(argv[0])) ? argv[0] : "week").toLowerCase();
|
|
53
54
|
const flag = (name, def) => {
|
|
54
55
|
const i = argv.indexOf(`--${name}`);
|
|
55
56
|
return i >= 0 && argv[i + 1] ? argv[i + 1] : def;
|
|
@@ -812,6 +813,7 @@ ${c.b("Usage")}
|
|
|
812
813
|
symbiot standup yesterday + today, for standup
|
|
813
814
|
symbiot todo what's still on your plate
|
|
814
815
|
symbiot app open the visual app in your browser
|
|
816
|
+
symbiot --version which Symbiot this is
|
|
815
817
|
symbiot open start Symbiot in the background and open its window
|
|
816
818
|
(what its app-menu icon does)
|
|
817
819
|
symbiot uninstall [--keep-data] remove Symbiot and everything it added
|
|
@@ -868,6 +870,7 @@ ${c.d("local Ollama model).")}`;
|
|
|
868
870
|
// ---- main -----------------------------------------------------------------
|
|
869
871
|
async function main() {
|
|
870
872
|
if (cmd === "help" || cmd === "--help" || cmd === "-h") { console.log(HELP); return; }
|
|
873
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") { console.log(VERSION); return; }
|
|
871
874
|
if (cmd === "login" || cmd === "auth") return cmdLogin();
|
|
872
875
|
if (cmd === "logout") return cmdLogout();
|
|
873
876
|
if (cmd === "whoami" || cmd === "status") return cmdWhoami();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.1",
|
|
4
4
|
"description": "Your week, written from your real work. Reads your local git and writes it up with the AI of your choice \u2014 Claude, OpenAI, Gemini, or a local model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@anthropic-ai/sdk": "^0.128.0"
|
|
81
81
|
},
|
|
82
82
|
"scripts": {
|
|
83
|
-
"test": "node test/load.mjs && node test/fixtures.mjs && node test/links.mjs && node test/post.mjs && node test/peek.mjs && node test/mind.mjs && node test/knowledge.mjs && node test/checks.mjs &&node test/lanes.mjs && node test/handback.mjs && node test/autoallow.mjs && node test/guard.mjs && node test/reports.mjs && node test/sandbox.mjs && node test/adapt.mjs && node test/work.mjs && node test/mapknn.mjs && node test/away.mjs && node test/subscription.mjs && node test/launcher.mjs && node test/onboarding.mjs && node test/marketing.mjs && node test/paths.mjs && node test/smoke.mjs && node test/app.mjs && node test/install.mjs",
|
|
83
|
+
"test": "node test/load.mjs && node test/fixtures.mjs && node test/links.mjs && node test/post.mjs && node test/peek.mjs && node test/mind.mjs && node test/knowledge.mjs && node test/checks.mjs &&node test/lanes.mjs && node test/handback.mjs && node test/autoallow.mjs && node test/guard.mjs && node test/reports.mjs && node test/sandbox.mjs && node test/adapt.mjs && node test/work.mjs && node test/mapknn.mjs && node test/away.mjs && node test/subscription.mjs && node test/launcher.mjs && node test/onboarding.mjs && node test/windows.mjs && node test/marketing.mjs && node test/paths.mjs && node test/smoke.mjs && node test/app.mjs && node test/install.mjs",
|
|
84
84
|
"lint": "eslint .",
|
|
85
85
|
"postinstall": "node postinstall.mjs"
|
|
86
86
|
},
|