@oriro/orirocli 0.3.2 → 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 +204 -52
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -6403,6 +6403,147 @@ async function handleCompact(session, cmd) {
|
|
|
6403
6403
|
}
|
|
6404
6404
|
}
|
|
6405
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
|
+
|
|
6406
6547
|
// src/repl-ui/tui-repl.ts
|
|
6407
6548
|
var editorTheme = {
|
|
6408
6549
|
borderColor: (s) => dim(s),
|
|
@@ -6485,7 +6626,7 @@ async function runTuiRepl(session) {
|
|
|
6485
6626
|
const help = [
|
|
6486
6627
|
" Just type to chat \u2014 ORIRO writes and runs code for you (keyless, free).",
|
|
6487
6628
|
` ${accent("/routers")} pool add\xB7rotate ${accent("/model")} <id\u2026> switch ${accent("/usage")} health ${accent("/trace")} tool+router activity ${accent("/compact")} free context`,
|
|
6488
|
-
` ${accent("/review")} artifacts from the last reply ${accent("/save")} <n> [path] ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
|
|
6629
|
+
` ${accent("/review")} artifacts from the last reply ${accent("/save")} <n> [path] ${accent("/init")} write AGENTS.md ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
|
|
6489
6630
|
` ${dim("Shift+Tab")} posture ${dim("Alt+Shift+T")} thinking ${accent("/help")} ${accent("/exit")}`
|
|
6490
6631
|
].join("\n");
|
|
6491
6632
|
chat.addChild(new Text(help, 0, 0));
|
|
@@ -6548,6 +6689,12 @@ async function runTuiRepl(session) {
|
|
|
6548
6689
|
})();
|
|
6549
6690
|
return;
|
|
6550
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
|
+
}
|
|
6551
6698
|
if (slash === "/voice") {
|
|
6552
6699
|
editor.setText("");
|
|
6553
6700
|
const status = new Text(dim(" \u{1F399} listening\u2026 (needs ffmpeg + the transformers voice peer)"), 0, 0);
|
|
@@ -6639,8 +6786,8 @@ ${english}`;
|
|
|
6639
6786
|
// src/voice/mic.ts
|
|
6640
6787
|
import { spawn as spawn3 } from "child_process";
|
|
6641
6788
|
import { tmpdir as tmpdir3 } from "os";
|
|
6642
|
-
import { join as
|
|
6643
|
-
import { existsSync as
|
|
6789
|
+
import { join as join26 } from "path";
|
|
6790
|
+
import { existsSync as existsSync18, statSync as statSync4 } from "fs";
|
|
6644
6791
|
function recorders(outFile, seconds) {
|
|
6645
6792
|
const dur = String(seconds);
|
|
6646
6793
|
if (process.platform === "darwin") {
|
|
@@ -6661,12 +6808,12 @@ function recorders(outFile, seconds) {
|
|
|
6661
6808
|
];
|
|
6662
6809
|
}
|
|
6663
6810
|
async function recordMic(seconds = 6) {
|
|
6664
|
-
const outFile =
|
|
6811
|
+
const outFile = join26(tmpdir3(), `oriro-voice-${process.pid}-${seconds}.wav`);
|
|
6665
6812
|
for (const r of recorders(outFile, seconds)) {
|
|
6666
6813
|
const okFile = await new Promise((resolve3) => {
|
|
6667
6814
|
const child = spawn3(r.cmd, r.args, { stdio: "ignore" });
|
|
6668
6815
|
child.on("error", () => resolve3(false));
|
|
6669
|
-
child.on("close", (code) => resolve3(code === 0 &&
|
|
6816
|
+
child.on("close", (code) => resolve3(code === 0 && existsSync18(outFile) && statSync4(outFile).size > 44));
|
|
6670
6817
|
});
|
|
6671
6818
|
if (okFile) return outFile;
|
|
6672
6819
|
}
|
|
@@ -6733,6 +6880,7 @@ function replHelp() {
|
|
|
6733
6880
|
${dim("Models & routers")} ${accent("/routers")} list\xB7add\xB7rotate the racing pool ${accent("/model")} <id\u2026> switch
|
|
6734
6881
|
${dim("This session")} ${accent("/usage")} pool health & turns ${accent("/trace")} show tool + router activity ${accent("/compact")} free context
|
|
6735
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
|
|
6736
6884
|
${dim("Capabilities")} ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")} speak a turn
|
|
6737
6885
|
${dim("General")} ${accent("/help")} this ${accent("/exit")} / ${accent("/quit")} leave ${dim("(Ctrl-D / Ctrl-C also exit)")}
|
|
6738
6886
|
|
|
@@ -6812,6 +6960,10 @@ async function runReadlineRepl(session) {
|
|
|
6812
6960
|
stdout7.write((await handleCompact(session, line)).join("\n") + "\n");
|
|
6813
6961
|
continue;
|
|
6814
6962
|
}
|
|
6963
|
+
if (isInitSlash(slash)) {
|
|
6964
|
+
stdout7.write(handleInit(line).join("\n") + "\n");
|
|
6965
|
+
continue;
|
|
6966
|
+
}
|
|
6815
6967
|
if (isArtifactSlash(slash)) {
|
|
6816
6968
|
stdout7.write(handleArtifactSlash(line).join("\n") + "\n");
|
|
6817
6969
|
continue;
|
|
@@ -6939,8 +7091,8 @@ async function confirmDestructive(what, opts = {}) {
|
|
|
6939
7091
|
}
|
|
6940
7092
|
|
|
6941
7093
|
// src/config/store.ts
|
|
6942
|
-
import { readFileSync as
|
|
6943
|
-
import { join as
|
|
7094
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync20, mkdirSync as mkdirSync16 } from "fs";
|
|
7095
|
+
import { join as join27 } from "path";
|
|
6944
7096
|
var KEYS = {
|
|
6945
7097
|
output: {
|
|
6946
7098
|
desc: "default output format for list commands: text | json | csv",
|
|
@@ -6962,13 +7114,13 @@ function validateConfig(key, value) {
|
|
|
6962
7114
|
return KEYS[key].validate?.(value) ?? null;
|
|
6963
7115
|
}
|
|
6964
7116
|
function file4() {
|
|
6965
|
-
return
|
|
7117
|
+
return join27(oriroDir(), "config.json");
|
|
6966
7118
|
}
|
|
6967
7119
|
var cache = null;
|
|
6968
7120
|
function readAll() {
|
|
6969
7121
|
if (cache) return cache;
|
|
6970
7122
|
try {
|
|
6971
|
-
const v = JSON.parse(
|
|
7123
|
+
const v = JSON.parse(readFileSync22(file4(), "utf8"));
|
|
6972
7124
|
cache = v && typeof v === "object" ? v : {};
|
|
6973
7125
|
} catch {
|
|
6974
7126
|
cache = {};
|
|
@@ -6984,7 +7136,7 @@ function configAll() {
|
|
|
6984
7136
|
function configSet(key, value) {
|
|
6985
7137
|
const all = { ...readAll(), [key]: value };
|
|
6986
7138
|
mkdirSync16(oriroDir(), { recursive: true });
|
|
6987
|
-
|
|
7139
|
+
writeFileSync20(file4(), JSON.stringify(all, null, 2), "utf8");
|
|
6988
7140
|
cache = all;
|
|
6989
7141
|
}
|
|
6990
7142
|
function configUnset(key) {
|
|
@@ -6992,7 +7144,7 @@ function configUnset(key) {
|
|
|
6992
7144
|
if (!(key in all)) return false;
|
|
6993
7145
|
const rest = { ...all };
|
|
6994
7146
|
delete rest[key];
|
|
6995
|
-
|
|
7147
|
+
writeFileSync20(file4(), JSON.stringify(rest, null, 2), "utf8");
|
|
6996
7148
|
cache = rest;
|
|
6997
7149
|
return true;
|
|
6998
7150
|
}
|
|
@@ -7135,10 +7287,10 @@ function registerRoutersCommand(program2) {
|
|
|
7135
7287
|
}
|
|
7136
7288
|
|
|
7137
7289
|
// src/commands/scribe.ts
|
|
7138
|
-
import { readFileSync as
|
|
7290
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
7139
7291
|
|
|
7140
7292
|
// src/scribe/transcript.ts
|
|
7141
|
-
import { existsSync as
|
|
7293
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
|
|
7142
7294
|
function parseHookStdin(raw) {
|
|
7143
7295
|
try {
|
|
7144
7296
|
const j = JSON.parse(raw);
|
|
@@ -7171,8 +7323,8 @@ function isHumanUser(e) {
|
|
|
7171
7323
|
}
|
|
7172
7324
|
var FILE_KEYS = ["file_path", "path", "notebook_path", "filePath"];
|
|
7173
7325
|
function lastTurnFromTranscript(path) {
|
|
7174
|
-
if (!
|
|
7175
|
-
const raw =
|
|
7326
|
+
if (!existsSync20(path)) return null;
|
|
7327
|
+
const raw = readFileSync23(path, "utf8");
|
|
7176
7328
|
const entries = [];
|
|
7177
7329
|
for (const line of raw.split("\n")) {
|
|
7178
7330
|
if (!line.trim()) continue;
|
|
@@ -7233,7 +7385,7 @@ function lastTurnFromTranscript(path) {
|
|
|
7233
7385
|
// src/commands/scribe.ts
|
|
7234
7386
|
function readStdin() {
|
|
7235
7387
|
try {
|
|
7236
|
-
return
|
|
7388
|
+
return readFileSync24(0, "utf8");
|
|
7237
7389
|
} catch {
|
|
7238
7390
|
return "";
|
|
7239
7391
|
}
|
|
@@ -7543,14 +7695,14 @@ function registerConnectorsCommand(program2) {
|
|
|
7543
7695
|
}
|
|
7544
7696
|
|
|
7545
7697
|
// src/channels/config.ts
|
|
7546
|
-
import { readFileSync as
|
|
7547
|
-
import { join as
|
|
7698
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "fs";
|
|
7699
|
+
import { join as join28 } from "path";
|
|
7548
7700
|
function file5() {
|
|
7549
|
-
return
|
|
7701
|
+
return join28(oriroDir(), "channels.json");
|
|
7550
7702
|
}
|
|
7551
7703
|
function readChannels() {
|
|
7552
7704
|
try {
|
|
7553
|
-
const v = JSON.parse(
|
|
7705
|
+
const v = JSON.parse(readFileSync25(file5(), "utf8"));
|
|
7554
7706
|
return Array.isArray(v) ? v : [];
|
|
7555
7707
|
} catch {
|
|
7556
7708
|
return [];
|
|
@@ -7559,10 +7711,10 @@ function readChannels() {
|
|
|
7559
7711
|
function saveChannel(cfg) {
|
|
7560
7712
|
const all = readChannels().filter((c) => c.kind !== cfg.kind);
|
|
7561
7713
|
all.push(cfg);
|
|
7562
|
-
|
|
7714
|
+
writeFileSync21(join28(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
|
|
7563
7715
|
}
|
|
7564
7716
|
function removeChannel(kind) {
|
|
7565
|
-
|
|
7717
|
+
writeFileSync21(join28(ensureOriroDir(), "channels.json"), JSON.stringify(readChannels().filter((c) => c.kind !== kind), null, 2), "utf8");
|
|
7566
7718
|
}
|
|
7567
7719
|
|
|
7568
7720
|
// src/channels/telegram.ts
|
|
@@ -7679,9 +7831,9 @@ async function startDiscord(token) {
|
|
|
7679
7831
|
}
|
|
7680
7832
|
|
|
7681
7833
|
// src/channels/whatsapp.ts
|
|
7682
|
-
import { join as
|
|
7834
|
+
import { join as join29 } from "path";
|
|
7683
7835
|
function whatsappAuthDir() {
|
|
7684
|
-
return
|
|
7836
|
+
return join29(oriroDir(), "whatsapp-auth");
|
|
7685
7837
|
}
|
|
7686
7838
|
async function startWhatsApp() {
|
|
7687
7839
|
let baileys;
|
|
@@ -7799,8 +7951,8 @@ function registerChannelsCommand(program2) {
|
|
|
7799
7951
|
}
|
|
7800
7952
|
|
|
7801
7953
|
// src/commands/skills.ts
|
|
7802
|
-
import { existsSync as
|
|
7803
|
-
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";
|
|
7804
7956
|
function registerSkillsCommand(program2) {
|
|
7805
7957
|
const skills = program2.command("skills").description("the ORIRO skill library \u2014 bundled + your own");
|
|
7806
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) => {
|
|
@@ -7832,28 +7984,28 @@ function registerSkillsCommand(program2) {
|
|
|
7832
7984
|
});
|
|
7833
7985
|
skills.command("add <path>").description("add your own skill \u2014 a folder containing SKILL.md, or a SKILL.md file").action((p) => {
|
|
7834
7986
|
const src = resolve2(p);
|
|
7835
|
-
if (!
|
|
7987
|
+
if (!existsSync21(src)) die(`not found: ${src}`);
|
|
7836
7988
|
const dest = userSkillsDir();
|
|
7837
7989
|
mkdirSync17(dest, { recursive: true });
|
|
7838
|
-
const st =
|
|
7990
|
+
const st = statSync5(src);
|
|
7839
7991
|
if (st.isDirectory()) {
|
|
7840
|
-
if (!
|
|
7841
|
-
const name =
|
|
7842
|
-
cpSync(src,
|
|
7843
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
7844
|
-
} else if (
|
|
7845
|
-
const name =
|
|
7846
|
-
mkdirSync17(
|
|
7847
|
-
cpSync(src,
|
|
7848
|
-
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)}`);
|
|
7849
8001
|
} else {
|
|
7850
8002
|
die("expected a folder containing SKILL.md, or a SKILL.md file");
|
|
7851
8003
|
}
|
|
7852
8004
|
info("It loads on next launch \u2014 and is available in chat via /skill.");
|
|
7853
8005
|
});
|
|
7854
8006
|
skills.command("remove <name>").description("remove a skill you added").option("-f, --force", "skip the confirmation prompt").action(async (name, opts) => {
|
|
7855
|
-
const target =
|
|
7856
|
-
if (!
|
|
8007
|
+
const target = join30(userSkillsDir(), name);
|
|
8008
|
+
if (!existsSync21(target)) {
|
|
7857
8009
|
info(`'${name}' is not a user-added skill \u2014 nothing to remove`);
|
|
7858
8010
|
return;
|
|
7859
8011
|
}
|
|
@@ -8036,7 +8188,7 @@ function registerVoiceCommand(program2) {
|
|
|
8036
8188
|
}
|
|
8037
8189
|
|
|
8038
8190
|
// src/agents/catalog.ts
|
|
8039
|
-
import { readFileSync as
|
|
8191
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
8040
8192
|
function parseAgentDef(raw, now) {
|
|
8041
8193
|
if (!raw || typeof raw !== "object") return { ok: false, error: "not a JSON object" };
|
|
8042
8194
|
const o = raw;
|
|
@@ -8063,7 +8215,7 @@ async function fetchAgentSource(pathOrUrl) {
|
|
|
8063
8215
|
if (!res.ok) throw new Error(`fetch failed: HTTP ${res.status}`);
|
|
8064
8216
|
return await res.json();
|
|
8065
8217
|
}
|
|
8066
|
-
return JSON.parse(
|
|
8218
|
+
return JSON.parse(readFileSync26(pathOrUrl, "utf8"));
|
|
8067
8219
|
}
|
|
8068
8220
|
async function addAgentFromSource(pathOrUrl, now) {
|
|
8069
8221
|
let raw;
|
|
@@ -8441,7 +8593,7 @@ function registerConfigCommand(program2) {
|
|
|
8441
8593
|
|
|
8442
8594
|
// src/commands/setup.ts
|
|
8443
8595
|
import { rmSync as rmSync5 } from "fs";
|
|
8444
|
-
import { join as
|
|
8596
|
+
import { join as join31 } from "path";
|
|
8445
8597
|
import { stdin as stdin12, stdout as stdout11 } from "process";
|
|
8446
8598
|
var MARKERS = [
|
|
8447
8599
|
"language.json",
|
|
@@ -8449,14 +8601,14 @@ var MARKERS = [
|
|
|
8449
8601
|
"skills-onboarded.json",
|
|
8450
8602
|
"connectors-onboarded.json",
|
|
8451
8603
|
"models-onboarded.json",
|
|
8452
|
-
|
|
8604
|
+
join31("routers", "onboarded.json")
|
|
8453
8605
|
];
|
|
8454
8606
|
function registerSetupCommand(program2) {
|
|
8455
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) => {
|
|
8456
8608
|
if (opts.reset) {
|
|
8457
8609
|
for (const m of MARKERS) {
|
|
8458
8610
|
try {
|
|
8459
|
-
rmSync5(
|
|
8611
|
+
rmSync5(join31(oriroDir(), m), { force: true });
|
|
8460
8612
|
} catch {
|
|
8461
8613
|
}
|
|
8462
8614
|
}
|
|
@@ -8473,15 +8625,15 @@ function registerSetupCommand(program2) {
|
|
|
8473
8625
|
}
|
|
8474
8626
|
|
|
8475
8627
|
// src/commands/import.ts
|
|
8476
|
-
import { existsSync as
|
|
8477
|
-
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";
|
|
8478
8630
|
function registerImportCommand(program2) {
|
|
8479
8631
|
const imp = program2.command("import").description("migrate from another CLI (MCP servers, skills)");
|
|
8480
8632
|
imp.command("mcp <file>").description("import MCP servers from a Claude-compatible mcp.json (Guardian-vetted)").action((file6) => {
|
|
8481
|
-
if (!
|
|
8633
|
+
if (!existsSync22(file6)) die(`no such file: ${file6}`);
|
|
8482
8634
|
let servers;
|
|
8483
8635
|
try {
|
|
8484
|
-
const j = JSON.parse(
|
|
8636
|
+
const j = JSON.parse(readFileSync27(file6, "utf8"));
|
|
8485
8637
|
servers = j.mcpServers ?? j.servers ?? {};
|
|
8486
8638
|
} catch (e) {
|
|
8487
8639
|
die(`could not parse ${file6}: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -8527,15 +8679,15 @@ function registerImportCommand(program2) {
|
|
|
8527
8679
|
info(`${imported} imported \xB7 ${blocked2} blocked${imported ? ` \u2014 they connect in-session; see \`oriro connectors custom\`` : ""}`);
|
|
8528
8680
|
});
|
|
8529
8681
|
imp.command("skills <dir>").description("import SKILL.md skill folders from another CLI's skills directory").action((dir) => {
|
|
8530
|
-
if (!
|
|
8682
|
+
if (!existsSync22(dir) || !statSync6(dir).isDirectory()) die(`no such directory: ${dir}`);
|
|
8531
8683
|
const dest = userSkillsDir();
|
|
8532
8684
|
mkdirSync18(dest, { recursive: true });
|
|
8533
8685
|
heading("Import skills");
|
|
8534
|
-
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")));
|
|
8535
8687
|
let n = 0;
|
|
8536
8688
|
for (const src of sources) {
|
|
8537
|
-
cpSync2(src,
|
|
8538
|
-
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))}
|
|
8539
8691
|
`);
|
|
8540
8692
|
n++;
|
|
8541
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 && tsx scripts/test-compact.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"
|