@oriro/orirocli 0.3.8 → 0.3.10

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 (2) hide show
  1. package/dist/cli.js +196 -128
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -5130,7 +5130,7 @@ var init_assemble = __esm({
5130
5130
  // src/agents/worktree.ts
5131
5131
  import { execFile } from "child_process";
5132
5132
  import { promisify } from "util";
5133
- import { join as join27, basename as basename2 } from "path";
5133
+ import { join as join28, basename as basename2 } from "path";
5134
5134
  function parseAgentsSlash(line) {
5135
5135
  const m = /^\/agents(?:\s+(\S[\s\S]*))?$/i.exec(line.trim());
5136
5136
  if (!m) return void 0;
@@ -5152,7 +5152,7 @@ function fanBranch(stamp, i) {
5152
5152
  return `oriro/agents/${stamp}-a${i + 1}`;
5153
5153
  }
5154
5154
  function fanDir(repoRoot, stamp, i) {
5155
- return join27(oriroDir(), "worktrees", `${basename2(repoRoot)}-${stamp}-a${i + 1}`);
5155
+ return join28(oriroDir(), "worktrees", `${basename2(repoRoot)}-${stamp}-a${i + 1}`);
5156
5156
  }
5157
5157
  async function git(cwd, ...args) {
5158
5158
  try {
@@ -7030,13 +7030,119 @@ function parsePlanSlash(line) {
7030
7030
  return { cmd: "reject" };
7031
7031
  }
7032
7032
 
7033
+ // src/repl-ui/slash-imagine.ts
7034
+ import { existsSync as existsSync15, writeFileSync as writeFileSync18 } from "fs";
7035
+ import { join as join26 } from "path";
7036
+
7037
+ // src/repl-ui/artifacts.ts
7038
+ var LANG_EXT = {
7039
+ python: "py",
7040
+ py: "py",
7041
+ javascript: "js",
7042
+ js: "js",
7043
+ typescript: "ts",
7044
+ ts: "ts",
7045
+ tsx: "tsx",
7046
+ jsx: "jsx",
7047
+ html: "html",
7048
+ css: "css",
7049
+ json: "json",
7050
+ yaml: "yaml",
7051
+ yml: "yml",
7052
+ bash: "sh",
7053
+ sh: "sh",
7054
+ shell: "sh",
7055
+ sql: "sql",
7056
+ go: "go",
7057
+ rust: "rs",
7058
+ rs: "rs",
7059
+ java: "java",
7060
+ c: "c",
7061
+ cpp: "cpp",
7062
+ "c++": "cpp",
7063
+ ruby: "rb",
7064
+ rb: "rb",
7065
+ php: "php",
7066
+ markdown: "md",
7067
+ md: "md",
7068
+ svg: "svg"
7069
+ };
7070
+ function extFor(lang) {
7071
+ return LANG_EXT[lang.toLowerCase()] ?? "txt";
7072
+ }
7073
+ function extractArtifacts(text) {
7074
+ const out = [];
7075
+ if (!text) return out;
7076
+ const fence = /```([\w+#.-]*)\n([\s\S]*?)```/g;
7077
+ let m;
7078
+ while ((m = fence.exec(text)) !== null) {
7079
+ const lang = (m[1] ?? "").trim();
7080
+ const content = (m[2] ?? "").replace(/\n$/, "");
7081
+ if (!content.trim()) continue;
7082
+ const isSvg = lang.toLowerCase() === "svg" || /^\s*<svg[\s>]/.test(content);
7083
+ out.push({
7084
+ kind: isSvg ? "svg" : "code",
7085
+ lang: lang || (isSvg ? "svg" : ""),
7086
+ content,
7087
+ suggestedName: `artifact-${out.length + 1}.${isSvg ? "svg" : extFor(lang)}`
7088
+ });
7089
+ }
7090
+ const svg = /<svg[\s>][\s\S]*?<\/svg>/gi;
7091
+ while ((m = svg.exec(text)) !== null) {
7092
+ const content = m[0];
7093
+ if (out.some((a) => a.content.includes(content))) continue;
7094
+ out.push({ kind: "svg", lang: "svg", content, suggestedName: `artifact-${out.length + 1}.svg` });
7095
+ }
7096
+ return out;
7097
+ }
7098
+ var current3 = [];
7099
+ function setArtifacts(a) {
7100
+ current3 = a;
7101
+ }
7102
+ function getArtifacts() {
7103
+ return current3;
7104
+ }
7105
+
7106
+ // src/repl-ui/slash-imagine.ts
7107
+ init_theme();
7108
+ function isImagineSlash(cmd) {
7109
+ return /^\/imagine(\s|$)/i.test(cmd.trim());
7110
+ }
7111
+ function imagineTask(raw) {
7112
+ const rest = raw.trim().replace(/^\/imagine\s*/i, "").trim();
7113
+ return rest.length ? rest : void 0;
7114
+ }
7115
+ var IMAGINE_PRIMER = "IMAGE MODE: you are ORIRO's image engine. Create ONE complete, self-contained SVG artwork for the request below. Reply with ONLY a single fenced ```svg code block containing valid standalone SVG \u2014 root <svg> with xmlns and a viewBox, generous use of shapes/paths/gradients, NO external images, fonts, scripts or links. No prose before or after the block.";
7116
+ function imagineDest(now, cwd = process.cwd()) {
7117
+ const p = (n, w = 2) => String(n).padStart(w, "0");
7118
+ const base = `imagine-${p(now.getMonth() + 1)}${p(now.getDate())}-${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}`;
7119
+ let dest = join26(cwd, `${base}.svg`);
7120
+ for (let i = 2; existsSync15(dest); i++) dest = join26(cwd, `${base}-${i}.svg`);
7121
+ return dest;
7122
+ }
7123
+ function imagineResultLines(finalText, now = /* @__PURE__ */ new Date(), cwd) {
7124
+ const svg = extractArtifacts(finalText).find((a) => a.kind === "svg");
7125
+ if (!svg) {
7126
+ return [dim(" \u2300 no SVG came back this turn \u2014 /imagine again (free), or rephrase the scene.")];
7127
+ }
7128
+ const dest = imagineDest(now, cwd);
7129
+ try {
7130
+ writeFileSync18(dest, svg.content, "utf8");
7131
+ } catch (e) {
7132
+ return [dim(` \u2717 could not save the image: ${e instanceof Error ? e.message : String(e)} \u2014 /save it via /review instead`)];
7133
+ }
7134
+ return [
7135
+ ` ${fgHex(PALETTE.success, "\u2713 imagined")} \u2192 ${accent(dest)} ${dim(`(${svg.content.length} bytes \u2014 open it in any browser)`)}`
7136
+ ];
7137
+ }
7138
+
7033
7139
  // src/repl-ui/tui-repl.ts
7034
7140
  init_posture_gate();
7035
7141
  init_scribe_pi();
7036
7142
  init_filter();
7037
7143
 
7038
7144
  // src/repl-ui/verify-actions.ts
7039
- import { existsSync as existsSync15 } from "fs";
7145
+ import { existsSync as existsSync16 } from "fs";
7040
7146
  import { isAbsolute, resolve } from "path";
7041
7147
  var CLAIM = /\b(?:have|has)\s+been\s+created\b|\b(?:created|wrote|written|saved|generated)\b(?![ \t]*(?:by you|it yourself))/i;
7042
7148
  var SUGGESTION = /\byou\s+(?:can|could|should|may)\s+(?:create|add|save|make|put)\b/i;
@@ -7049,7 +7155,7 @@ function phantomFileWarning(reply, cwd = process.cwd()) {
7049
7155
  if (!p) continue;
7050
7156
  if (/^https?:|node_modules|<[^>]+>|your-|example\./i.test(p)) continue;
7051
7157
  const abs = isAbsolute(p) ? p : resolve(cwd, p.replace(/^[.][\\/]/, ""));
7052
- if (!existsSync15(abs)) missing.add(p);
7158
+ if (!existsSync16(abs)) missing.add(p);
7053
7159
  }
7054
7160
  if (missing.size === 0) return "";
7055
7161
  if (SUGGESTION.test(reply) && !/\b(?:have|has)\s+been\s+created\b/i.test(reply)) return "";
@@ -7169,78 +7275,7 @@ function handleUsage() {
7169
7275
  }
7170
7276
 
7171
7277
  // src/repl-ui/slash-artifacts.ts
7172
- import { existsSync as existsSync16, writeFileSync as writeFileSync18 } from "fs";
7173
-
7174
- // src/repl-ui/artifacts.ts
7175
- var LANG_EXT = {
7176
- python: "py",
7177
- py: "py",
7178
- javascript: "js",
7179
- js: "js",
7180
- typescript: "ts",
7181
- ts: "ts",
7182
- tsx: "tsx",
7183
- jsx: "jsx",
7184
- html: "html",
7185
- css: "css",
7186
- json: "json",
7187
- yaml: "yaml",
7188
- yml: "yml",
7189
- bash: "sh",
7190
- sh: "sh",
7191
- shell: "sh",
7192
- sql: "sql",
7193
- go: "go",
7194
- rust: "rs",
7195
- rs: "rs",
7196
- java: "java",
7197
- c: "c",
7198
- cpp: "cpp",
7199
- "c++": "cpp",
7200
- ruby: "rb",
7201
- rb: "rb",
7202
- php: "php",
7203
- markdown: "md",
7204
- md: "md",
7205
- svg: "svg"
7206
- };
7207
- function extFor(lang) {
7208
- return LANG_EXT[lang.toLowerCase()] ?? "txt";
7209
- }
7210
- function extractArtifacts(text) {
7211
- const out = [];
7212
- if (!text) return out;
7213
- const fence = /```([\w+#.-]*)\n([\s\S]*?)```/g;
7214
- let m;
7215
- while ((m = fence.exec(text)) !== null) {
7216
- const lang = (m[1] ?? "").trim();
7217
- const content = (m[2] ?? "").replace(/\n$/, "");
7218
- if (!content.trim()) continue;
7219
- const isSvg = lang.toLowerCase() === "svg" || /^\s*<svg[\s>]/.test(content);
7220
- out.push({
7221
- kind: isSvg ? "svg" : "code",
7222
- lang: lang || (isSvg ? "svg" : ""),
7223
- content,
7224
- suggestedName: `artifact-${out.length + 1}.${isSvg ? "svg" : extFor(lang)}`
7225
- });
7226
- }
7227
- const svg = /<svg[\s>][\s\S]*?<\/svg>/gi;
7228
- while ((m = svg.exec(text)) !== null) {
7229
- const content = m[0];
7230
- if (out.some((a) => a.content.includes(content))) continue;
7231
- out.push({ kind: "svg", lang: "svg", content, suggestedName: `artifact-${out.length + 1}.svg` });
7232
- }
7233
- return out;
7234
- }
7235
- var current3 = [];
7236
- function setArtifacts(a) {
7237
- current3 = a;
7238
- }
7239
- function getArtifacts() {
7240
- return current3;
7241
- }
7242
-
7243
- // src/repl-ui/slash-artifacts.ts
7278
+ import { existsSync as existsSync17, writeFileSync as writeFileSync19 } from "fs";
7244
7279
  init_theme();
7245
7280
  function isArtifactSlash(cmd) {
7246
7281
  return /^\/(review|artifacts?|save)(\s|$)/i.test(cmd.trim());
@@ -7257,9 +7292,9 @@ function handleArtifactSlash(raw) {
7257
7292
  const art = arts[idx - 1];
7258
7293
  if (!art) return [dim(" no such artifact")];
7259
7294
  const dest = parts[2] || art.suggestedName;
7260
- if (existsSync16(dest)) return [dim(` \u2717 ${dest} already exists \u2014 give a different path: /save ${idx} <path>`)];
7295
+ if (existsSync17(dest)) return [dim(` \u2717 ${dest} already exists \u2014 give a different path: /save ${idx} <path>`)];
7261
7296
  try {
7262
- writeFileSync18(dest, art.content, "utf8");
7297
+ writeFileSync19(dest, art.content, "utf8");
7263
7298
  } catch (e) {
7264
7299
  return [dim(` \u2717 could not write ${dest}: ${e instanceof Error ? e.message : String(e)}`)];
7265
7300
  }
@@ -7317,8 +7352,8 @@ async function handleCompact(session, cmd) {
7317
7352
  }
7318
7353
 
7319
7354
  // src/context/init-agents.ts
7320
- import { existsSync as existsSync17, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync19 } from "fs";
7321
- import { join as join26, basename } from "path";
7355
+ import { existsSync as existsSync18, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync20 } from "fs";
7356
+ import { join as join27, basename } from "path";
7322
7357
  var CODE_EXT = {
7323
7358
  ts: "TypeScript",
7324
7359
  tsx: "TypeScript",
@@ -7351,8 +7386,8 @@ function readJson(p) {
7351
7386
  }
7352
7387
  function detectProject(cwd) {
7353
7388
  const facts = { name: basename(cwd) || "project", languages: [], commands: [], topDirs: [] };
7354
- const pkgPath = join26(cwd, "package.json");
7355
- if (existsSync17(pkgPath)) {
7389
+ const pkgPath = join27(cwd, "package.json");
7390
+ if (existsSync18(pkgPath)) {
7356
7391
  const pkg = readJson(pkgPath);
7357
7392
  if (typeof pkg.name === "string" && pkg.name) facts.name = pkg.name;
7358
7393
  if (typeof pkg.description === "string" && pkg.description) facts.description = pkg.description;
@@ -7360,11 +7395,11 @@ function detectProject(cwd) {
7360
7395
  for (const key of ["dev", "build", "test", "lint", "start"]) {
7361
7396
  if (scripts[key]) facts.commands.push({ label: key, cmd: `npm run ${key}` });
7362
7397
  }
7363
- } else if (existsSync17(join26(cwd, "pyproject.toml")) || existsSync17(join26(cwd, "requirements.txt"))) {
7398
+ } else if (existsSync18(join27(cwd, "pyproject.toml")) || existsSync18(join27(cwd, "requirements.txt"))) {
7364
7399
  if (!facts.description) facts.description = "Python project";
7365
- } else if (existsSync17(join26(cwd, "Cargo.toml"))) {
7400
+ } else if (existsSync18(join27(cwd, "Cargo.toml"))) {
7366
7401
  facts.commands.push({ label: "build", cmd: "cargo build" }, { label: "test", cmd: "cargo test" });
7367
- } else if (existsSync17(join26(cwd, "go.mod"))) {
7402
+ } else if (existsSync18(join27(cwd, "go.mod"))) {
7368
7403
  facts.commands.push({ label: "build", cmd: "go build ./..." }, { label: "test", cmd: "go test ./..." });
7369
7404
  }
7370
7405
  const langCount = /* @__PURE__ */ new Map();
@@ -7379,7 +7414,7 @@ function detectProject(cwd) {
7379
7414
  } catch {
7380
7415
  }
7381
7416
  for (const e of entries) {
7382
- const full = join26(cwd, e);
7417
+ const full = join27(cwd, e);
7383
7418
  let isDir = false;
7384
7419
  try {
7385
7420
  isDir = statSync3(full).isDirectory();
@@ -7392,7 +7427,7 @@ function detectProject(cwd) {
7392
7427
  try {
7393
7428
  for (const f of readdirSync3(full)) {
7394
7429
  try {
7395
- if (statSync3(join26(full, f)).isFile()) tallyExt(f);
7430
+ if (statSync3(join27(full, f)).isFile()) tallyExt(f);
7396
7431
  } catch {
7397
7432
  }
7398
7433
  }
@@ -7426,10 +7461,10 @@ function generateAgentsMd(cwd) {
7426
7461
  return lines.join("\n");
7427
7462
  }
7428
7463
  function writeAgentsMd(cwd = process.cwd(), force = false) {
7429
- const path = join26(cwd, "AGENTS.md");
7464
+ const path = join27(cwd, "AGENTS.md");
7430
7465
  const facts = detectProject(cwd);
7431
- if (existsSync17(path) && !force) return { path, created: false, facts };
7432
- writeFileSync19(path, generateAgentsMd(cwd), "utf8");
7466
+ if (existsSync18(path) && !force) return { path, created: false, facts };
7467
+ writeFileSync20(path, generateAgentsMd(cwd), "utf8");
7433
7468
  return { path, created: true, facts };
7434
7469
  }
7435
7470
 
@@ -7599,6 +7634,7 @@ async function runTuiRepl(session) {
7599
7634
  ` ${accent("/review")} artifacts ${accent("/save")} <n> [path] ${accent("/init")} AGENTS.md ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
7600
7635
  ` ${accent("/sessions")} list saved ${accent("/undo")} rewind a turn ${dim("resume:")} ${accent("oriro -c")} / ${accent("oriro --resume <id>")}`,
7601
7636
  ` ${accent("/plan")} <task> plan read-only ${accent("/approve")} execute it ${accent("/reject")} discard ${accent("/agents")} parallel worktree fan-out`,
7637
+ ` ${accent("/imagine")} <scene> draw an SVG artwork (keyless, auto-saved)`,
7602
7638
  ` ${dim("Shift+Tab")} posture ${dim("Alt+Shift+T")} thinking ${accent("/help")} ${accent("/exit")}`
7603
7639
  ].join("\n");
7604
7640
  chat.addChild(new Text(help, 0, 0));
@@ -7726,6 +7762,18 @@ async function runTuiRepl(session) {
7726
7762
  turnText = plan.task;
7727
7763
  }
7728
7764
  }
7765
+ let imagineTurn = false;
7766
+ if (isImagineSlash(slash)) {
7767
+ const task = imagineTask(text);
7768
+ if (!task) {
7769
+ chat.addChild(new Text(dim(" usage: /imagine <what to draw> \u2014 ORIRO draws a self-contained SVG and saves it here"), 0, 0));
7770
+ editor.setText("");
7771
+ tui.requestRender();
7772
+ return;
7773
+ }
7774
+ imagineTurn = true;
7775
+ turnText = task;
7776
+ }
7729
7777
  if (slash === "/voice") {
7730
7778
  editor.setText("");
7731
7779
  const status = new Text(dim(" \u{1F399} listening\u2026 (needs ffmpeg + the transformers voice peer)"), 0, 0);
@@ -7765,6 +7813,9 @@ async function runTuiRepl(session) {
7765
7813
  bumpTurns();
7766
7814
  void (async () => {
7767
7815
  let english = internalPrompt ?? await translateIncoming(turnText);
7816
+ if (imagineTurn) english = `${IMAGINE_PRIMER}
7817
+
7818
+ ${english}`;
7768
7819
  if (getMode() === "plan") english = `${PLAN_PRIMER}
7769
7820
 
7770
7821
  ${english}`;
@@ -7810,6 +7861,7 @@ ${english}`;
7810
7861
  if (getMode() === "plan" && notePlanOutput(finalText)) {
7811
7862
  chat.addChild(new Text(dim(" \u25A2 plan ready \u2014 ") + accent("/approve") + dim(" to execute \xB7 ") + accent("/reject") + dim(" to discard"), 0, 0));
7812
7863
  }
7864
+ if (imagineTurn) chat.addChild(new Text(imagineResultLines(finalText).join("\n"), 0, 0));
7813
7865
  tui.requestRender();
7814
7866
  busy = false;
7815
7867
  })();
@@ -7823,8 +7875,8 @@ ${english}`;
7823
7875
  // src/voice/mic.ts
7824
7876
  import { spawn as spawn3 } from "child_process";
7825
7877
  import { tmpdir as tmpdir3 } from "os";
7826
- import { join as join28 } from "path";
7827
- import { existsSync as existsSync18, statSync as statSync4 } from "fs";
7878
+ import { join as join29 } from "path";
7879
+ import { existsSync as existsSync19, statSync as statSync4 } from "fs";
7828
7880
  function recorders(outFile, seconds) {
7829
7881
  const dur = String(seconds);
7830
7882
  if (process.platform === "darwin") {
@@ -7845,12 +7897,12 @@ function recorders(outFile, seconds) {
7845
7897
  ];
7846
7898
  }
7847
7899
  async function recordMic(seconds = 6) {
7848
- const outFile = join28(tmpdir3(), `oriro-voice-${process.pid}-${seconds}.wav`);
7900
+ const outFile = join29(tmpdir3(), `oriro-voice-${process.pid}-${seconds}.wav`);
7849
7901
  for (const r of recorders(outFile, seconds)) {
7850
7902
  const okFile = await new Promise((resolve3) => {
7851
7903
  const child = spawn3(r.cmd, r.args, { stdio: "ignore" });
7852
7904
  child.on("error", () => resolve3(false));
7853
- child.on("close", (code) => resolve3(code === 0 && existsSync18(outFile) && statSync4(outFile).size > 44));
7905
+ child.on("close", (code) => resolve3(code === 0 && existsSync19(outFile) && statSync4(outFile).size > 44));
7854
7906
  });
7855
7907
  if (okFile) return outFile;
7856
7908
  }
@@ -7922,6 +7974,7 @@ function replHelp() {
7922
7974
  ${dim("Continuity")} ${accent("/sessions")} list saved sessions ${dim("resume:")} ${accent("oriro -c")} ${dim("or")} ${accent("oriro --resume <id>")}
7923
7975
  ${dim("Plan loop")} ${accent("/plan")} <task> read-only plan ${accent("/approve")} execute it ${accent("/reject")} discard
7924
7976
  ${dim("Fan-out")} ${accent("/agents")} <A> | <B> parallel sub-agents in isolated git worktrees
7977
+ ${dim("Images")} ${accent("/imagine")} <scene> draw an SVG artwork (keyless, auto-saved to cwd)
7925
7978
  ${dim("Artifacts")} ${accent("/review")} code/SVG from the last reply ${accent("/save")} <n> [path] write one
7926
7979
  ${dim("Project")} ${accent("/init")} write a starter AGENTS.md ORIRO reads each session
7927
7980
  ${dim("Capabilities")} ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")} speak a turn
@@ -8054,8 +8107,22 @@ async function runReadlineRepl(session) {
8054
8107
  turnText = plan.task;
8055
8108
  }
8056
8109
  }
8110
+ let imagineTurn = false;
8111
+ if (isImagineSlash(slash)) {
8112
+ const task = imagineTask(line);
8113
+ if (!task) {
8114
+ stdout7.write(` ${dim("usage: /imagine <what to draw> \u2014 ORIRO draws a self-contained SVG and saves it here")}
8115
+ `);
8116
+ continue;
8117
+ }
8118
+ imagineTurn = true;
8119
+ turnText = task;
8120
+ }
8057
8121
  bumpTurns();
8058
8122
  let english = internalPrompt ?? await translateIncoming(turnText);
8123
+ if (imagineTurn) english = `${IMAGINE_PRIMER}
8124
+
8125
+ ${english}`;
8059
8126
  if (getMode() === "plan") english = `${PLAN_PRIMER}
8060
8127
 
8061
8128
  ${english}`;
@@ -8086,6 +8153,7 @@ ${hint}
8086
8153
  stdout7.write(` ${dim("\u25A2 plan ready \u2014")} ${accent("/approve")} ${dim("to execute \xB7")} ${accent("/reject")} ${dim("to discard")}
8087
8154
  `);
8088
8155
  }
8156
+ if (imagineTurn) stdout7.write(imagineResultLines(shown).join("\n") + "\n");
8089
8157
  }
8090
8158
  } finally {
8091
8159
  process.removeListener("SIGINT", onSigint);
@@ -8194,8 +8262,8 @@ import jmespath from "jmespath";
8194
8262
 
8195
8263
  // src/config/store.ts
8196
8264
  init_paths();
8197
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync20, mkdirSync as mkdirSync16 } from "fs";
8198
- import { join as join29 } from "path";
8265
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync21, mkdirSync as mkdirSync16 } from "fs";
8266
+ import { join as join30 } from "path";
8199
8267
  var KEYS = {
8200
8268
  output: {
8201
8269
  desc: "default output format for list commands: text | json | csv",
@@ -8217,7 +8285,7 @@ function validateConfig(key, value) {
8217
8285
  return KEYS[key].validate?.(value) ?? null;
8218
8286
  }
8219
8287
  function file4() {
8220
- return join29(oriroDir(), "config.json");
8288
+ return join30(oriroDir(), "config.json");
8221
8289
  }
8222
8290
  var cache = null;
8223
8291
  function readAll() {
@@ -8239,7 +8307,7 @@ function configAll() {
8239
8307
  function configSet(key, value) {
8240
8308
  const all = { ...readAll(), [key]: value };
8241
8309
  mkdirSync16(oriroDir(), { recursive: true });
8242
- writeFileSync20(file4(), JSON.stringify(all, null, 2), "utf8");
8310
+ writeFileSync21(file4(), JSON.stringify(all, null, 2), "utf8");
8243
8311
  cache = all;
8244
8312
  }
8245
8313
  function configUnset(key) {
@@ -8247,7 +8315,7 @@ function configUnset(key) {
8247
8315
  if (!(key in all)) return false;
8248
8316
  const rest = { ...all };
8249
8317
  delete rest[key];
8250
- writeFileSync20(file4(), JSON.stringify(rest, null, 2), "utf8");
8318
+ writeFileSync21(file4(), JSON.stringify(rest, null, 2), "utf8");
8251
8319
  cache = rest;
8252
8320
  return true;
8253
8321
  }
@@ -8496,7 +8564,7 @@ init_wal();
8496
8564
  init_retrieval();
8497
8565
 
8498
8566
  // src/scribe/transcript.ts
8499
- import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
8567
+ import { existsSync as existsSync21, readFileSync as readFileSync23 } from "fs";
8500
8568
  function parseHookStdin(raw) {
8501
8569
  try {
8502
8570
  const j = JSON.parse(raw);
@@ -8529,7 +8597,7 @@ function isHumanUser(e) {
8529
8597
  }
8530
8598
  var FILE_KEYS = ["file_path", "path", "notebook_path", "filePath"];
8531
8599
  function lastTurnFromTranscript(path) {
8532
- if (!existsSync20(path)) return null;
8600
+ if (!existsSync21(path)) return null;
8533
8601
  const raw = readFileSync23(path, "utf8");
8534
8602
  const entries = [];
8535
8603
  for (const line of raw.split("\n")) {
@@ -8908,10 +8976,10 @@ function registerConnectorsCommand(program2) {
8908
8976
 
8909
8977
  // src/channels/config.ts
8910
8978
  init_paths();
8911
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "fs";
8912
- import { join as join30 } from "path";
8979
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync22 } from "fs";
8980
+ import { join as join31 } from "path";
8913
8981
  function file5() {
8914
- return join30(oriroDir(), "channels.json");
8982
+ return join31(oriroDir(), "channels.json");
8915
8983
  }
8916
8984
  function readChannels() {
8917
8985
  try {
@@ -8924,10 +8992,10 @@ function readChannels() {
8924
8992
  function saveChannel(cfg) {
8925
8993
  const all = readChannels().filter((c) => c.kind !== cfg.kind);
8926
8994
  all.push(cfg);
8927
- writeFileSync21(join30(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
8995
+ writeFileSync22(join31(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
8928
8996
  }
8929
8997
  function removeChannel(kind) {
8930
- writeFileSync21(join30(ensureOriroDir(), "channels.json"), JSON.stringify(readChannels().filter((c) => c.kind !== kind), null, 2), "utf8");
8998
+ writeFileSync22(join31(ensureOriroDir(), "channels.json"), JSON.stringify(readChannels().filter((c) => c.kind !== kind), null, 2), "utf8");
8931
8999
  }
8932
9000
 
8933
9001
  // src/channels/telegram.ts
@@ -9048,9 +9116,9 @@ async function startDiscord(token) {
9048
9116
 
9049
9117
  // src/channels/whatsapp.ts
9050
9118
  init_paths();
9051
- import { join as join31 } from "path";
9119
+ import { join as join32 } from "path";
9052
9120
  function whatsappAuthDir() {
9053
- return join31(oriroDir(), "whatsapp-auth");
9121
+ return join32(oriroDir(), "whatsapp-auth");
9054
9122
  }
9055
9123
  async function startWhatsApp() {
9056
9124
  let baileys;
@@ -9170,8 +9238,8 @@ function registerChannelsCommand(program2) {
9170
9238
 
9171
9239
  // src/commands/skills.ts
9172
9240
  init_loader();
9173
- import { existsSync as existsSync21, statSync as statSync5, mkdirSync as mkdirSync17, cpSync, rmSync as rmSync4 } from "fs";
9174
- import { resolve as resolve2, join as join32, basename as basename3, dirname as dirname4 } from "path";
9241
+ import { existsSync as existsSync22, statSync as statSync5, mkdirSync as mkdirSync17, cpSync, rmSync as rmSync4 } from "fs";
9242
+ import { resolve as resolve2, join as join33, basename as basename3, dirname as dirname4 } from "path";
9175
9243
  init_theme();
9176
9244
  function registerSkillsCommand(program2) {
9177
9245
  const skills = program2.command("skills").description("the ORIRO skill library \u2014 bundled + your own");
@@ -9204,28 +9272,28 @@ function registerSkillsCommand(program2) {
9204
9272
  });
9205
9273
  skills.command("add <path>").description("add your own skill \u2014 a folder containing SKILL.md, or a SKILL.md file").action((p) => {
9206
9274
  const src = resolve2(p);
9207
- if (!existsSync21(src)) die(`not found: ${src}`);
9275
+ if (!existsSync22(src)) die(`not found: ${src}`);
9208
9276
  const dest = userSkillsDir();
9209
9277
  mkdirSync17(dest, { recursive: true });
9210
9278
  const st = statSync5(src);
9211
9279
  if (st.isDirectory()) {
9212
- if (!existsSync21(join32(src, "SKILL.md"))) die(`no SKILL.md in ${src} \u2014 a skill folder must contain SKILL.md`);
9280
+ if (!existsSync22(join33(src, "SKILL.md"))) die(`no SKILL.md in ${src} \u2014 a skill folder must contain SKILL.md`);
9213
9281
  const name = basename3(src);
9214
- cpSync(src, join32(dest, name), { recursive: true });
9215
- ok(`added skill ${accent(name)} \u2192 ${join32(dest, name)}`);
9282
+ cpSync(src, join33(dest, name), { recursive: true });
9283
+ ok(`added skill ${accent(name)} \u2192 ${join33(dest, name)}`);
9216
9284
  } else if (basename3(src).toLowerCase() === "skill.md") {
9217
9285
  const name = basename3(dirname4(src)) || "custom-skill";
9218
- mkdirSync17(join32(dest, name), { recursive: true });
9219
- cpSync(src, join32(dest, name, "SKILL.md"));
9220
- ok(`added skill ${accent(name)} \u2192 ${join32(dest, name)}`);
9286
+ mkdirSync17(join33(dest, name), { recursive: true });
9287
+ cpSync(src, join33(dest, name, "SKILL.md"));
9288
+ ok(`added skill ${accent(name)} \u2192 ${join33(dest, name)}`);
9221
9289
  } else {
9222
9290
  die("expected a folder containing SKILL.md, or a SKILL.md file");
9223
9291
  }
9224
9292
  info("It loads on next launch \u2014 and is available in chat via /skill.");
9225
9293
  });
9226
9294
  skills.command("remove <name>").description("remove a skill you added").option("-f, --force", "skip the confirmation prompt").action(async (name, opts) => {
9227
- const target = join32(userSkillsDir(), name);
9228
- if (!existsSync21(target)) {
9295
+ const target = join33(userSkillsDir(), name);
9296
+ if (!existsSync22(target)) {
9229
9297
  info(`'${name}' is not a user-added skill \u2014 nothing to remove`);
9230
9298
  return;
9231
9299
  }
@@ -9829,7 +9897,7 @@ function registerConfigCommand(program2) {
9829
9897
 
9830
9898
  // src/commands/setup.ts
9831
9899
  import { rmSync as rmSync5 } from "fs";
9832
- import { join as join33 } from "path";
9900
+ import { join as join34 } from "path";
9833
9901
  import { stdin as stdin12, stdout as stdout11 } from "process";
9834
9902
  init_paths();
9835
9903
  init_theme();
@@ -9839,14 +9907,14 @@ var MARKERS = [
9839
9907
  "skills-onboarded.json",
9840
9908
  "connectors-onboarded.json",
9841
9909
  "models-onboarded.json",
9842
- join33("routers", "onboarded.json")
9910
+ join34("routers", "onboarded.json")
9843
9911
  ];
9844
9912
  function registerSetupCommand(program2) {
9845
9913
  program2.command("setup").description("run the guided setup wizard (language \xB7 routers \xB7 connectors \xB7 skills \xB7 avatar)").option("--reset", "clear your settled choices and re-ask every step").action(async (opts) => {
9846
9914
  if (opts.reset) {
9847
9915
  for (const m of MARKERS) {
9848
9916
  try {
9849
- rmSync5(join33(oriroDir(), m), { force: true });
9917
+ rmSync5(join34(oriroDir(), m), { force: true });
9850
9918
  } catch {
9851
9919
  }
9852
9920
  }
@@ -9863,8 +9931,8 @@ function registerSetupCommand(program2) {
9863
9931
  }
9864
9932
 
9865
9933
  // src/commands/import.ts
9866
- import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync4, statSync as statSync6, cpSync as cpSync2, mkdirSync as mkdirSync18 } from "fs";
9867
- import { join as join34, basename as basename4 } from "path";
9934
+ import { existsSync as existsSync23, readFileSync as readFileSync27, readdirSync as readdirSync4, statSync as statSync6, cpSync as cpSync2, mkdirSync as mkdirSync18 } from "fs";
9935
+ import { join as join35, basename as basename4 } from "path";
9868
9936
  init_mcp_client();
9869
9937
  init_custom();
9870
9938
  init_loader();
@@ -9872,7 +9940,7 @@ init_theme();
9872
9940
  function registerImportCommand(program2) {
9873
9941
  const imp = program2.command("import").description("migrate from another CLI (MCP servers, skills)");
9874
9942
  imp.command("mcp <file>").description("import MCP servers from a Claude-compatible mcp.json (Guardian-vetted)").action((file6) => {
9875
- if (!existsSync22(file6)) die(`no such file: ${file6}`);
9943
+ if (!existsSync23(file6)) die(`no such file: ${file6}`);
9876
9944
  let servers;
9877
9945
  try {
9878
9946
  const j = JSON.parse(readFileSync27(file6, "utf8"));
@@ -9921,14 +9989,14 @@ function registerImportCommand(program2) {
9921
9989
  info(`${imported} imported \xB7 ${blocked2} blocked${imported ? ` \u2014 they connect in-session; see \`oriro connectors custom\`` : ""}`);
9922
9990
  });
9923
9991
  imp.command("skills <dir>").description("import SKILL.md skill folders from another CLI's skills directory").action((dir) => {
9924
- if (!existsSync22(dir) || !statSync6(dir).isDirectory()) die(`no such directory: ${dir}`);
9992
+ if (!existsSync23(dir) || !statSync6(dir).isDirectory()) die(`no such directory: ${dir}`);
9925
9993
  const dest = userSkillsDir();
9926
9994
  mkdirSync18(dest, { recursive: true });
9927
9995
  heading("Import skills");
9928
- const sources = existsSync22(join34(dir, "SKILL.md")) ? [dir] : readdirSync4(dir).map((e) => join34(dir, e)).filter((p) => statSync6(p).isDirectory() && existsSync22(join34(p, "SKILL.md")));
9996
+ const sources = existsSync23(join35(dir, "SKILL.md")) ? [dir] : readdirSync4(dir).map((e) => join35(dir, e)).filter((p) => statSync6(p).isDirectory() && existsSync23(join35(p, "SKILL.md")));
9929
9997
  let n = 0;
9930
9998
  for (const src of sources) {
9931
- cpSync2(src, join34(dest, basename4(src)), { recursive: true });
9999
+ cpSync2(src, join35(dest, basename4(src)), { recursive: true });
9932
10000
  process.stdout.write(` ${fgHex(PALETTE.success, "\u2713")} ${accent(basename4(src))}
9933
10001
  `);
9934
10002
  n++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oriro/orirocli",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "ORIRO — a free, on-device-friendly terminal AI agent. Built on the Pi agent harness (used as a library).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "dev": "tsx src/cli.ts",
24
24
  "build": "tsup",
25
25
  "typecheck": "tsc --noEmit",
26
- "test:unit": "tsx scripts/test-tool-sanitize.ts && tsx scripts/test-guardian.ts && tsx scripts/test-scribe.ts && tsx scripts/test-race.ts && tsx scripts/test-weights.ts && tsx scripts/test-output.ts && tsx scripts/test-connectors.ts && tsx scripts/test-artifacts.ts && tsx scripts/test-project-md.ts && tsx scripts/test-compact.ts && tsx scripts/test-init.ts && tsx scripts/test-sessions.ts && tsx scripts/test-permission.ts && tsx scripts/test-plan-mode.ts && tsx scripts/test-agents-fanout.ts && tsx scripts/test-serve.ts",
26
+ "test:unit": "tsx scripts/test-tool-sanitize.ts && tsx scripts/test-guardian.ts && tsx scripts/test-scribe.ts && tsx scripts/test-race.ts && tsx scripts/test-weights.ts && tsx scripts/test-output.ts && tsx scripts/test-connectors.ts && tsx scripts/test-artifacts.ts && tsx scripts/test-project-md.ts && tsx scripts/test-compact.ts && tsx scripts/test-init.ts && tsx scripts/test-sessions.ts && tsx scripts/test-permission.ts && tsx scripts/test-plan-mode.ts && tsx scripts/test-agents-fanout.ts && tsx scripts/test-serve.ts && tsx scripts/test-imagine.ts",
27
27
  "smoke": "npm run build && node scripts/smoke.mjs",
28
28
  "prepublishOnly": "npm run build && npm run test:unit && node scripts/smoke.mjs && node scripts/prepublish-check.mjs",
29
29
  "start": "node dist/cli.js"