@oriro/orirocli 0.3.1 → 0.3.3
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 +262 -54
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -6363,6 +6363,187 @@ function handleArtifactSlash(raw) {
|
|
|
6363
6363
|
return lines;
|
|
6364
6364
|
}
|
|
6365
6365
|
|
|
6366
|
+
// src/repl-ui/slash-compact.ts
|
|
6367
|
+
function isCompactSlash(cmd) {
|
|
6368
|
+
return /^\/compact(\s|$)/i.test(cmd.trim());
|
|
6369
|
+
}
|
|
6370
|
+
function compactInstructions(cmd) {
|
|
6371
|
+
const rest = cmd.trim().replace(/^\/compact\s*/i, "").trim();
|
|
6372
|
+
return rest.length ? rest : void 0;
|
|
6373
|
+
}
|
|
6374
|
+
function formatCompactionResult(result) {
|
|
6375
|
+
const before = result.tokensBefore;
|
|
6376
|
+
const after = result.estimatedTokensAfter;
|
|
6377
|
+
const lines = [];
|
|
6378
|
+
if (typeof after === "number" && before > 0) {
|
|
6379
|
+
const freed = Math.max(0, before - after);
|
|
6380
|
+
const pct = Math.round(freed / before * 100);
|
|
6381
|
+
lines.push(
|
|
6382
|
+
` ${fgHex(PALETTE.success, "\u2713 compacted")} ${dim(`${before.toLocaleString()} \u2192 ${after.toLocaleString()} tokens`)} ${accent(`(${pct}% freed)`)}`
|
|
6383
|
+
);
|
|
6384
|
+
} else {
|
|
6385
|
+
lines.push(` ${fgHex(PALETTE.success, "\u2713 compacted")} ${dim(`${before.toLocaleString()} tokens summarized`)}`);
|
|
6386
|
+
}
|
|
6387
|
+
lines.push(dim(" history summarized; the summary is kept, raw turns dropped. Keep going."));
|
|
6388
|
+
return lines;
|
|
6389
|
+
}
|
|
6390
|
+
async function handleCompact(session, cmd) {
|
|
6391
|
+
if (session.isCompacting) {
|
|
6392
|
+
return [dim(" compaction already in progress \u2014 hold on\u2026")];
|
|
6393
|
+
}
|
|
6394
|
+
if (session.messages.length < 4) {
|
|
6395
|
+
return [dim(" not much to compact yet \u2014 keep chatting, then /compact frees context.")];
|
|
6396
|
+
}
|
|
6397
|
+
try {
|
|
6398
|
+
const result = await session.compact(compactInstructions(cmd));
|
|
6399
|
+
if (!result) return [dim(" nothing to compact right now.")];
|
|
6400
|
+
return formatCompactionResult(result);
|
|
6401
|
+
} catch (e) {
|
|
6402
|
+
return [` ${fgHex(PALETTE.error, "compaction failed")}: ${dim(e instanceof Error ? e.message : String(e))}`];
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
|
|
6406
|
+
// src/context/init-agents.ts
|
|
6407
|
+
import { existsSync as existsSync17, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync19 } from "fs";
|
|
6408
|
+
import { join as join25, basename } from "path";
|
|
6409
|
+
var CODE_EXT = {
|
|
6410
|
+
ts: "TypeScript",
|
|
6411
|
+
tsx: "TypeScript",
|
|
6412
|
+
js: "JavaScript",
|
|
6413
|
+
jsx: "JavaScript",
|
|
6414
|
+
mjs: "JavaScript",
|
|
6415
|
+
py: "Python",
|
|
6416
|
+
go: "Go",
|
|
6417
|
+
rs: "Rust",
|
|
6418
|
+
java: "Java",
|
|
6419
|
+
kt: "Kotlin",
|
|
6420
|
+
rb: "Ruby",
|
|
6421
|
+
php: "PHP",
|
|
6422
|
+
c: "C",
|
|
6423
|
+
h: "C",
|
|
6424
|
+
cpp: "C++",
|
|
6425
|
+
cc: "C++",
|
|
6426
|
+
cs: "C#",
|
|
6427
|
+
swift: "Swift",
|
|
6428
|
+
sh: "Shell",
|
|
6429
|
+
sql: "SQL"
|
|
6430
|
+
};
|
|
6431
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "target", "__pycache__", ".venv", "venv", ".oriro"]);
|
|
6432
|
+
function readJson(p) {
|
|
6433
|
+
try {
|
|
6434
|
+
return JSON.parse(readFileSync21(p, "utf8"));
|
|
6435
|
+
} catch {
|
|
6436
|
+
return {};
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
function detectProject(cwd) {
|
|
6440
|
+
const facts = { name: basename(cwd) || "project", languages: [], commands: [], topDirs: [] };
|
|
6441
|
+
const pkgPath = join25(cwd, "package.json");
|
|
6442
|
+
if (existsSync17(pkgPath)) {
|
|
6443
|
+
const pkg = readJson(pkgPath);
|
|
6444
|
+
if (typeof pkg.name === "string" && pkg.name) facts.name = pkg.name;
|
|
6445
|
+
if (typeof pkg.description === "string" && pkg.description) facts.description = pkg.description;
|
|
6446
|
+
const scripts = pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {};
|
|
6447
|
+
for (const key of ["dev", "build", "test", "lint", "start"]) {
|
|
6448
|
+
if (scripts[key]) facts.commands.push({ label: key, cmd: `npm run ${key}` });
|
|
6449
|
+
}
|
|
6450
|
+
} else if (existsSync17(join25(cwd, "pyproject.toml")) || existsSync17(join25(cwd, "requirements.txt"))) {
|
|
6451
|
+
if (!facts.description) facts.description = "Python project";
|
|
6452
|
+
} else if (existsSync17(join25(cwd, "Cargo.toml"))) {
|
|
6453
|
+
facts.commands.push({ label: "build", cmd: "cargo build" }, { label: "test", cmd: "cargo test" });
|
|
6454
|
+
} else if (existsSync17(join25(cwd, "go.mod"))) {
|
|
6455
|
+
facts.commands.push({ label: "build", cmd: "go build ./..." }, { label: "test", cmd: "go test ./..." });
|
|
6456
|
+
}
|
|
6457
|
+
const langCount = /* @__PURE__ */ new Map();
|
|
6458
|
+
const tallyExt = (file6) => {
|
|
6459
|
+
const ext = file6.split(".").pop()?.toLowerCase();
|
|
6460
|
+
const lang = ext && CODE_EXT[ext];
|
|
6461
|
+
if (lang) langCount.set(lang, (langCount.get(lang) ?? 0) + 1);
|
|
6462
|
+
};
|
|
6463
|
+
let entries = [];
|
|
6464
|
+
try {
|
|
6465
|
+
entries = readdirSync3(cwd);
|
|
6466
|
+
} catch {
|
|
6467
|
+
}
|
|
6468
|
+
for (const e of entries) {
|
|
6469
|
+
const full = join25(cwd, e);
|
|
6470
|
+
let isDir = false;
|
|
6471
|
+
try {
|
|
6472
|
+
isDir = statSync3(full).isDirectory();
|
|
6473
|
+
} catch {
|
|
6474
|
+
continue;
|
|
6475
|
+
}
|
|
6476
|
+
if (isDir) {
|
|
6477
|
+
if (SKIP_DIRS.has(e) || e.startsWith(".")) continue;
|
|
6478
|
+
facts.topDirs.push(e);
|
|
6479
|
+
try {
|
|
6480
|
+
for (const f of readdirSync3(full)) {
|
|
6481
|
+
try {
|
|
6482
|
+
if (statSync3(join25(full, f)).isFile()) tallyExt(f);
|
|
6483
|
+
} catch {
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
} catch {
|
|
6487
|
+
}
|
|
6488
|
+
} else {
|
|
6489
|
+
tallyExt(e);
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
facts.languages = [...langCount.entries()].sort((a, b) => b[1] - a[1]).map(([l]) => l);
|
|
6493
|
+
facts.topDirs.sort();
|
|
6494
|
+
return facts;
|
|
6495
|
+
}
|
|
6496
|
+
function generateAgentsMd(cwd) {
|
|
6497
|
+
const f = detectProject(cwd);
|
|
6498
|
+
const lines = [];
|
|
6499
|
+
lines.push(`# ${f.name}`, "");
|
|
6500
|
+
lines.push(f.description ?? "_One-line description of what this project does._", "");
|
|
6501
|
+
lines.push("## Stack");
|
|
6502
|
+
lines.push(f.languages.length ? `- Languages: ${f.languages.join(", ")}` : "- Languages: _add the main languages_");
|
|
6503
|
+
if (f.topDirs.length) lines.push(`- Layout: ${f.topDirs.map((d) => `\`${d}/\``).join(", ")}`);
|
|
6504
|
+
lines.push("");
|
|
6505
|
+
lines.push("## Commands");
|
|
6506
|
+
if (f.commands.length) for (const c of f.commands) lines.push(`- ${c.label}: \`${c.cmd}\``);
|
|
6507
|
+
else lines.push("- _add build/test/run commands here_");
|
|
6508
|
+
lines.push("");
|
|
6509
|
+
lines.push("## Conventions");
|
|
6510
|
+
lines.push("- _House rules for this repo: style, patterns to follow, things never to touch._");
|
|
6511
|
+
lines.push("- _ORIRO reads this file automatically each session \u2014 keep it short and current._");
|
|
6512
|
+
lines.push("");
|
|
6513
|
+
return lines.join("\n");
|
|
6514
|
+
}
|
|
6515
|
+
function writeAgentsMd(cwd = process.cwd(), force = false) {
|
|
6516
|
+
const path = join25(cwd, "AGENTS.md");
|
|
6517
|
+
const facts = detectProject(cwd);
|
|
6518
|
+
if (existsSync17(path) && !force) return { path, created: false, facts };
|
|
6519
|
+
writeFileSync19(path, generateAgentsMd(cwd), "utf8");
|
|
6520
|
+
return { path, created: true, facts };
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
// src/repl-ui/slash-init.ts
|
|
6524
|
+
function isInitSlash(cmd) {
|
|
6525
|
+
return /^\/init(\s|$)/i.test(cmd.trim());
|
|
6526
|
+
}
|
|
6527
|
+
function handleInit(cmd, cwd = process.cwd()) {
|
|
6528
|
+
const force = /(^|\s)--force(\s|$)/i.test(cmd);
|
|
6529
|
+
let res;
|
|
6530
|
+
try {
|
|
6531
|
+
res = writeAgentsMd(cwd, force);
|
|
6532
|
+
} catch (e) {
|
|
6533
|
+
return [` ${fgHex(PALETTE.error, "init failed")}: ${dim(e instanceof Error ? e.message : String(e))}`];
|
|
6534
|
+
}
|
|
6535
|
+
const lines = [];
|
|
6536
|
+
if (!res.created) {
|
|
6537
|
+
lines.push(` ${dim("AGENTS.md already exists")} ${accent(res.path)} ${dim("\u2014 use /init --force to overwrite.")}`);
|
|
6538
|
+
return lines;
|
|
6539
|
+
}
|
|
6540
|
+
const f = res.facts;
|
|
6541
|
+
lines.push(` ${fgHex(PALETTE.success, "\u2713 wrote")} ${accent(res.path)}`);
|
|
6542
|
+
lines.push(dim(` detected: ${f.languages.length ? f.languages.join(", ") : "no languages"}${f.commands.length ? ` \xB7 ${f.commands.length} command${f.commands.length === 1 ? "" : "s"}` : ""}${f.topDirs.length ? ` \xB7 ${f.topDirs.length} dir${f.topDirs.length === 1 ? "" : "s"}` : ""}`));
|
|
6543
|
+
lines.push(dim(" edit it to add house rules \u2014 ORIRO reads it automatically each session."));
|
|
6544
|
+
return lines;
|
|
6545
|
+
}
|
|
6546
|
+
|
|
6366
6547
|
// src/repl-ui/tui-repl.ts
|
|
6367
6548
|
var editorTheme = {
|
|
6368
6549
|
borderColor: (s) => dim(s),
|
|
@@ -6444,8 +6625,8 @@ async function runTuiRepl(session) {
|
|
|
6444
6625
|
if (slash === "/help" || slash === "/?") {
|
|
6445
6626
|
const help = [
|
|
6446
6627
|
" Just type to chat \u2014 ORIRO writes and runs code for you (keyless, free).",
|
|
6447
|
-
` ${accent("/routers")} pool add\xB7rotate ${accent("/model")} <id\u2026> switch ${accent("/usage")} health ${accent("/trace")} tool+router activity`,
|
|
6448
|
-
` ${accent("/review")} artifacts from the last reply ${accent("/save")} <n> [path] ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
|
|
6628
|
+
` ${accent("/routers")} pool add\xB7rotate ${accent("/model")} <id\u2026> switch ${accent("/usage")} health ${accent("/trace")} tool+router activity ${accent("/compact")} free context`,
|
|
6629
|
+
` ${accent("/review")} artifacts from the last reply ${accent("/save")} <n> [path] ${accent("/init")} write AGENTS.md ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
|
|
6449
6630
|
` ${dim("Shift+Tab")} posture ${dim("Alt+Shift+T")} thinking ${accent("/help")} ${accent("/exit")}`
|
|
6450
6631
|
].join("\n");
|
|
6451
6632
|
chat.addChild(new Text(help, 0, 0));
|
|
@@ -6496,6 +6677,24 @@ async function runTuiRepl(session) {
|
|
|
6496
6677
|
tui.requestRender();
|
|
6497
6678
|
return;
|
|
6498
6679
|
}
|
|
6680
|
+
if (isCompactSlash(slash)) {
|
|
6681
|
+
editor.setText("");
|
|
6682
|
+
const pending = new Text(dim(" compacting\u2026"), 0, 0);
|
|
6683
|
+
chat.addChild(pending);
|
|
6684
|
+
tui.requestRender();
|
|
6685
|
+
void (async () => {
|
|
6686
|
+
const lines = await handleCompact(session, text);
|
|
6687
|
+
pending.setText(lines.join("\n"));
|
|
6688
|
+
tui.requestRender();
|
|
6689
|
+
})();
|
|
6690
|
+
return;
|
|
6691
|
+
}
|
|
6692
|
+
if (isInitSlash(slash)) {
|
|
6693
|
+
chat.addChild(new Text(handleInit(text).join("\n"), 0, 0));
|
|
6694
|
+
editor.setText("");
|
|
6695
|
+
tui.requestRender();
|
|
6696
|
+
return;
|
|
6697
|
+
}
|
|
6499
6698
|
if (slash === "/voice") {
|
|
6500
6699
|
editor.setText("");
|
|
6501
6700
|
const status = new Text(dim(" \u{1F399} listening\u2026 (needs ffmpeg + the transformers voice peer)"), 0, 0);
|
|
@@ -6587,8 +6786,8 @@ ${english}`;
|
|
|
6587
6786
|
// src/voice/mic.ts
|
|
6588
6787
|
import { spawn as spawn3 } from "child_process";
|
|
6589
6788
|
import { tmpdir as tmpdir3 } from "os";
|
|
6590
|
-
import { join as
|
|
6591
|
-
import { existsSync as
|
|
6789
|
+
import { join as join26 } from "path";
|
|
6790
|
+
import { existsSync as existsSync18, statSync as statSync4 } from "fs";
|
|
6592
6791
|
function recorders(outFile, seconds) {
|
|
6593
6792
|
const dur = String(seconds);
|
|
6594
6793
|
if (process.platform === "darwin") {
|
|
@@ -6609,12 +6808,12 @@ function recorders(outFile, seconds) {
|
|
|
6609
6808
|
];
|
|
6610
6809
|
}
|
|
6611
6810
|
async function recordMic(seconds = 6) {
|
|
6612
|
-
const outFile =
|
|
6811
|
+
const outFile = join26(tmpdir3(), `oriro-voice-${process.pid}-${seconds}.wav`);
|
|
6613
6812
|
for (const r of recorders(outFile, seconds)) {
|
|
6614
6813
|
const okFile = await new Promise((resolve3) => {
|
|
6615
6814
|
const child = spawn3(r.cmd, r.args, { stdio: "ignore" });
|
|
6616
6815
|
child.on("error", () => resolve3(false));
|
|
6617
|
-
child.on("close", (code) => resolve3(code === 0 &&
|
|
6816
|
+
child.on("close", (code) => resolve3(code === 0 && existsSync18(outFile) && statSync4(outFile).size > 44));
|
|
6618
6817
|
});
|
|
6619
6818
|
if (okFile) return outFile;
|
|
6620
6819
|
}
|
|
@@ -6679,8 +6878,9 @@ function replHelp() {
|
|
|
6679
6878
|
${dim("Just type to chat; ORIRO writes and runs code for you (keyless, free).")}
|
|
6680
6879
|
|
|
6681
6880
|
${dim("Models & routers")} ${accent("/routers")} list\xB7add\xB7rotate the racing pool ${accent("/model")} <id\u2026> switch
|
|
6682
|
-
${dim("This session")} ${accent("/usage")} pool health & turns ${accent("/trace")} show tool + router activity
|
|
6881
|
+
${dim("This session")} ${accent("/usage")} pool health & turns ${accent("/trace")} show tool + router activity ${accent("/compact")} free context
|
|
6683
6882
|
${dim("Artifacts")} ${accent("/review")} code/SVG from the last reply ${accent("/save")} <n> [path] write one
|
|
6883
|
+
${dim("Project")} ${accent("/init")} write a starter AGENTS.md ORIRO reads each session
|
|
6684
6884
|
${dim("Capabilities")} ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")} speak a turn
|
|
6685
6885
|
${dim("General")} ${accent("/help")} this ${accent("/exit")} / ${accent("/quit")} leave ${dim("(Ctrl-D / Ctrl-C also exit)")}
|
|
6686
6886
|
|
|
@@ -6756,6 +6956,14 @@ async function runReadlineRepl(session) {
|
|
|
6756
6956
|
`);
|
|
6757
6957
|
continue;
|
|
6758
6958
|
}
|
|
6959
|
+
if (isCompactSlash(slash)) {
|
|
6960
|
+
stdout7.write((await handleCompact(session, line)).join("\n") + "\n");
|
|
6961
|
+
continue;
|
|
6962
|
+
}
|
|
6963
|
+
if (isInitSlash(slash)) {
|
|
6964
|
+
stdout7.write(handleInit(line).join("\n") + "\n");
|
|
6965
|
+
continue;
|
|
6966
|
+
}
|
|
6759
6967
|
if (isArtifactSlash(slash)) {
|
|
6760
6968
|
stdout7.write(handleArtifactSlash(line).join("\n") + "\n");
|
|
6761
6969
|
continue;
|
|
@@ -6883,8 +7091,8 @@ async function confirmDestructive(what, opts = {}) {
|
|
|
6883
7091
|
}
|
|
6884
7092
|
|
|
6885
7093
|
// src/config/store.ts
|
|
6886
|
-
import { readFileSync as
|
|
6887
|
-
import { join as
|
|
7094
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync20, mkdirSync as mkdirSync16 } from "fs";
|
|
7095
|
+
import { join as join27 } from "path";
|
|
6888
7096
|
var KEYS = {
|
|
6889
7097
|
output: {
|
|
6890
7098
|
desc: "default output format for list commands: text | json | csv",
|
|
@@ -6906,13 +7114,13 @@ function validateConfig(key, value) {
|
|
|
6906
7114
|
return KEYS[key].validate?.(value) ?? null;
|
|
6907
7115
|
}
|
|
6908
7116
|
function file4() {
|
|
6909
|
-
return
|
|
7117
|
+
return join27(oriroDir(), "config.json");
|
|
6910
7118
|
}
|
|
6911
7119
|
var cache = null;
|
|
6912
7120
|
function readAll() {
|
|
6913
7121
|
if (cache) return cache;
|
|
6914
7122
|
try {
|
|
6915
|
-
const v = JSON.parse(
|
|
7123
|
+
const v = JSON.parse(readFileSync22(file4(), "utf8"));
|
|
6916
7124
|
cache = v && typeof v === "object" ? v : {};
|
|
6917
7125
|
} catch {
|
|
6918
7126
|
cache = {};
|
|
@@ -6928,7 +7136,7 @@ function configAll() {
|
|
|
6928
7136
|
function configSet(key, value) {
|
|
6929
7137
|
const all = { ...readAll(), [key]: value };
|
|
6930
7138
|
mkdirSync16(oriroDir(), { recursive: true });
|
|
6931
|
-
|
|
7139
|
+
writeFileSync20(file4(), JSON.stringify(all, null, 2), "utf8");
|
|
6932
7140
|
cache = all;
|
|
6933
7141
|
}
|
|
6934
7142
|
function configUnset(key) {
|
|
@@ -6936,7 +7144,7 @@ function configUnset(key) {
|
|
|
6936
7144
|
if (!(key in all)) return false;
|
|
6937
7145
|
const rest = { ...all };
|
|
6938
7146
|
delete rest[key];
|
|
6939
|
-
|
|
7147
|
+
writeFileSync20(file4(), JSON.stringify(rest, null, 2), "utf8");
|
|
6940
7148
|
cache = rest;
|
|
6941
7149
|
return true;
|
|
6942
7150
|
}
|
|
@@ -7079,10 +7287,10 @@ function registerRoutersCommand(program2) {
|
|
|
7079
7287
|
}
|
|
7080
7288
|
|
|
7081
7289
|
// src/commands/scribe.ts
|
|
7082
|
-
import { readFileSync as
|
|
7290
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
7083
7291
|
|
|
7084
7292
|
// src/scribe/transcript.ts
|
|
7085
|
-
import { existsSync as
|
|
7293
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
|
|
7086
7294
|
function parseHookStdin(raw) {
|
|
7087
7295
|
try {
|
|
7088
7296
|
const j = JSON.parse(raw);
|
|
@@ -7115,8 +7323,8 @@ function isHumanUser(e) {
|
|
|
7115
7323
|
}
|
|
7116
7324
|
var FILE_KEYS = ["file_path", "path", "notebook_path", "filePath"];
|
|
7117
7325
|
function lastTurnFromTranscript(path) {
|
|
7118
|
-
if (!
|
|
7119
|
-
const raw =
|
|
7326
|
+
if (!existsSync20(path)) return null;
|
|
7327
|
+
const raw = readFileSync23(path, "utf8");
|
|
7120
7328
|
const entries = [];
|
|
7121
7329
|
for (const line of raw.split("\n")) {
|
|
7122
7330
|
if (!line.trim()) continue;
|
|
@@ -7177,7 +7385,7 @@ function lastTurnFromTranscript(path) {
|
|
|
7177
7385
|
// src/commands/scribe.ts
|
|
7178
7386
|
function readStdin() {
|
|
7179
7387
|
try {
|
|
7180
|
-
return
|
|
7388
|
+
return readFileSync24(0, "utf8");
|
|
7181
7389
|
} catch {
|
|
7182
7390
|
return "";
|
|
7183
7391
|
}
|
|
@@ -7487,14 +7695,14 @@ function registerConnectorsCommand(program2) {
|
|
|
7487
7695
|
}
|
|
7488
7696
|
|
|
7489
7697
|
// src/channels/config.ts
|
|
7490
|
-
import { readFileSync as
|
|
7491
|
-
import { join as
|
|
7698
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "fs";
|
|
7699
|
+
import { join as join28 } from "path";
|
|
7492
7700
|
function file5() {
|
|
7493
|
-
return
|
|
7701
|
+
return join28(oriroDir(), "channels.json");
|
|
7494
7702
|
}
|
|
7495
7703
|
function readChannels() {
|
|
7496
7704
|
try {
|
|
7497
|
-
const v = JSON.parse(
|
|
7705
|
+
const v = JSON.parse(readFileSync25(file5(), "utf8"));
|
|
7498
7706
|
return Array.isArray(v) ? v : [];
|
|
7499
7707
|
} catch {
|
|
7500
7708
|
return [];
|
|
@@ -7503,10 +7711,10 @@ function readChannels() {
|
|
|
7503
7711
|
function saveChannel(cfg) {
|
|
7504
7712
|
const all = readChannels().filter((c) => c.kind !== cfg.kind);
|
|
7505
7713
|
all.push(cfg);
|
|
7506
|
-
|
|
7714
|
+
writeFileSync21(join28(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
|
|
7507
7715
|
}
|
|
7508
7716
|
function removeChannel(kind) {
|
|
7509
|
-
|
|
7717
|
+
writeFileSync21(join28(ensureOriroDir(), "channels.json"), JSON.stringify(readChannels().filter((c) => c.kind !== kind), null, 2), "utf8");
|
|
7510
7718
|
}
|
|
7511
7719
|
|
|
7512
7720
|
// src/channels/telegram.ts
|
|
@@ -7623,9 +7831,9 @@ async function startDiscord(token) {
|
|
|
7623
7831
|
}
|
|
7624
7832
|
|
|
7625
7833
|
// src/channels/whatsapp.ts
|
|
7626
|
-
import { join as
|
|
7834
|
+
import { join as join29 } from "path";
|
|
7627
7835
|
function whatsappAuthDir() {
|
|
7628
|
-
return
|
|
7836
|
+
return join29(oriroDir(), "whatsapp-auth");
|
|
7629
7837
|
}
|
|
7630
7838
|
async function startWhatsApp() {
|
|
7631
7839
|
let baileys;
|
|
@@ -7743,8 +7951,8 @@ function registerChannelsCommand(program2) {
|
|
|
7743
7951
|
}
|
|
7744
7952
|
|
|
7745
7953
|
// src/commands/skills.ts
|
|
7746
|
-
import { existsSync as
|
|
7747
|
-
import { resolve as resolve2, join as
|
|
7954
|
+
import { existsSync as existsSync21, statSync as statSync5, mkdirSync as mkdirSync17, cpSync, rmSync as rmSync4 } from "fs";
|
|
7955
|
+
import { resolve as resolve2, join as join30, basename as basename2, dirname as dirname4 } from "path";
|
|
7748
7956
|
function registerSkillsCommand(program2) {
|
|
7749
7957
|
const skills = program2.command("skills").description("the ORIRO skill library \u2014 bundled + your own");
|
|
7750
7958
|
skills.command("list").description("show CORE / TAIL skill counts (use --all to list names)").option("-a, --all", "list every skill name").option("-o, --output <fmt>", "output format: text (default) | json | csv").option("-q, --query <expr>", "filter/select: 'field', 'field=value', or 'field=value:selectField'").action(async (opts) => {
|
|
@@ -7776,28 +7984,28 @@ function registerSkillsCommand(program2) {
|
|
|
7776
7984
|
});
|
|
7777
7985
|
skills.command("add <path>").description("add your own skill \u2014 a folder containing SKILL.md, or a SKILL.md file").action((p) => {
|
|
7778
7986
|
const src = resolve2(p);
|
|
7779
|
-
if (!
|
|
7987
|
+
if (!existsSync21(src)) die(`not found: ${src}`);
|
|
7780
7988
|
const dest = userSkillsDir();
|
|
7781
7989
|
mkdirSync17(dest, { recursive: true });
|
|
7782
|
-
const st =
|
|
7990
|
+
const st = statSync5(src);
|
|
7783
7991
|
if (st.isDirectory()) {
|
|
7784
|
-
if (!
|
|
7785
|
-
const name =
|
|
7786
|
-
cpSync(src,
|
|
7787
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
7788
|
-
} else if (
|
|
7789
|
-
const name =
|
|
7790
|
-
mkdirSync17(
|
|
7791
|
-
cpSync(src,
|
|
7792
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
7992
|
+
if (!existsSync21(join30(src, "SKILL.md"))) die(`no SKILL.md in ${src} \u2014 a skill folder must contain SKILL.md`);
|
|
7993
|
+
const name = basename2(src);
|
|
7994
|
+
cpSync(src, join30(dest, name), { recursive: true });
|
|
7995
|
+
ok(`added skill ${accent(name)} \u2192 ${join30(dest, name)}`);
|
|
7996
|
+
} else if (basename2(src).toLowerCase() === "skill.md") {
|
|
7997
|
+
const name = basename2(dirname4(src)) || "custom-skill";
|
|
7998
|
+
mkdirSync17(join30(dest, name), { recursive: true });
|
|
7999
|
+
cpSync(src, join30(dest, name, "SKILL.md"));
|
|
8000
|
+
ok(`added skill ${accent(name)} \u2192 ${join30(dest, name)}`);
|
|
7793
8001
|
} else {
|
|
7794
8002
|
die("expected a folder containing SKILL.md, or a SKILL.md file");
|
|
7795
8003
|
}
|
|
7796
8004
|
info("It loads on next launch \u2014 and is available in chat via /skill.");
|
|
7797
8005
|
});
|
|
7798
8006
|
skills.command("remove <name>").description("remove a skill you added").option("-f, --force", "skip the confirmation prompt").action(async (name, opts) => {
|
|
7799
|
-
const target =
|
|
7800
|
-
if (!
|
|
8007
|
+
const target = join30(userSkillsDir(), name);
|
|
8008
|
+
if (!existsSync21(target)) {
|
|
7801
8009
|
info(`'${name}' is not a user-added skill \u2014 nothing to remove`);
|
|
7802
8010
|
return;
|
|
7803
8011
|
}
|
|
@@ -7980,7 +8188,7 @@ function registerVoiceCommand(program2) {
|
|
|
7980
8188
|
}
|
|
7981
8189
|
|
|
7982
8190
|
// src/agents/catalog.ts
|
|
7983
|
-
import { readFileSync as
|
|
8191
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
7984
8192
|
function parseAgentDef(raw, now) {
|
|
7985
8193
|
if (!raw || typeof raw !== "object") return { ok: false, error: "not a JSON object" };
|
|
7986
8194
|
const o = raw;
|
|
@@ -8007,7 +8215,7 @@ async function fetchAgentSource(pathOrUrl) {
|
|
|
8007
8215
|
if (!res.ok) throw new Error(`fetch failed: HTTP ${res.status}`);
|
|
8008
8216
|
return await res.json();
|
|
8009
8217
|
}
|
|
8010
|
-
return JSON.parse(
|
|
8218
|
+
return JSON.parse(readFileSync26(pathOrUrl, "utf8"));
|
|
8011
8219
|
}
|
|
8012
8220
|
async function addAgentFromSource(pathOrUrl, now) {
|
|
8013
8221
|
let raw;
|
|
@@ -8385,7 +8593,7 @@ function registerConfigCommand(program2) {
|
|
|
8385
8593
|
|
|
8386
8594
|
// src/commands/setup.ts
|
|
8387
8595
|
import { rmSync as rmSync5 } from "fs";
|
|
8388
|
-
import { join as
|
|
8596
|
+
import { join as join31 } from "path";
|
|
8389
8597
|
import { stdin as stdin12, stdout as stdout11 } from "process";
|
|
8390
8598
|
var MARKERS = [
|
|
8391
8599
|
"language.json",
|
|
@@ -8393,14 +8601,14 @@ var MARKERS = [
|
|
|
8393
8601
|
"skills-onboarded.json",
|
|
8394
8602
|
"connectors-onboarded.json",
|
|
8395
8603
|
"models-onboarded.json",
|
|
8396
|
-
|
|
8604
|
+
join31("routers", "onboarded.json")
|
|
8397
8605
|
];
|
|
8398
8606
|
function registerSetupCommand(program2) {
|
|
8399
8607
|
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) => {
|
|
8400
8608
|
if (opts.reset) {
|
|
8401
8609
|
for (const m of MARKERS) {
|
|
8402
8610
|
try {
|
|
8403
|
-
rmSync5(
|
|
8611
|
+
rmSync5(join31(oriroDir(), m), { force: true });
|
|
8404
8612
|
} catch {
|
|
8405
8613
|
}
|
|
8406
8614
|
}
|
|
@@ -8417,15 +8625,15 @@ function registerSetupCommand(program2) {
|
|
|
8417
8625
|
}
|
|
8418
8626
|
|
|
8419
8627
|
// src/commands/import.ts
|
|
8420
|
-
import { existsSync as
|
|
8421
|
-
import { join as
|
|
8628
|
+
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync4, statSync as statSync6, cpSync as cpSync2, mkdirSync as mkdirSync18 } from "fs";
|
|
8629
|
+
import { join as join32, basename as basename3 } from "path";
|
|
8422
8630
|
function registerImportCommand(program2) {
|
|
8423
8631
|
const imp = program2.command("import").description("migrate from another CLI (MCP servers, skills)");
|
|
8424
8632
|
imp.command("mcp <file>").description("import MCP servers from a Claude-compatible mcp.json (Guardian-vetted)").action((file6) => {
|
|
8425
|
-
if (!
|
|
8633
|
+
if (!existsSync22(file6)) die(`no such file: ${file6}`);
|
|
8426
8634
|
let servers;
|
|
8427
8635
|
try {
|
|
8428
|
-
const j = JSON.parse(
|
|
8636
|
+
const j = JSON.parse(readFileSync27(file6, "utf8"));
|
|
8429
8637
|
servers = j.mcpServers ?? j.servers ?? {};
|
|
8430
8638
|
} catch (e) {
|
|
8431
8639
|
die(`could not parse ${file6}: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -8471,15 +8679,15 @@ function registerImportCommand(program2) {
|
|
|
8471
8679
|
info(`${imported} imported \xB7 ${blocked2} blocked${imported ? ` \u2014 they connect in-session; see \`oriro connectors custom\`` : ""}`);
|
|
8472
8680
|
});
|
|
8473
8681
|
imp.command("skills <dir>").description("import SKILL.md skill folders from another CLI's skills directory").action((dir) => {
|
|
8474
|
-
if (!
|
|
8682
|
+
if (!existsSync22(dir) || !statSync6(dir).isDirectory()) die(`no such directory: ${dir}`);
|
|
8475
8683
|
const dest = userSkillsDir();
|
|
8476
8684
|
mkdirSync18(dest, { recursive: true });
|
|
8477
8685
|
heading("Import skills");
|
|
8478
|
-
const sources =
|
|
8686
|
+
const sources = existsSync22(join32(dir, "SKILL.md")) ? [dir] : readdirSync4(dir).map((e) => join32(dir, e)).filter((p) => statSync6(p).isDirectory() && existsSync22(join32(p, "SKILL.md")));
|
|
8479
8687
|
let n = 0;
|
|
8480
8688
|
for (const src of sources) {
|
|
8481
|
-
cpSync2(src,
|
|
8482
|
-
process.stdout.write(` ${fgHex(PALETTE.success, "\u2713")} ${accent(
|
|
8689
|
+
cpSync2(src, join32(dest, basename3(src)), { recursive: true });
|
|
8690
|
+
process.stdout.write(` ${fgHex(PALETTE.success, "\u2713")} ${accent(basename3(src))}
|
|
8483
8691
|
`);
|
|
8484
8692
|
n++;
|
|
8485
8693
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oriro/orirocli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
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",
|
|
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",
|
|
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"
|