isorite 0.8.0 → 0.8.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.
Files changed (33) hide show
  1. package/dist/main.js +86 -31
  2. package/docs/setup-for-agents.md +1 -0
  3. package/package.json +1 -1
  4. package/web/_app/immutable/chunks/{B-65IYX-.js → B2TCcw-P.js} +1 -1
  5. package/web/_app/immutable/chunks/{dvrIP-Hm.js → B4sg4e8i.js} +1 -1
  6. package/web/_app/immutable/chunks/{hOAYc4Tf.js → BAkGB2fM.js} +1 -1
  7. package/web/_app/immutable/chunks/{BjPiibIz.js → C1hqerMA.js} +1 -1
  8. package/web/_app/immutable/chunks/{Di_ac7WN.js → CMTjeXEe.js} +1 -1
  9. package/web/_app/immutable/chunks/{DgZW3JtG.js → C_ER1Any.js} +1 -1
  10. package/web/_app/immutable/chunks/{BtUtjfIh.js → Cgml88Pl.js} +1 -1
  11. package/web/_app/immutable/chunks/{CnP_Si9g.js → CurmVJyp.js} +1 -1
  12. package/web/_app/immutable/chunks/{CGzfi9Nl.js → D29fKEzc.js} +1 -1
  13. package/web/_app/immutable/chunks/{B91efLFT.js → D3BEa5-_.js} +3 -3
  14. package/web/_app/immutable/chunks/{CCiHXe5d.js → DPnfDiTn.js} +1 -1
  15. package/web/_app/immutable/chunks/{NmqB5ax3.js → DsIcKd8x.js} +1 -1
  16. package/web/_app/immutable/chunks/DtPULAe0.js +1 -0
  17. package/web/_app/immutable/chunks/{D_JZ0swv.js → yXUzBjUq.js} +1 -1
  18. package/web/_app/immutable/entry/{app.DO9lj7h3.js → app.idRgnoYp.js} +2 -2
  19. package/web/_app/immutable/entry/start.CEx_UQO6.js +1 -0
  20. package/web/_app/immutable/nodes/{0.N1e1uAFq.js → 0.BUd_gY_8.js} +1 -1
  21. package/web/_app/immutable/nodes/{1.BRTPLKyU.js → 1.DHK7VmG0.js} +1 -1
  22. package/web/_app/immutable/nodes/2.CDAt-6SJ.js +1 -0
  23. package/web/_app/immutable/nodes/3.4Wv5nmaw.js +1 -0
  24. package/web/_app/immutable/nodes/{4.DYW7Fxdy.js → 4.Ceb5g4Ps.js} +1 -1
  25. package/web/_app/immutable/nodes/{5.Dkvkif0E.js → 5.DioYOANs.js} +1 -1
  26. package/web/_app/immutable/nodes/6.CzYfNb6g.js +1 -0
  27. package/web/_app/version.json +1 -1
  28. package/web/index.html +14 -14
  29. package/web/_app/immutable/chunks/BrXPCJaM.js +0 -1
  30. package/web/_app/immutable/entry/start.Bg5TZs6n.js +0 -1
  31. package/web/_app/immutable/nodes/2.BWh1AYEd.js +0 -1
  32. package/web/_app/immutable/nodes/3.Cgqgn7Zv.js +0 -1
  33. package/web/_app/immutable/nodes/6.BinB0fuq.js +0 -1
package/dist/main.js CHANGED
@@ -8713,28 +8713,47 @@ var Setup = class {
8713
8713
  };
8714
8714
  //#endregion
8715
8715
  //#region packages/server/src/version.ts
8716
- const VERSION = "0.8.0";
8716
+ const VERSION = "0.8.1";
8717
8717
  //#endregion
8718
- //#region packages/server/src/studybase.ts
8719
- init_src$1();
8720
- function git(cwd, ...args) {
8718
+ //#region packages/server/src/git.ts
8719
+ /** The top of the repository `folder` is in, or null: the nearest folder holding `.git`, a directory or, in a
8720
+ * worktree or submodule, a file. No `git` is run. */
8721
+ function repoOf(folder) {
8722
+ for (let at = resolve(folder);; at = dirname(at)) {
8723
+ if (existsSync(join(at, ".git"))) return at;
8724
+ if (dirname(at) === at) return null;
8725
+ }
8726
+ }
8727
+ /** The environment for a `git` isorite runs: none of the shell's GIT_* (a GIT_DIR would point it elsewhere), no
8728
+ * system configuration (where a company's hooks and helpers are usually set), and no prompt for a password. */
8729
+ function plainGitEnv(env = process.env) {
8730
+ return {
8731
+ ...Object.fromEntries(Object.entries(env).filter(([k]) => !k.startsWith("GIT_"))),
8732
+ GIT_CONFIG_NOSYSTEM: "1",
8733
+ GIT_TERMINAL_PROMPT: "0"
8734
+ };
8735
+ }
8736
+ /** `git init` in a folder, given `timeout` ms; whether it was made a repository. */
8737
+ function gitInit(folder, { env = process.env, timeout = 1e4 } = {}) {
8721
8738
  try {
8722
- return execFileSync("git", args, {
8723
- cwd,
8724
- encoding: "utf8",
8725
- stdio: [
8726
- "ignore",
8727
- "pipe",
8728
- "ignore"
8729
- ]
8730
- }).trim();
8739
+ execFileSync("git", ["init", "--quiet"], {
8740
+ cwd: folder,
8741
+ stdio: "ignore",
8742
+ env: plainGitEnv(env),
8743
+ timeout,
8744
+ killSignal: "SIGKILL"
8745
+ });
8746
+ return existsSync(join(folder, ".git"));
8731
8747
  } catch {
8732
- return null;
8748
+ return false;
8733
8749
  }
8734
8750
  }
8751
+ //#endregion
8752
+ //#region packages/server/src/studybase.ts
8753
+ init_src$1();
8735
8754
  /** Where format 1 put a study base when not told: beside the knowledge base's repository, named after it. */
8736
8755
  function defaultStudyPath(notes) {
8737
- const repo = git(notes, "rev-parse", "--show-toplevel") ?? notes;
8756
+ const repo = repoOf(notes) ?? resolve(notes);
8738
8757
  return join(dirname(repo), basename(repo) + "-study");
8739
8758
  }
8740
8759
  /** A study base's configuration, and its knowledge base's folder as an absolute path. */
@@ -8946,6 +8965,12 @@ function openBrowser(url) {
8946
8965
  }).on("error", () => void 0).unref();
8947
8966
  } catch {}
8948
8967
  }
8968
+ /** How long a step may take before it's said what is being waited for (M21), and where it's said: stderr, as
8969
+ * `mcp` speaks on stdout. */
8970
+ const SLOW = 2e3;
8971
+ const waiting = (line) => {
8972
+ process.stderr.write(line + "\n");
8973
+ };
8949
8974
  /** The name the server on this URL serves, or null when nothing answers. */
8950
8975
  async function serving(url) {
8951
8976
  try {
@@ -8959,7 +8984,7 @@ async function serving(url) {
8959
8984
  * well from now on, M20; or for any, when no base is named), or one started for the base, its output kept in a
8960
8985
  * log. `url` is the base's own address there. A server this started stops once the last of those using it lets it
8961
8986
  * go; one you started yourself goes on. */
8962
- async function ensureServer(path, { port = 4767, env = process.env, wait = 12e4 } = {}) {
8987
+ async function ensureServer(path, { port = 4767, env = process.env, wait = 12e4, say = waiting } = {}) {
8963
8988
  const root = `http://127.0.0.1:${port}`;
8964
8989
  const opened = path === void 0 ? null : locate(path);
8965
8990
  let name = await serving(root), started = null;
@@ -8985,7 +9010,12 @@ async function ensureServer(path, { port = 4767, env = process.env, wait = 12e4
8985
9010
  });
8986
9011
  const exited = new Promise((done) => started.once("exit", () => done()));
8987
9012
  const begun = Date.now();
9013
+ let told = false;
8988
9014
  while ((name = await serving(root)) === null) {
9015
+ if (!told && Date.now() - begun > SLOW) {
9016
+ say(`isorite: waiting for the isorite server; its output is in ${log}`);
9017
+ told = true;
9018
+ }
8989
9019
  if (started.exitCode !== null || Date.now() - begun > wait) {
8990
9020
  started.kill();
8991
9021
  throw new StudyError(`the isorite server did not start; its output is in ${log}`);
@@ -9054,7 +9084,7 @@ async function tell(url, path, body) {
9054
9084
  }
9055
9085
  /** OpenCode's own server, on this machine only, with a password of its own; its output kept in a log. The terminal
9056
9086
  * attaches to it, and so does the isorite server, which shows its session in the Iso pane (M11). */
9057
- async function startOpenCode(command, { cwd, env, wait = 6e4 }) {
9087
+ async function startOpenCode(command, { cwd, env, wait = 6e4, say = waiting }) {
9058
9088
  const port = await freePort(), password = randomBytes(24).toString("base64url"), url = `http://127.0.0.1:${port}`;
9059
9089
  const log = join(tmpdir(), `isorite-opencode-${port}.log`);
9060
9090
  const out = openSync(log, "w");
@@ -9078,7 +9108,17 @@ async function startOpenCode(command, { cwd, env, wait = 6e4 }) {
9078
9108
  });
9079
9109
  const auth = { authorization: "Basic " + Buffer.from(`opencode:${password}`).toString("base64") };
9080
9110
  const started = Date.now();
9111
+ let told = 0;
9081
9112
  for (;;) {
9113
+ const waited = Date.now() - started;
9114
+ if (told === 0 && waited > SLOW) {
9115
+ say("isorite: waiting for OpenCode's server");
9116
+ told = 1;
9117
+ }
9118
+ if (told === 1 && waited > 1e4) {
9119
+ say(`isorite: OpenCode's server has not answered in 10 s (a git it runs may be waiting); its output is in ${log}`);
9120
+ told = 2;
9121
+ }
9082
9122
  if (server.exitCode !== null || Date.now() - started > wait) {
9083
9123
  server.kill();
9084
9124
  throw new StudyError(`OpenCode's server did not start; its output is in ${log}`);
@@ -9176,7 +9216,8 @@ async function term(path, { port = 4767, cwd = process.cwd(), env: given = proce
9176
9216
  const shared = {
9177
9217
  ...env,
9178
9218
  OPENCODE_CONFIG_DIR: home,
9179
- ISORITE_URL: server.url
9219
+ ISORITE_URL: server.url,
9220
+ GIT_TERMINAL_PROMPT: "0"
9180
9221
  };
9181
9222
  let backend;
9182
9223
  try {
@@ -9200,7 +9241,8 @@ async function term(path, { port = 4767, cwd = process.cwd(), env: given = proce
9200
9241
  ...given,
9201
9242
  OPENCODE_CONFIG_DIR: home,
9202
9243
  ISORITE_URL: server.url,
9203
- OPENCODE_PASSWORD: backend.password
9244
+ OPENCODE_PASSWORD: backend.password,
9245
+ GIT_TERMINAL_PROMPT: "0"
9204
9246
  }
9205
9247
  });
9206
9248
  const forward = (signal) => () => child.kill(signal);
@@ -10941,7 +10983,7 @@ anything secret: keys, passwords, tokens.
10941
10983
  `;
10942
10984
  /** Make the memory, or say where it is: the folder (as isorite new makes it), a git repository of its own, its
10943
10985
  * practice, and its row in bases.toml, lent. */
10944
- function memory(folder = MEMORY_FOLDER, { config = configPath(), out = console.log } = {}) {
10986
+ function memory(folder = MEMORY_FOLDER, { config = configPath(), out = console.log, env = process.env } = {}) {
10945
10987
  const at = resolve(folder), known = knownBases(config);
10946
10988
  const had = known.find((b) => b.path === at) ?? (folder === MEMORY_FOLDER ? known.find((b) => b.name === "memory") : void 0);
10947
10989
  if (had && existsSync(join(had.path, "AGENTS.md"))) {
@@ -10952,15 +10994,7 @@ function memory(folder = MEMORY_FOLDER, { config = configPath(), out = console.l
10952
10994
  if (existsSync(at) && readdirSync(at).some((f) => f !== ".isorite" && f !== ".git")) throw new StudyError(`${at} has files in it already, and no ${AGENTS_FILE}: name another folder, isorite memory <folder>`);
10953
10995
  newBase(at);
10954
10996
  writeFileSync(join(at, AGENTS_FILE), MEMORY_AGENTS);
10955
- try {
10956
- if (!existsSync(join(at, ".git"))) execFileSync("git", ["init", "--quiet"], {
10957
- cwd: at,
10958
- stdio: "ignore"
10959
- });
10960
- } catch {
10961
- out(" (git is not here: the memory is not a repository yet)");
10962
- }
10963
- out(`isorite memory: made at ${at}, a private repository of its own: push it where you like`);
10997
+ out(existsSync(join(at, ".git")) || gitInit(at, { env }) ? `isorite memory: made at ${at}, a private repository of its own: push it where you like` : `isorite memory: made at ${at}; not a git repository (git did not answer, or is not here): git init it yourself`);
10964
10998
  }
10965
10999
  const name = rememberBase(at, {
10966
11000
  path: config,
@@ -11182,11 +11216,32 @@ function main(argv) {
11182
11216
  process.stdout.write(USAGE);
11183
11217
  return command && command !== "help" && command !== "--help" ? 2 : 0;
11184
11218
  }
11219
+ /** The commands that start something, which say so at once (M21): a subprocess that stalls after this is then
11220
+ * seen to stall. To stderr, as `mcp` speaks on stdout; once, not again when the command runs itself again with
11221
+ * the proxy on (ISORITE_STARTING). */
11222
+ const STARTS = /* @__PURE__ */ new Set([
11223
+ "serve",
11224
+ "term",
11225
+ "mcp",
11226
+ "guide",
11227
+ "tutorial",
11228
+ "--tutorial"
11229
+ ]);
11230
+ function starting(argv, env = process.env, write = (s) => process.stderr.write(s)) {
11231
+ if (!STARTS.has(argv[0] ?? "") || env.ISORITE_STARTING) return false;
11232
+ write("isorite: starting…\n");
11233
+ return true;
11234
+ }
11185
11235
  if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(resolve(process.argv[1]))).href) {
11236
+ starting(process.argv.slice(2));
11237
+ delete process.env.ISORITE_STARTING;
11186
11238
  const env = proxied(process.env);
11187
- if (env) process.exit(await rerun(env));
11239
+ if (env) process.exit(await rerun({
11240
+ ...env,
11241
+ ISORITE_STARTING: "1"
11242
+ }));
11188
11243
  trustSystem();
11189
11244
  process.exit(await main(process.argv.slice(2)));
11190
11245
  }
11191
11246
  //#endregion
11192
- export { check, main };
11247
+ export { check, main, starting };
@@ -157,6 +157,7 @@ Rules:
157
157
  | `The model could not be fetched:` and a line for npm, GitHub, Hugging Face | None reachable from here. | Copy the model's folder from a machine that has it (`isorite model` there says where), then `isorite model --from <folder>`; or any compatible model. |
158
158
  | `said 400: … stream_options` | The gateway refuses `stream_options`. | `stream_usage = false`. |
159
159
  | `✗ tools: … answered without calling the tool` | The model or gateway drops tools. | Choose a model that supports tool calls. Iso and the terminal need them. |
160
+ | `isorite: starting…` and then nothing, or `waiting for OpenCode's server` | Something it starts is stalled. isorite runs no `git` to start (0.8.1); OpenCode may, and a company's `git` wrapper that waits on the network stalls it. | Read the log the line names. Check `git --version` answers at once in that folder; if not, the wrapper is the cause. |
160
161
  | `isorite term needs OpenCode 2` | OpenCode 1, or none. | `npm i -g @opencode/cli`, or `ISORITE_OPENCODE=…`. |
161
162
  | `port 4767 is serving <other>, not <notes>` | Another base is served there. | Stop it, or `--port`. |
162
163
  | The link doesn't open, over SSH | `localhost` is the remote machine. | `ssh -L 4767:localhost:4767 <host>` from the person's own machine. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isorite",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "An agentic harness for understanding: your notes as a knowledge base, an Atlas of them, an agent that reads them, and study.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
- import{$ as e,A as t,B as n,D as r,E as i,F as a,H as o,J as s,L as c,O as l,P as u,Q as d,S as f,T as p,U as m,X as h,Y as g,_,at as v,dt as y,f as ee,h as b,i as x,j as S,k as C,n as w,ot as T,q as E,rt as te,s as ne,tt as D,u as re,x as ie,z as ae}from"./nMarIA7J.js";import{t as O}from"./BrXPCJaM.js";import"./xihTtKlq.js";import{A as k,C as oe,D as se,F as ce,S as le,_ as ue,a as de,b as fe,c as pe,f as me,h as he,i as ge,k as _e,m as ve,n as ye,o as be,p as xe,r as Se,s as Ce,t as we,u as Te,v as Ee,y as De}from"./B91efLFT.js";import{a as Oe,n as ke,r as Ae,s as je,t as A}from"./CnP_Si9g.js";import{i as Me}from"./hOAYc4Tf.js";import{B as Ne,D as j,E as Pe,F as M,G as Fe,H as Ie,I as Le,J as N,K as Re,L as P,M as F,N as I,P as ze,R as Be,S as Ve,W as He,X as Ue,Y as We,at as Ge,ct as Ke,et as qe,it as Je,lt as Ye,mt as Xe,n as Ze,nt as L,rt as R,t as Qe,ut as $e,v as et,w as tt,x as nt,y as rt}from"./BxTFtVqu.js";import{i as it,l as at,n as ot,o as st,r as ct}from"./yxd8jSz3.js";var lt=class{note;#e=e([]);get entries(){return c(this.#e)}set entries(e){d(this.#e,e)}#t=e(null);get prompt(){return c(this.#t)}set prompt(e){d(this.#t,e)}#n=e(null);get asking(){return c(this.#n)}set asking(e){d(this.#n,e)}#r=e(null);get failed(){return c(this.#r)}set failed(e){d(this.#r,e)}get tier(){return xe.tier}#i=e(null);get cursor(){return c(this.#i)}set cursor(e){d(this.#i,e)}#a=e(``);get busy(){return c(this.#a)}set busy(e){d(this.#a,e,!0)}#o=e(null);get editing(){return c(this.#o)}set editing(e){d(this.#o,e,!0)}stop=null;constructor(e){this.note=e}#s=D(()=>oe(this.entries));get waiting(){return c(this.#s)}set waiting(e){d(this.#s,e)}#c=D(()=>this.waiting.filter(e=>e.form===`insert`));get proposals(){return c(this.#c)}set proposals(e){d(this.#c,e)}#l=D(()=>{let e=[],t=se(this.note.text);for(let n of this.waiting)if(n.form===`rewrite`)for(let r of n.open??[]){let i=n.regions[r],a=ue(this.note.text,i.old),o=a?this.note.text.slice(0,a.from).split(`
1
+ import{$ as e,A as t,B as n,D as r,E as i,F as a,H as o,J as s,L as c,O as l,P as u,Q as d,S as f,T as p,U as m,X as h,Y as g,_,at as v,dt as y,f as ee,h as b,i as x,j as S,k as C,n as w,ot as T,q as E,rt as te,s as ne,tt as D,u as re,x as ie,z as ae}from"./nMarIA7J.js";import{t as O}from"./DtPULAe0.js";import"./xihTtKlq.js";import{A as k,C as oe,D as se,F as ce,S as le,_ as ue,a as de,b as fe,c as pe,f as me,h as he,i as ge,k as _e,m as ve,n as ye,o as be,p as xe,r as Se,s as Ce,t as we,u as Te,v as Ee,y as De}from"./D3BEa5-_.js";import{a as Oe,n as ke,r as Ae,s as je,t as A}from"./CurmVJyp.js";import{i as Me}from"./BAkGB2fM.js";import{B as Ne,D as j,E as Pe,F as M,G as Fe,H as Ie,I as Le,J as N,K as Re,L as P,M as F,N as I,P as ze,R as Be,S as Ve,W as He,X as Ue,Y as We,at as Ge,ct as Ke,et as qe,it as Je,lt as Ye,mt as Xe,n as Ze,nt as L,rt as R,t as Qe,ut as $e,v as et,w as tt,x as nt,y as rt}from"./BxTFtVqu.js";import{i as it,l as at,n as ot,o as st,r as ct}from"./yxd8jSz3.js";var lt=class{note;#e=e([]);get entries(){return c(this.#e)}set entries(e){d(this.#e,e)}#t=e(null);get prompt(){return c(this.#t)}set prompt(e){d(this.#t,e)}#n=e(null);get asking(){return c(this.#n)}set asking(e){d(this.#n,e)}#r=e(null);get failed(){return c(this.#r)}set failed(e){d(this.#r,e)}get tier(){return xe.tier}#i=e(null);get cursor(){return c(this.#i)}set cursor(e){d(this.#i,e)}#a=e(``);get busy(){return c(this.#a)}set busy(e){d(this.#a,e,!0)}#o=e(null);get editing(){return c(this.#o)}set editing(e){d(this.#o,e,!0)}stop=null;constructor(e){this.note=e}#s=D(()=>oe(this.entries));get waiting(){return c(this.#s)}set waiting(e){d(this.#s,e)}#c=D(()=>this.waiting.filter(e=>e.form===`insert`));get proposals(){return c(this.#c)}set proposals(e){d(this.#c,e)}#l=D(()=>{let e=[],t=se(this.note.text);for(let n of this.waiting)if(n.form===`rewrite`)for(let r of n.open??[]){let i=n.regions[r],a=ue(this.note.text,i.old),o=a?this.note.text.slice(0,a.from).split(`
2
2
  `).length-1:-1,s=t.find(e=>e.from<=o&&o<e.to);e.push({key:`r:${n.id}:${r}`,proposal:n,k:r,at:a,block:/\n/.test(i.old.text.trim())||/\n/.test(i.new.trim()),code:!!(s&&/^\s*(```|~~~)/.test(s.text))})}return e.sort((e,t)=>(e.at?.from??1/0)-(t.at?.from??1/0))});get changes(){return c(this.#l)}set changes(e){d(this.#l,e)}#u=D(()=>{try{let e=k(this.note.text)[0];return{lines:e.split(`
3
3
  `).length-1,chars:e.length}}catch{return{lines:0,chars:0}}});get head(){return c(this.#u)}set head(e){d(this.#u,e)}get foot(){return this.note.text.split(`
4
4
  `).length}spans(e){return e.flatMap(e=>{let t=ue(this.note.text,e);return t?[t]:[]})}placed(e){return fe(this.note.text,e)!==null}#d=D(()=>{let e=[];for(let t of this.proposals){let n=fe(this.note.text,t);e.push({key:`p:${t.id}`,line:n?.form===`insert`?n.line:this.foot})}this.changes.some(e=>!e.at)&&e.push({key:`lost`,line:this.foot}),this.asking&&e.push({key:`asking`,line:this.asking.line}),this.failed&&e.push({key:`failed`,line:this.failed.line}),this.prompt&&e.push({key:`prompt`,line:this.prompt.line});let t=this.changes.filter(e=>e.at);for(let n of t)n.block&&e.push({key:n.key,line:this.note.text.slice(0,n.at.to).split(`