@ghostystudio/cli 0.14.0 → 0.16.0
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/README.md +18 -1
- package/dist/index.js +225 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,11 +44,14 @@ ghosty channels whatsapp history <id> --integration ID [--days 7] [--dry-run]
|
|
|
44
44
|
ghosty channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no [--dry-run] # personal WhatsApp session, no QR
|
|
45
45
|
ghosty channels wa pair|status|pause <id> [--phone N]
|
|
46
46
|
ghosty channels wa groups ls|set|create <id> [group] [--role equipo|cliente] [--on|--off] [--dry-run] # personal WhatsApp groups
|
|
47
|
+
ghosty boards ls|create|assign|move-cards|rm <workspace|agent> … [--columns-from main] [--from A --to B --integration X --dry-run]
|
|
48
|
+
ghosty board ls <id> [--q TEXT] [--limit 20] # recent cards on its sales board
|
|
49
|
+
ghosty board show <id> <folio|phone|id> [--limit 20] # a card's thread, attachments and whether gs kept a copy
|
|
47
50
|
ghosty board archive <id> --integration X | --column X | --before 30d [--dry-run]
|
|
48
51
|
ghosty mcp get|set <id> [--file servers.json]
|
|
49
52
|
|
|
50
53
|
ghosty try <id> "message" [--session] [--reset]
|
|
51
|
-
ghosty try <id> "message" --waba --session PHONE [--media FILE] [--history FILE] # real WhatsApp path, nothing sent
|
|
54
|
+
ghosty try <id> "message" --waba --session PHONE [--media FILE [--as-formmy]] [--history FILE] # real WhatsApp path, nothing sent
|
|
52
55
|
ghosty try <id> --replay FILE --out DIR [--max 15] [--concurrency 8] [--resume]
|
|
53
56
|
ghosty chat <id> ["message"] [--conversation ID]
|
|
54
57
|
ghosty conversations ls <id>
|
|
@@ -84,6 +87,20 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
|
84
87
|
- `try --replay FILE --out DIR`: replays real conversations from your machine. The report is written after every step, survives Ghosty deploys (it honors `Retry-After`), and `--resume` continues a partial run.
|
|
85
88
|
- `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
|
|
86
89
|
|
|
90
|
+
### Changes in 0.16.0
|
|
91
|
+
|
|
92
|
+
- `agents create --like <agent> [--copy-dbs] [--dry-run]`: a new agent with another's setup (skills and their switches, toolsets, connectors, voice, databases, documents, board, shares), never its prompt, env or channels.
|
|
93
|
+
- `skills house <id> --only a,b | --enable | --disable`: Ghosty's skills in bulk.
|
|
94
|
+
- `agents docs grant <id> A B …` and `--like <agent>`.
|
|
95
|
+
- `agents get --fields toolsets,connectors`: effective toolsets and each connector (connected, granted, effective).
|
|
96
|
+
- `boards ls|create|assign|move-cards`: a workspace's boards, one per number if you like, with the columns of another and the cards moved over.
|
|
97
|
+
|
|
98
|
+
### Changes in 0.15.0
|
|
99
|
+
|
|
100
|
+
- `try --waba` lists everything the agent would send, tools included: 📍 location, 📄 files, 🎤 voice, ↩️ quotes, 🔘 buttons, 👤 contacts, reactions. Quotes and reactions now find the test message to point at.
|
|
101
|
+
- `try --waba --media FILE --as-formmy`: the attachment arrives like a real Formmy forward (proxy with credential, copy to gs, transcription) and the output says whether the copy worked.
|
|
102
|
+
- `board ls` (recent cards) and `board show <folio|phone|id>` (a card's thread: who wrote, text, attachment, copy in gs).
|
|
103
|
+
|
|
87
104
|
### Changes in 0.14.0
|
|
88
105
|
|
|
89
106
|
- Fix: `--json` output over a pipe no longer gets cut at 64 KB (an 80 KB prompt came out as invalid JSON). The CLI now waits for stdout to drain before exiting.
|
package/dist/index.js
CHANGED
|
@@ -288,7 +288,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
288
288
|
const req = httpsRequest(`${base}${path}`, {
|
|
289
289
|
method: "POST",
|
|
290
290
|
timeout: timeoutMs,
|
|
291
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
291
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.16.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
292
292
|
}, (res) => {
|
|
293
293
|
let buf = "";
|
|
294
294
|
res.on("data", (c) => buf += c);
|
|
@@ -330,7 +330,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
330
330
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
331
331
|
headers: {
|
|
332
332
|
authorization: `Bearer ${token}`,
|
|
333
|
-
"user-agent": `ghosty-cli/${"0.
|
|
333
|
+
"user-agent": `ghosty-cli/${"0.16.0"}`,
|
|
334
334
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
335
335
|
...opts.headers
|
|
336
336
|
},
|
|
@@ -428,13 +428,34 @@ async function uploadMedia(file) {
|
|
|
428
428
|
const mime = MIME[ext] ?? "application/octet-stream";
|
|
429
429
|
return { url: r.url, mime, filename: r.name, type: mime.startsWith("image/") ? "image" : mime.startsWith("audio/") ? "audio" : mime.startsWith("video/") ? "video" : "document" };
|
|
430
430
|
}
|
|
431
|
+
function richLine(s) {
|
|
432
|
+
const d = s.data ?? {};
|
|
433
|
+
const str = (k) => typeof d[k] === "string" ? d[k] : "";
|
|
434
|
+
switch (s.rich) {
|
|
435
|
+
case "ubicacion":
|
|
436
|
+
return `\u{1F4CD} ${str("nombre") || "location"} \xB7 ${Number(d.latitud).toFixed(4)},${Number(d.longitud).toFixed(4)}${str("direccion") ? dim(` ${str("direccion")}`) : ""}`;
|
|
437
|
+
case "boton":
|
|
438
|
+
return `\u{1F518} ${str("boton")} ${dim(str("url"))} ${str("texto")}`;
|
|
439
|
+
case "contacto":
|
|
440
|
+
return `\u{1F464} ${str("nombre")} ${str("telefono")}`;
|
|
441
|
+
case "encuesta":
|
|
442
|
+
return `\u{1F4CA} ${str("pregunta")}`;
|
|
443
|
+
case "reaccion":
|
|
444
|
+
return `${str("emoji")} ${dim(`reaction to ${str("mensajeId") || "the last message"}`)}`;
|
|
445
|
+
case "cita":
|
|
446
|
+
return `\u21A9\uFE0F ${str("texto")} ${dim(`quoting ${str("mensajeId") || "the last message"}`)}`;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
431
449
|
function sentText(sent) {
|
|
432
|
-
return sent.map(
|
|
450
|
+
return sent.map(
|
|
451
|
+
(s) => s.kind === "text" ? `${lavender("\u2192")} ${s.text}` : s.kind === "voice" ? `${lavender("\u{1F3A4}")} ${s.text}` : s.kind === "file" ? `${lavender(/^audio\//.test(s.mime ?? "") ? "\u{1F3A4}" : "\u{1F4C4}")} ${s.name} ${dim(s.url)}` : richLine(s)
|
|
452
|
+
).join("\n\n");
|
|
433
453
|
}
|
|
434
454
|
async function tryWaba(id, text, v) {
|
|
435
455
|
const phone = v.session ?? "";
|
|
436
456
|
if (!/^\d{10,15}$/.test(phone)) throw usage("--waba needs --session <phone> (10-15 digits, a made-up number).", 'Example: ghosty try <agent-id> "hola" --waba --session 5210000000009');
|
|
437
457
|
if ((v.media?.length ?? 0) > 1) throw usage("One --media per turn (WhatsApp sends one file per message).", "Send several turns, one file each.");
|
|
458
|
+
if (v["as-formmy"] && !v.media?.length) throw usage("--as-formmy goes with --media FILE.", 'Example: ghosty try <agent-id> "" --waba --session 5210000000009 --media nota.ogg --as-formmy');
|
|
438
459
|
if (!text && !v.media?.length && !v.reset) throw usage("Missing message.", 'Example: ghosty try <agent-id> "hola" --waba --session 5210000000009');
|
|
439
460
|
let history;
|
|
440
461
|
if (v.history) {
|
|
@@ -445,9 +466,11 @@ async function tryWaba(id, text, v) {
|
|
|
445
466
|
}
|
|
446
467
|
const media = v.media?.[0] ? await uploadMedia(v.media[0]) : void 0;
|
|
447
468
|
if (process.stderr.isTTY && !globals.json && (text || media)) info(dim("Thinking\u2026 (the real WhatsApp path; nothing is sent)"));
|
|
448
|
-
const r = await tryPost(id, { as: "waba", session: phone, reset: v.reset, text: text ?? "", ...media ? { media } : {}, ...history ? { history } : {} });
|
|
469
|
+
const r = await tryPost(id, { as: "waba", session: phone, reset: v.reset, text: text ?? "", ...media ? { media } : {}, ...history ? { history } : {}, ...v["as-formmy"] ? { asFormmy: true } : {} });
|
|
449
470
|
if (r.status !== 200) throw Object.assign(new Error(`${r.status} ${r.body.error ?? "turn failed"}`), { code: 1 });
|
|
450
|
-
|
|
471
|
+
const via = r.body.media ? `
|
|
472
|
+
${r.body.media.copied ? dim("attachment: downloaded through the Formmy-like proxy and copied to gs") : red("attachment: NOT copied to gs (the proxy download failed)")}` : "";
|
|
473
|
+
out(r.body, () => (r.body.sent ? sentText(r.body.sent) || dim("(the agent sent nothing)") : dim("Session reset.")) + via + (r.body.error ? `
|
|
451
474
|
${dim(`(warning: ${r.body.error})`)}` : ""));
|
|
452
475
|
}
|
|
453
476
|
async function tryReplay(id, v) {
|
|
@@ -550,11 +573,22 @@ async function agentsCreate(v) {
|
|
|
550
573
|
if (v.workspace) body.workspace = v.workspace;
|
|
551
574
|
const channels = parseChannels(v.channels);
|
|
552
575
|
if (channels) body.channels = channels;
|
|
576
|
+
if (v.like) Object.assign(body, { like: v.like, ...v["copy-dbs"] ? { copyDbs: true } : {}, ...v["dry-run"] ? { dryRun: true } : {} });
|
|
577
|
+
else if (v["dry-run"]) throw usage("--dry-run goes with --like (it lists what would be copied).");
|
|
578
|
+
if (v.like && v["dry-run"]) {
|
|
579
|
+
const p = await api("/api/v2/me/agents", { method: "POST", body });
|
|
580
|
+
return out(p, () => `${dim(`like ${v.like} \xB7 ${p.engine ?? "?"} \xB7 ${p.model ?? "default"}${p.workspace ? ` \xB7 workspace ${p.workspace}` : ""}`)}
|
|
581
|
+
${p.plan.join("\n")}
|
|
582
|
+
${dim("Not copied: prompt, env, channels. Dry run: nothing created.")}`);
|
|
583
|
+
}
|
|
553
584
|
const r = await api("/api/v2/me/agents", { method: "POST", body });
|
|
554
585
|
const where = r.workspace ? `
|
|
555
586
|
workspace: ${r.workspace}${r.handle ? ` \xB7 @${r.handle} in Teams` : dim(" \xB7 not active in Teams")}` : "";
|
|
587
|
+
const copied = r.copied?.length ? `
|
|
588
|
+
${r.copied.map((l) => ` ${l}`).join("\n")}
|
|
589
|
+
${dim(" Now give it its own prompt: ghosty agents set " + r.id + " --prompt-file PROMPT.md")}` : "";
|
|
556
590
|
out(r, () => `${lavender("\u2713")} Created ${r.name} ${dim(`(${r.engine} \xB7 ${r.model ?? "default"})`)}
|
|
557
|
-
id: ${r.id}${where}`);
|
|
591
|
+
id: ${r.id}${where}${copied}`);
|
|
558
592
|
}
|
|
559
593
|
async function agentsGet(id, fields, promptOut) {
|
|
560
594
|
id = need(id, "agent id", "ghosty agents get <agent-id>");
|
|
@@ -749,7 +783,7 @@ async function readSkillDir(dir) {
|
|
|
749
783
|
}
|
|
750
784
|
var trashDir = (id, slug) => join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "ghosty", "trash", id, `${slug}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
|
|
751
785
|
async function skillsCmd(sub, id, slug, v) {
|
|
752
|
-
const ex = "ghosty skills ls <agent-id> | on|off <agent-id> <slug> | get <agent-id> <slug> [--out DIR] | templates <agent-id> | add <agent-id> <slug> [--file SKILL.md | --dir FOLDER] | rm <agent-id> <slug>";
|
|
786
|
+
const ex = "ghosty skills ls <agent-id> | on|off <agent-id> <slug> | house <agent-id> [--only a,b] | get <agent-id> <slug> [--out DIR] | templates <agent-id> | add <agent-id> <slug> [--file SKILL.md | --dir FOLDER] | rm <agent-id> <slug>";
|
|
753
787
|
id = need(id, "agent id", ex);
|
|
754
788
|
switch (sub ?? "ls") {
|
|
755
789
|
case "ls":
|
|
@@ -765,6 +799,21 @@ async function skillsCmd(sub, id, slug, v) {
|
|
|
765
799
|
${dim(`${more} more in the community catalog: ghosty skills add <agent-id> <slug> (--json lists them) \xB7 overlaps: ghosty agents doctor ${id}`)}`);
|
|
766
800
|
return;
|
|
767
801
|
}
|
|
802
|
+
case "house": {
|
|
803
|
+
const list = (x) => x ? x.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
804
|
+
const only = list(v.only), on = list(v.enable), off = list(v.disable);
|
|
805
|
+
if (!only && !on && !off) {
|
|
806
|
+
const r2 = await api(agentPath(id, "/skills/house"));
|
|
807
|
+
out(r2, () => `${table(r2.house.map((h) => ({ slug: h.slug, on: h.enabled ? "on" : "off" })), ["slug", "on"])}
|
|
808
|
+
${dim(`Change: ghosty skills house ${id} --only a,b | --enable a | --disable b [--dry-run]`)}`);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const body = { ...only ? { only } : {}, ...on ? { on } : {}, ...off ? { off } : {}, ...v["dry-run"] ? { dryRun: true } : {} };
|
|
812
|
+
const r = await api(agentPath(id, "/skills/house"), { method: "PUT", body });
|
|
813
|
+
out(r, () => `${r.changes.map((c) => `${c.enabled ? "+" : "-"} ${c.slug}`).join("\n") || dim("Nothing to change.")}
|
|
814
|
+
${dim(`On now: ${r.on.join(", ") || "none"}${r.dryRun ? " \xB7 dry run: nothing changed" : ""}`)}`);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
768
817
|
case "on":
|
|
769
818
|
case "off": {
|
|
770
819
|
slug = need(slug, "skill slug", `ghosty skills ${sub} <agent-id> <slug>`);
|
|
@@ -1220,7 +1269,7 @@ async function call(path, v, init = {}) {
|
|
|
1220
1269
|
"x-ghosty-ts": ts,
|
|
1221
1270
|
"x-ghosty-sig": sig,
|
|
1222
1271
|
"content-type": "application/json",
|
|
1223
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1272
|
+
"user-agent": `ghosty-cli/${"0.16.0"}`
|
|
1224
1273
|
},
|
|
1225
1274
|
body: method === "GET" ? void 0 : raw
|
|
1226
1275
|
});
|
|
@@ -1500,7 +1549,8 @@ ${dim(`${rows.length} row(s) \xB7 ${Math.round(r.ms)} ms`)}`;
|
|
|
1500
1549
|
throw usage(`Unknown dbs subcommand: ${sub}.`, "Try: ghosty dbs ls | tables | query | import | grant | revoke (ghosty dbs --help)");
|
|
1501
1550
|
}
|
|
1502
1551
|
}
|
|
1503
|
-
async function agentDocsCmd(sub, id,
|
|
1552
|
+
async function agentDocsCmd(sub, id, docs, v) {
|
|
1553
|
+
let doc = docs[0];
|
|
1504
1554
|
id = need2(id, "agent id", "ghosty agents docs ls <agent-id>");
|
|
1505
1555
|
switch (sub ?? "ls") {
|
|
1506
1556
|
case "ls":
|
|
@@ -1510,13 +1560,23 @@ async function agentDocsCmd(sub, id, doc, v) {
|
|
|
1510
1560
|
return out(r.documents, () => rows.length ? table(rows, ["name", "access", "delivers", "id"]) : dim("This account has no documents yet: upload them at /app/datos."));
|
|
1511
1561
|
}
|
|
1512
1562
|
case "grant": {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1563
|
+
let wanted;
|
|
1564
|
+
if (v.like) {
|
|
1565
|
+
if (docs.length) throw usage("Use document names or --like, not both.");
|
|
1566
|
+
const other = await api(agentPath2(v.like, "/documents"));
|
|
1567
|
+
wanted = other.documents.filter((d) => d.granted).map((d) => ({ ref: d.id, body: { mode: d.mode ?? "read", deliverable: d.deliverable ?? true } }));
|
|
1568
|
+
if (!wanted.length) return out({ granted: [] }, () => dim(`${v.like} has no documents granted.`));
|
|
1569
|
+
} else {
|
|
1570
|
+
if (!docs.length) throw usage("Missing document (id or exact name).", 'Example: ghosty agents docs grant <agent-id> "Cat\xE1logo.pdf" "Precios.xlsx" (or --like <other-agent>)');
|
|
1571
|
+
const body = {};
|
|
1572
|
+
if (v.write) body.mode = "write";
|
|
1573
|
+
if (v["no-deliver"]) body.deliverable = false;
|
|
1574
|
+
else if (v.deliver) body.deliverable = true;
|
|
1575
|
+
wanted = docs.map((ref) => ({ ref, body }));
|
|
1576
|
+
}
|
|
1577
|
+
const done = [];
|
|
1578
|
+
for (const w of wanted) done.push(await api(agentPath2(id, `/documents/${enc6(w.ref)}`), { method: "PUT", body: w.body }));
|
|
1579
|
+
return out(done.length === 1 ? done[0] : { granted: done }, () => done.map((r) => `${lavender("\u2713")} ${r.name}: ${r.mode === "write" ? "reads and replaces it" : "reads it"}${r.deliverable ? ", and can send it" : ", never sends it"}.`).join("\n"));
|
|
1520
1580
|
}
|
|
1521
1581
|
case "add": {
|
|
1522
1582
|
if (!v.url === !v.file) throw usage("Use --url URL or --file PATH (one of them).", 'Example: ghosty agents docs add <agent-id> --url https://\u2026/lista.pdf --description "Precios 2026"');
|
|
@@ -1891,8 +1951,37 @@ Cards appear on the team's board (no "new lead" alerts).`, { yes: v.yes, retry:
|
|
|
1891
1951
|
}
|
|
1892
1952
|
|
|
1893
1953
|
// src/commands/board.ts
|
|
1894
|
-
|
|
1895
|
-
|
|
1954
|
+
var WHO = { user: "customer", assistant: "agent", operator: "operator" };
|
|
1955
|
+
async function boardLs(id, v) {
|
|
1956
|
+
const qs = new URLSearchParams({ ...v.q ? { q: v.q } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
1957
|
+
const r = await api(`/api/v2/agents/${encodeURIComponent(id)}/board/conversations?${qs}`);
|
|
1958
|
+
const rows = r.conversations.map((c) => ({ folio: c.folio ? `#${c.folio}` : "", name: c.name ?? "", phone: c.phone, column: c.column ?? "", msgs: c.messages, last: (c.lastActivity ?? "").slice(0, 16).replace("T", " ") }));
|
|
1959
|
+
out(r, () => rows.length ? `${table(rows, ["folio", "name", "phone", "column", "msgs", "last"])}
|
|
1960
|
+
${dim(`\xAB${r.board}\xBB \xB7 thread: ghosty board show ${id} <folio|phone>`)}` : dim(`No cards on \xAB${r.board}\xBB${v.q ? ` matching "${v.q}"` : ""}.`));
|
|
1961
|
+
}
|
|
1962
|
+
async function boardShow(id, ref, v) {
|
|
1963
|
+
if (!ref) throw usage("Missing conversation.", `Example: ghosty board show ${id} "#12" (folio, phone, session or id; list them with ghosty board ls ${id})`);
|
|
1964
|
+
const r = await api(`/api/v2/agents/${encodeURIComponent(id)}/board/conversations/${encodeURIComponent(ref)}${v.limit ? `?limit=${encodeURIComponent(v.limit)}` : ""}`);
|
|
1965
|
+
const c = r.conversation;
|
|
1966
|
+
const head = `${bold(c.name ?? c.phone)} ${dim(`${c.folio ? `#${c.folio} \xB7 ` : ""}+${c.phone} \xB7 ${c.column ?? "no column"} \xB7 ${c.session}`)}`;
|
|
1967
|
+
const line = (m) => {
|
|
1968
|
+
const at = m.createdAt.slice(5, 16).replace("T", " ");
|
|
1969
|
+
const media = m.mediaType || m.mediaFilename ? `
|
|
1970
|
+
${dim(`\u{1F4CE} ${m.mediaType ?? "file"} ${m.mediaFilename ?? ""} \xB7 ${m.mediaCopied ? "copy in gs" : "no copy"}`)}` : "";
|
|
1971
|
+
return `${dim(at)} ${lavender((WHO[m.role] ?? m.role).padEnd(8))} ${m.content.replace(/\n/g, "\n ")}${media}`;
|
|
1972
|
+
};
|
|
1973
|
+
out(r, () => [head, ...r.messages.map(line)].join("\n"));
|
|
1974
|
+
}
|
|
1975
|
+
async function boardCmd(sub, id, v, ref) {
|
|
1976
|
+
if (sub === "ls" || sub === "list") {
|
|
1977
|
+
if (!id) throw usage("Missing agent id.", "Example: ghosty board ls <agent-id> [--q text] [--limit 20]");
|
|
1978
|
+
return boardLs(id, v);
|
|
1979
|
+
}
|
|
1980
|
+
if (sub === "show") {
|
|
1981
|
+
if (!id) throw usage("Missing agent id.", 'Example: ghosty board show <agent-id> "#12"');
|
|
1982
|
+
return boardShow(id, ref, v);
|
|
1983
|
+
}
|
|
1984
|
+
if (sub !== "archive") throw usage(`Unknown board subcommand: ${sub ?? "(none)"}.`, "Try: ghosty board ls <agent-id> \xB7 ghosty board show <agent-id> <folio|phone> \xB7 ghosty board archive <agent-id> --integration prueba --dry-run");
|
|
1896
1985
|
if (!id) throw usage("Missing agent id.", "Example: ghosty board archive <agent-id> --integration prueba --dry-run");
|
|
1897
1986
|
const filter = { ...v.integration ? { integration: v.integration } : {}, ...v.column ? { column: v.column } : {}, ...v.before ? { before: v.before } : {} };
|
|
1898
1987
|
if (!Object.keys(filter).length) throw usage("Add at least one filter: --integration, --column or --before.", "A filter-less archive would empty the board.");
|
|
@@ -1911,6 +2000,57 @@ Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a new mes
|
|
|
1911
2000
|
out(r, () => `${lavender("\u2713")} Archived ${r.archived} card(s) on \xAB${r.board}\xBB.`);
|
|
1912
2001
|
}
|
|
1913
2002
|
|
|
2003
|
+
// src/commands/boards.ts
|
|
2004
|
+
var enc8 = encodeURIComponent;
|
|
2005
|
+
async function boardsCmd(sub, a1, a2, v) {
|
|
2006
|
+
switch (sub ?? "ls") {
|
|
2007
|
+
case "ls":
|
|
2008
|
+
case "list": {
|
|
2009
|
+
if (!a1) throw usage("Missing workspace.", "Example: ghosty boards ls <workspace-slug>");
|
|
2010
|
+
const r = await api(`/api/v2/workspaces/${enc8(a1)}/boards`);
|
|
2011
|
+
const rows = r.boards.map((b) => ({ name: b.main ? `${b.name} (main)` : b.name, cards: b.cards, agents: b.agents.map((x) => x.name ?? x.id).join(", "), columns: b.columns.length, id: b.id }));
|
|
2012
|
+
return out(r, () => table(rows, ["name", "cards", "agents", "columns", "id"]));
|
|
2013
|
+
}
|
|
2014
|
+
case "create": {
|
|
2015
|
+
if (!a1 || !a2) throw usage("Missing workspace or name.", 'Example: ghosty boards create <workspace> "Siiqtec - Ixmiquilpan" --columns-from main');
|
|
2016
|
+
const r = await api(`/api/v2/workspaces/${enc8(a1)}/boards`, { method: "POST", body: { name: a2, ...v["columns-from"] ? { columnsFrom: v["columns-from"] } : {} } });
|
|
2017
|
+
return out(r, () => `${lavender("\u2713")} Board \xAB${r.name}\xBB created${r.columnsFrom ? ` with the ${r.columns} column(s) of \xAB${r.columnsFrom}\xBB` : ""}. ${dim(`Send an agent's cards there: ghosty boards assign <agent-id> "${r.name}"`)}`);
|
|
2018
|
+
}
|
|
2019
|
+
case "rm": {
|
|
2020
|
+
if (!a1 || !a2) throw usage("Missing workspace or board.", 'Example: ghosty boards rm <workspace> "Prueba"');
|
|
2021
|
+
requireYesIfHeadless(v.yes, `ghosty boards rm ${a1} ${JSON.stringify(a2)}`);
|
|
2022
|
+
await confirm(`Delete the board \xAB${a2}\xBB of ${a1}? (only an empty one; its agents go back to the main board)`, { yes: v.yes, retry: `ghosty boards rm ${a1} ${JSON.stringify(a2)}` });
|
|
2023
|
+
const r = await api(`/api/v2/workspaces/${enc8(a1)}/boards`, { method: "DELETE", body: { board: a2 } });
|
|
2024
|
+
return out(r, () => `${lavender("\u2713")} Board \xAB${r.deleted}\xBB deleted.`);
|
|
2025
|
+
}
|
|
2026
|
+
case "assign": {
|
|
2027
|
+
if (!a1 || !a2) throw usage("Missing agent or board.", 'Example: ghosty boards assign <agent-id> "Siiqtec - Ixmiquilpan" (or main)');
|
|
2028
|
+
const r = await api(`/api/v2/agents/${enc8(a1)}`, { method: "PATCH", body: { board: a2 } });
|
|
2029
|
+
return out(r, () => `${lavender("\u2713")} New cards of ${a1} go to \xAB${r.board}\xBB. ${dim(`Existing ones stay where they are: ghosty boards move-cards ${r.workspace} --from \u2026 --to "${r.board}" \u2026`)}`);
|
|
2030
|
+
}
|
|
2031
|
+
case "move-cards": {
|
|
2032
|
+
if (!a1 || !v.from || !v.to) throw usage("Missing workspace, --from or --to.", 'Example: ghosty boards move-cards <workspace> --from main --to "Siiqtec - Ixmiquilpan" --integration 6a3b02ad\u2026 --dry-run');
|
|
2033
|
+
if (!v.integration && !v.phone && !v.all) throw usage("Add a filter: --integration ID, --phone N or --all.", "A filter-less move would empty the board.");
|
|
2034
|
+
const path = `/api/v2/workspaces/${enc8(a1)}/boards/move-cards`;
|
|
2035
|
+
const body = { from: v.from, to: v.to, ...v.integration ? { integration: v.integration } : {}, ...v.phone ? { phone: v.phone } : {}, ...v.all ? { all: true } : {} };
|
|
2036
|
+
const retry = `ghosty boards move-cards ${a1} --from ${JSON.stringify(v.from)} --to ${JSON.stringify(v.to)}${v.integration ? ` --integration ${v.integration}` : ""}${v.phone ? ` --phone ${v.phone}` : ""}${v.all ? " --all" : ""}`;
|
|
2037
|
+
if (!v["dry-run"]) requireYesIfHeadless(v.yes, retry);
|
|
2038
|
+
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
2039
|
+
const sample = table(plan.sample.map((c) => ({ folio: c.folio ? `#${c.folio}` : "", name: c.name ?? "", phone: c.phone })), ["folio", "name", "phone"]);
|
|
2040
|
+
const text = `${sample}
|
|
2041
|
+
${plan.moved} of ${plan.matched} card(s) \xAB${plan.from}\xBB \u2192 \xAB${plan.to}\xBB${plan.skipped.length ? dim(` \xB7 ${plan.skipped.length} already in \xAB${plan.to}\xBB, skipped`) : ""}`;
|
|
2042
|
+
if (v["dry-run"] || !plan.moved) return out(plan, () => `${text}
|
|
2043
|
+
${dim("Dry run: nothing changed.")}`);
|
|
2044
|
+
await confirm(`${text}
|
|
2045
|
+
Move them with their threads and orders? (they get a new folio there)`, { yes: v.yes, retry });
|
|
2046
|
+
const r = await api(path, { method: "POST", body });
|
|
2047
|
+
return out(r, () => `${lavender("\u2713")} Moved ${r.moved} card(s) (${r.messages} message(s), ${r.orders} order(s)) to \xAB${r.to}\xBB.${r.skipped.length ? dim(` Skipped ${r.skipped.length}.`) : ""}`);
|
|
2048
|
+
}
|
|
2049
|
+
default:
|
|
2050
|
+
throw usage(`Unknown boards subcommand: ${sub}.`, "Try: ghosty boards ls | create | assign | move-cards | rm");
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
|
|
1914
2054
|
// src/help.ts
|
|
1915
2055
|
var DOCS = "https://www.ghosty.studio/docs/cli";
|
|
1916
2056
|
var REF = `${DOCS}/comandos`;
|
|
@@ -1919,12 +2059,12 @@ var HELPS = {
|
|
|
1919
2059
|
summary: "Create, inspect, change, check and delete agents.",
|
|
1920
2060
|
usage: [
|
|
1921
2061
|
"ghosty agents ls",
|
|
1922
|
-
"ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,\u2026] [--workspace SLUG] [--channels teams=off,\u2026]",
|
|
2062
|
+
"ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,\u2026] [--workspace SLUG] [--channels teams=off,\u2026] [--like AGENT [--copy-dbs] [--dry-run]]",
|
|
1923
2063
|
"ghosty agents get <agent-id> [--fields name,model,prompt] [--prompt-out FILE]",
|
|
1924
2064
|
"ghosty agents set <agent-id> [--name] [--engine] [--model] [--prompt | --prompt-file] [--env K=V,\u2026] [--connect ID,\u2026] [--disconnect ID,\u2026] [--voice E:ID] [--voice-replies auto|off] [--auto-stage on|off] [--toolsets crm,sales | +crm,-sales]",
|
|
1925
2065
|
"ghosty agents set <agent-id> --prompt-base <base-agent-id> --prompt-overlay overlay.json [--dry-run [--out FILE]] | --prompt-base none",
|
|
1926
2066
|
"ghosty agents doctor <agent-id> [--verbose]",
|
|
1927
|
-
"ghosty agents docs ls|grant|revoke <agent-id> [doc] [--write] [--no-deliver]",
|
|
2067
|
+
"ghosty agents docs ls|grant|revoke <agent-id> [doc\u2026] [--write] [--no-deliver] [--like AGENT]",
|
|
1928
2068
|
"ghosty agents docs add <agent-id> --url URL | --file PATH [--name N] [--description TEXT] [--grant read|write|no] [--no-deliver]",
|
|
1929
2069
|
"ghosty agents share <agent-id> <email> [--role editor|admin]",
|
|
1930
2070
|
"ghosty agents unshare <agent-id> <email> [--yes]",
|
|
@@ -1937,6 +2077,8 @@ var HELPS = {
|
|
|
1937
2077
|
flags: [
|
|
1938
2078
|
["--fields a,b", "get: return only these fields"],
|
|
1939
2079
|
["--prompt-out FILE", "get: write the prompt to FILE (edit it and come back with set --prompt-file)"],
|
|
2080
|
+
["--like AGENT", "create: copy that agent's setup (skills and their switches, toolsets, connectors, voice, databases, documents, board, shares) \u2014 not its prompt, env or channels. docs grant: the same documents"],
|
|
2081
|
+
["--copy-dbs", "create --like: duplicate its databases with their rows instead of granting the same ones"],
|
|
1940
2082
|
["--workspace SLUG", "create: born in that workspace (you own or administer it), owned by its owner and active in its Teams"],
|
|
1941
2083
|
["--channels k=on|off,\u2026", "create/set: turn channels on/off: teams, web, whatsapp, messenger, wa (personal WhatsApp)"],
|
|
1942
2084
|
["--toolsets LIST", "set: gs tool groups it gets (crm, sales\u2026): a list replaces; +x / -x add or remove"],
|
|
@@ -1977,6 +2119,9 @@ var HELPS = {
|
|
|
1977
2119
|
"ghosty agents set <id> --toolsets +crm",
|
|
1978
2120
|
"ghosty agents share <id> ana@empresa.com --role admin",
|
|
1979
2121
|
"ghosty agents get <id> --prompt-out PROMPT.md",
|
|
2122
|
+
"ghosty agents get <id> --fields toolsets,connectors # what it really has: effective toolsets, connectors connected + granted",
|
|
2123
|
+
"ghosty agents create --name tania-mkt --like <pia-id> --dry-run",
|
|
2124
|
+
'ghosty agents docs grant <id> "Cat\xE1logo.pdf" "Precios.xlsx" \xB7 ghosty agents docs grant <id> --like <other-id>',
|
|
1980
2125
|
"ghosty agents set <pia-id> --prompt-base <tania-id> --prompt-overlay capa.json --dry-run --out /tmp/pia.md",
|
|
1981
2126
|
'ghosty agents demo <id> --slug guardias --on --vence "2026-10-15T20:00" --starter "Ens\xE9\xF1ame c\xF3mo empezar"',
|
|
1982
2127
|
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates"
|
|
@@ -1989,6 +2134,7 @@ var HELPS = {
|
|
|
1989
2134
|
usage: [
|
|
1990
2135
|
"ghosty skills ls <agent-id>",
|
|
1991
2136
|
"ghosty skills on|off <agent-id> <slug>",
|
|
2137
|
+
"ghosty skills house <agent-id> [--only a,b | --enable a,b | --disable a,b] [--dry-run]",
|
|
1992
2138
|
"ghosty skills get <agent-id> <slug> [--out DIR]",
|
|
1993
2139
|
"ghosty skills templates <agent-id>",
|
|
1994
2140
|
"ghosty skills add <agent-id> <slug> [--file SKILL.md | --dir FOLDER]",
|
|
@@ -1996,6 +2142,8 @@ var HELPS = {
|
|
|
1996
2142
|
],
|
|
1997
2143
|
flags: [
|
|
1998
2144
|
["--out DIR", "get: folder to write into; without it, get only lists the files"],
|
|
2145
|
+
["--only a,b", "house: leave ONLY these of Ghosty's skills on (the rest off)"],
|
|
2146
|
+
["--enable / --disable a,b", "house: turn these of Ghosty's skills on / off"],
|
|
1999
2147
|
["--file FILE", "add: upload one SKILL.md"],
|
|
2000
2148
|
["--dir FOLDER", "add: upload a whole skill folder (SKILL.md + assets); restores a backup"],
|
|
2001
2149
|
["--yes", "rm: skip the confirmation (required without a terminal)"]
|
|
@@ -2073,13 +2221,14 @@ var HELPS = {
|
|
|
2073
2221
|
summary: "Test turns: plain, through the real WhatsApp path (nothing is sent), or a replay of real conversations.",
|
|
2074
2222
|
usage: [
|
|
2075
2223
|
'ghosty try <agent-id> "message" [--session ID] [--reset]',
|
|
2076
|
-
'ghosty try <agent-id> "message" --waba --session PHONE [--media FILE] [--history FILE] [--reset]',
|
|
2224
|
+
'ghosty try <agent-id> "message" --waba --session PHONE [--media FILE [--as-formmy]] [--history FILE] [--reset]',
|
|
2077
2225
|
"ghosty try <agent-id> --replay FILE --out DIR [--max 15] [--concurrency 8] [--resume]"
|
|
2078
2226
|
],
|
|
2079
2227
|
flags: [
|
|
2080
2228
|
["--waba", "the real WhatsApp Business path: inherited context, price rule, board, voice. Shows what it WOULD send"],
|
|
2081
2229
|
["--session PHONE", "with --waba: a made-up customer phone (10-15 digits); each phone is its own conversation"],
|
|
2082
2230
|
["--media FILE", "with --waba: the customer sends this photo/audio/PDF (one per turn)"],
|
|
2231
|
+
["--as-formmy", "with --media: it arrives like a real Formmy forward (proxy with credential, copy to gs, transcription) and says if the copy worked"],
|
|
2083
2232
|
["--history FILE", "with --waba: what was said before, as [{role: user|agent, text, at?}]; counts on the first turn (--reset)"],
|
|
2084
2233
|
["--reset", "forget that test session (with --waba also its test thread)"],
|
|
2085
2234
|
["--replay FILE", "run the conversations in FILE ({sample: [{id, fakePhone, messages}]}) against the agent"],
|
|
@@ -2096,7 +2245,8 @@ var HELPS = {
|
|
|
2096
2245
|
],
|
|
2097
2246
|
notes: [
|
|
2098
2247
|
"Replays survive Ghosty deploys: a 503 is retried after its Retry-After; the report lives on your disk.",
|
|
2099
|
-
"--whatsapp is now an alias of --waba."
|
|
2248
|
+
"--whatsapp is now an alias of --waba.",
|
|
2249
|
+
"--waba lists everything it would send, tools included: \u{1F4CD} location, \u{1F4C4} files, \u{1F3A4} voice, \u21A9\uFE0F quotes, \u{1F518} buttons, \u{1F464} contacts, reactions."
|
|
2100
2250
|
],
|
|
2101
2251
|
learn: `${REF}#turnos-de-prueba`
|
|
2102
2252
|
},
|
|
@@ -2187,18 +2337,44 @@ HELPS.channels = {
|
|
|
2187
2337
|
],
|
|
2188
2338
|
learn: `${REF}#canales-de-whatsapp`
|
|
2189
2339
|
};
|
|
2340
|
+
HELPS.boards = {
|
|
2341
|
+
summary: "A WORKSPACE's sales boards: list them, create one (copying another's columns), choose where an agent sends its cards, and move cards between boards.",
|
|
2342
|
+
usage: [
|
|
2343
|
+
"ghosty boards ls <workspace>",
|
|
2344
|
+
'ghosty boards create <workspace> "Name" [--columns-from main|<board>]',
|
|
2345
|
+
"ghosty boards assign <agent-id> <board|main>",
|
|
2346
|
+
"ghosty boards move-cards <workspace> --from <board> --to <board> [--integration ID | --phone N | --all] [--dry-run] [--yes]",
|
|
2347
|
+
'ghosty boards rm <workspace> "Name" [--yes]'
|
|
2348
|
+
],
|
|
2349
|
+
flags: [
|
|
2350
|
+
["--columns-from B", "create: copy the columns of that board (main, a name or an id) instead of the default ones"],
|
|
2351
|
+
["--from / --to B", "move-cards: boards by name, id or main"],
|
|
2352
|
+
["--integration ID | --phone N | --all", "move-cards: which cards (a filter is required)"],
|
|
2353
|
+
["-n, --dry-run", "move-cards: count and show a sample, change nothing"],
|
|
2354
|
+
["--yes", "move-cards: skip the confirmation (required without a terminal)"]
|
|
2355
|
+
],
|
|
2356
|
+
examples: ["ghosty boards ls siiqtec", 'ghosty boards create siiqtec "Siiqtec - Ixmiquilpan" --columns-from main', 'ghosty boards assign <agent-id> "Siiqtec - Ixmiquilpan"', 'ghosty boards move-cards siiqtec --from main --to "Siiqtec - Ixmiquilpan" --integration 6a3b02ad\u2026 --dry-run'],
|
|
2357
|
+
notes: ["assign changes where NEW cards go; move-cards brings the existing ones (thread, orders, activity) with a new folio. A card whose conversation is already on the target board is skipped.", "Reading needs membership; changing needs owner or staff of the workspace.", "rm only deletes an empty board that is not the main one."],
|
|
2358
|
+
learn: `${REF}#tablero`
|
|
2359
|
+
};
|
|
2190
2360
|
HELPS.board = {
|
|
2191
2361
|
summary: "The agent's sales board. It belongs to the WORKSPACE: you must be a member, a shared agent is not enough.",
|
|
2192
|
-
usage: [
|
|
2362
|
+
usage: [
|
|
2363
|
+
"ghosty board ls <agent-id> [--q TEXT] [--limit 20]",
|
|
2364
|
+
"ghosty board show <agent-id> <folio|phone|session|id> [--limit 20]",
|
|
2365
|
+
"ghosty board archive <agent-id> [--integration ID] [--column NAME] [--before 30d|ISO] [--dry-run] [--yes]"
|
|
2366
|
+
],
|
|
2193
2367
|
flags: [
|
|
2368
|
+
["--q TEXT", "ls: cards whose name, phone or last message contains TEXT"],
|
|
2369
|
+
["--limit N", "ls: cards (default 20) \xB7 show: last N messages (default 20, max 500)"],
|
|
2194
2370
|
["--integration ID", "cards from this channel/integration (e.g. prueba)"],
|
|
2195
2371
|
["--column NAME", "cards in this column"],
|
|
2196
2372
|
["--before T", "cards with no activity since T (30d, 12h or an ISO date)"],
|
|
2197
2373
|
["-n, --dry-run", "count and show a sample, change nothing"],
|
|
2198
2374
|
["--yes", "skip the confirmation (required without a terminal)"]
|
|
2199
2375
|
],
|
|
2200
|
-
examples: ["ghosty board archive <id> --integration prueba --dry-run", "ghosty board archive <id> --column Perdido --before 30d --yes"],
|
|
2201
|
-
notes: ["Archiving is reversible: when the customer writes again the card comes back. At least one filter is required."],
|
|
2376
|
+
examples: ["ghosty board ls <id> --q 7712", 'ghosty board show <id> "#12"', "ghosty board show <id> 5210000009922 --limit 50", "ghosty board archive <id> --integration prueba --dry-run", "ghosty board archive <id> --column Perdido --before 30d --yes"],
|
|
2377
|
+
notes: ["show prints each message with who wrote it (customer, agent, operator), its attachment and whether gs keeps a copy of it.", "Archiving is reversible: when the customer writes again the card comes back. At least one filter is required."],
|
|
2202
2378
|
learn: `${REF}#tablero`
|
|
2203
2379
|
};
|
|
2204
2380
|
HELPS.conversations = HELPS.chat;
|
|
@@ -2272,6 +2448,7 @@ ${bold("Agents")}
|
|
|
2272
2448
|
agents ls List your agents
|
|
2273
2449
|
agents create Create an agent --name [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,\u2026]
|
|
2274
2450
|
[--workspace SLUG] [--channels teams=off,\u2026] born in a workspace, active in its Teams
|
|
2451
|
+
[--like AGENT [--copy-dbs] [--dry-run]] with another agent's setup (not its prompt, env or channels)
|
|
2275
2452
|
agents get <id> Show an agent's config [--fields name,model,prompt] [--prompt-out FILE]
|
|
2276
2453
|
agents set <id> Change identity or engine [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,\u2026]
|
|
2277
2454
|
[--connect google-drive,\u2026] [--disconnect \u2026] give/remove access to your connectors
|
|
@@ -2281,6 +2458,7 @@ ${bold("Agents")}
|
|
|
2281
2458
|
[--prompt-base AGENT|none --prompt-overlay FILE [--dry-run]] prompt = another agent's + edits, kept in sync
|
|
2282
2459
|
agents doctor <id> Check the config: overlapping skills, tools the prompt names, broken grants, failing turns
|
|
2283
2460
|
agents docs ls|grant|revoke <id> [doc] Documents it reads and may send [--write] [--no-deliver]
|
|
2461
|
+
agents docs grant <id> A B\u2026 Grant several documents at once [--like AGENT] copies another agent's
|
|
2284
2462
|
agents docs add <id> Upload a document and grant it --url URL | --file PATH [--description] [--grant read|write|no]
|
|
2285
2463
|
agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
|
|
2286
2464
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes)
|
|
@@ -2292,6 +2470,7 @@ ${bold("Agents")}
|
|
|
2292
2470
|
${bold("Configure an agent")}
|
|
2293
2471
|
files ls|get|put|rm <id> [path] Files in the agent's machine [--file LOCAL] [--out LOCAL]
|
|
2294
2472
|
skills on|off <id> <slug> Turn one of its skills (or one of Ghosty's) on or off
|
|
2473
|
+
skills house <id> Ghosty's skills in bulk [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
2295
2474
|
skills ls|get|add|rm <id> [slug] Skills (add from catalog, --file SKILL.md or --dir FOLDER; get lists, --out DIR saves; rm keeps a copy)
|
|
2296
2475
|
skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
|
|
2297
2476
|
dbs ls|tables|query|import|grant|revoke <id> [db] Databases it uses [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run]
|
|
@@ -2303,11 +2482,14 @@ ${bold("Configure an agent")}
|
|
|
2303
2482
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
2304
2483
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
2305
2484
|
channels wa groups ls|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--on|--off] [--dry-run]
|
|
2485
|
+
boards ls|create|assign|move-cards|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards
|
|
2486
|
+
board ls <id> Recent cards on its sales board [--q TEXT] [--limit 20]
|
|
2487
|
+
board show <id> <card> A card's thread: who wrote, text, attachments and whether gs kept a copy (folio, phone or id)
|
|
2306
2488
|
board archive <id> Archive cards in bulk (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
2307
2489
|
|
|
2308
2490
|
${bold("Talk to an agent")}
|
|
2309
2491
|
try <id> "message" One test turn, prints the full answer [--session] [--reset]
|
|
2310
|
-
--waba --session PHONE \u2026through the real WhatsApp path, nothing sent [--media FILE] [--history FILE]
|
|
2492
|
+
--waba --session PHONE \u2026through the real WhatsApp path, nothing sent; lists what it would send (tools too) [--media FILE [--as-formmy]] [--history FILE]
|
|
2311
2493
|
--replay FILE --out DIR Replay real conversations; report written locally [--max 15] [--concurrency 8] [--resume]
|
|
2312
2494
|
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID]
|
|
2313
2495
|
conversations ls <id> List conversations
|
|
@@ -2339,7 +2521,7 @@ ${bold("Global options")}
|
|
|
2339
2521
|
${bold("Exit codes")} 0 ok \xB7 1 API error \xB7 2 usage error \xB7 3 not signed in
|
|
2340
2522
|
${dim("Note: `ghosty` may collide with another binary on your PATH (e.g. the Ghosty TUI). Check with `which -a ghosty`; run `npx @ghostystudio/cli` to be sure.")}
|
|
2341
2523
|
${dim("Docs: https://www.ghosty.studio/docs/cli")}`;
|
|
2342
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "credentials", "docs", "help"];
|
|
2524
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
2343
2525
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
2344
2526
|
function unknown(what, input, known, helpCmd) {
|
|
2345
2527
|
const s = suggest(input, known);
|
|
@@ -2419,6 +2601,16 @@ async function main() {
|
|
|
2419
2601
|
limit: { type: "string" },
|
|
2420
2602
|
verbose: { type: "boolean" },
|
|
2421
2603
|
full: { type: "boolean" },
|
|
2604
|
+
"as-formmy": { type: "boolean" },
|
|
2605
|
+
like: { type: "string" },
|
|
2606
|
+
"copy-dbs": { type: "boolean" },
|
|
2607
|
+
only: { type: "string" },
|
|
2608
|
+
enable: { type: "string" },
|
|
2609
|
+
disable: { type: "string" },
|
|
2610
|
+
"columns-from": { type: "string" },
|
|
2611
|
+
to: { type: "string" },
|
|
2612
|
+
all: { type: "boolean" },
|
|
2613
|
+
q: { type: "string" },
|
|
2422
2614
|
"prompt-out": { type: "string" },
|
|
2423
2615
|
toolsets: { type: "string" },
|
|
2424
2616
|
"prompt-base": { type: "string" },
|
|
@@ -2458,7 +2650,7 @@ async function main() {
|
|
|
2458
2650
|
const tokens = splitTokens(pos, v);
|
|
2459
2651
|
globals.token = tokens.session;
|
|
2460
2652
|
globals.profile = v.profile;
|
|
2461
|
-
if (v.version) return out({ version: "0.
|
|
2653
|
+
if (v.version) return out({ version: "0.16.0" }, () => "0.16.0");
|
|
2462
2654
|
const [cmd, sub, a1, a2] = pos;
|
|
2463
2655
|
if (cmd === "partner" && (v.help || !sub || sub === "help")) return out({ help: PARTNER_HELP }, () => PARTNER_HELP);
|
|
2464
2656
|
const topic = cmd === "help" ? sub : v.help ? cmd : void 0;
|
|
@@ -2492,7 +2684,7 @@ async function main() {
|
|
|
2492
2684
|
if (sub === "set") return agentsSet(a1, v);
|
|
2493
2685
|
if (sub === "rm" || sub === "delete") return agentsRm(a1, v);
|
|
2494
2686
|
if (sub === "doctor") return agentsDoctor(a1, v);
|
|
2495
|
-
if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos
|
|
2687
|
+
if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
|
|
2496
2688
|
if (sub === "restart") return agentsRestart(a1);
|
|
2497
2689
|
if (sub === "demo") return agentsDemo(a1, v);
|
|
2498
2690
|
if (sub === "box") return agentsBox(a1, v);
|
|
@@ -2509,7 +2701,9 @@ async function main() {
|
|
|
2509
2701
|
case "turns":
|
|
2510
2702
|
return turnsCmd(sub, a1, a2, v);
|
|
2511
2703
|
case "board":
|
|
2512
|
-
return boardCmd(sub, a1, v);
|
|
2704
|
+
return boardCmd(sub, a1, v, a2);
|
|
2705
|
+
case "boards":
|
|
2706
|
+
return boardsCmd(sub, a1, a2, v);
|
|
2513
2707
|
case "channels":
|
|
2514
2708
|
if (a1 === "groups") return groupsCmd(pos.slice(3).filter((x) => x !== void 0), v);
|
|
2515
2709
|
return channelsCmd(sub, a1, a2, pos.slice(4).join(" ") || void 0, v);
|
|
@@ -2544,7 +2738,7 @@ async function main() {
|
|
|
2544
2738
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
2545
2739
|
}
|
|
2546
2740
|
}
|
|
2547
|
-
var notice = updateNotice("0.
|
|
2741
|
+
var notice = updateNotice("0.16.0").catch(() => null);
|
|
2548
2742
|
main().then(
|
|
2549
2743
|
async () => {
|
|
2550
2744
|
const n = await notice;
|
package/package.json
CHANGED