@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.
- package/dist/cli.js +196 -128
- 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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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 (
|
|
7295
|
+
if (existsSync17(dest)) return [dim(` \u2717 ${dest} already exists \u2014 give a different path: /save ${idx} <path>`)];
|
|
7261
7296
|
try {
|
|
7262
|
-
|
|
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
|
|
7321
|
-
import { join as
|
|
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 =
|
|
7355
|
-
if (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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(
|
|
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 =
|
|
7464
|
+
const path = join27(cwd, "AGENTS.md");
|
|
7430
7465
|
const facts = detectProject(cwd);
|
|
7431
|
-
if (
|
|
7432
|
-
|
|
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
|
|
7827
|
-
import { existsSync as
|
|
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 =
|
|
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 &&
|
|
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
|
|
8198
|
-
import { join as
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
|
8912
|
-
import { join as
|
|
8979
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync22 } from "fs";
|
|
8980
|
+
import { join as join31 } from "path";
|
|
8913
8981
|
function file5() {
|
|
8914
|
-
return
|
|
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
|
-
|
|
8995
|
+
writeFileSync22(join31(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
|
|
8928
8996
|
}
|
|
8929
8997
|
function removeChannel(kind) {
|
|
8930
|
-
|
|
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
|
|
9119
|
+
import { join as join32 } from "path";
|
|
9052
9120
|
function whatsappAuthDir() {
|
|
9053
|
-
return
|
|
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
|
|
9174
|
-
import { resolve as resolve2, join as
|
|
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 (!
|
|
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 (!
|
|
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,
|
|
9215
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
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(
|
|
9219
|
-
cpSync(src,
|
|
9220
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
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 =
|
|
9228
|
-
if (!
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
9867
|
-
import { join as
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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,
|
|
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.
|
|
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"
|