@ghostystudio/cli 0.16.1 → 0.17.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 +5 -0
- package/dist/index.js +123 -19
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -87,6 +87,11 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
|
87
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.
|
|
88
88
|
- `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
|
|
89
89
|
|
|
90
|
+
### Changes in 0.17.0
|
|
91
|
+
|
|
92
|
+
- Agents by **name** everywhere: `ghosty agents get pia-0`, `ghosty try "Ghosty Ventas" "hola"` (ids still work). Same-name agents: the one in the `ghosty use` workspace wins, otherwise the CLI lists their ids.
|
|
93
|
+
- `ghosty use <workspace>`: default workspace for `boards`, `agents create` and `agents ls` (`--all`, `--workspace none`, `use --clear`).
|
|
94
|
+
|
|
90
95
|
### Changes in 0.16.1
|
|
91
96
|
|
|
92
97
|
- `agents create --like`: `--board same|main|new|NAME` (`new` = its own board with the source's columns, one per number) and `--no-notify` (shared with the same people, no email).
|
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.17.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.17.0"}`,
|
|
334
334
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
335
335
|
...opts.headers
|
|
336
336
|
},
|
|
@@ -529,17 +529,21 @@ var need = (v, what, example) => {
|
|
|
529
529
|
if (!v) throw usage(`Missing ${what}.`, `Example: ${example}`);
|
|
530
530
|
return v;
|
|
531
531
|
};
|
|
532
|
-
async function agentsLs() {
|
|
532
|
+
async function agentsLs(workspace) {
|
|
533
533
|
const r = await api("/api/v2/me/agents?client=cli");
|
|
534
|
-
const
|
|
534
|
+
const list = workspace ? r.agentes.filter((a) => a.espacio?.nombre === workspace) : r.agentes;
|
|
535
|
+
const spaceOf = (a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "";
|
|
536
|
+
const rows = list.map((a) => ({
|
|
535
537
|
id: a.id,
|
|
536
538
|
name: a.nombre,
|
|
537
539
|
engine: a.motor ?? a.origen,
|
|
538
540
|
source: a.origen,
|
|
541
|
+
workspace: spaceOf(a),
|
|
539
542
|
lastActivity: a.ultimaActividad ?? null,
|
|
540
543
|
...a.compartidoPor ? { sharedBy: a.compartidoPor } : {}
|
|
541
544
|
}));
|
|
542
|
-
out(rows, () => table(rows, ["id", "name", "engine", "lastActivity"])
|
|
545
|
+
out(rows, () => `${table(rows, ["id", "name", "engine", "workspace", "lastActivity"])}${workspace ? `
|
|
546
|
+
${dim(`only ${workspace} (ghosty use) \xB7 all: --all`)}` : ""}`);
|
|
543
547
|
}
|
|
544
548
|
function parseEnv(raw) {
|
|
545
549
|
if (!raw) return void 0;
|
|
@@ -1269,7 +1273,7 @@ async function call(path, v, init = {}) {
|
|
|
1269
1273
|
"x-ghosty-ts": ts,
|
|
1270
1274
|
"x-ghosty-sig": sig,
|
|
1271
1275
|
"content-type": "application/json",
|
|
1272
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1276
|
+
"user-agent": `ghosty-cli/${"0.17.0"}`
|
|
1273
1277
|
},
|
|
1274
1278
|
body: method === "GET" ? void 0 : raw
|
|
1275
1279
|
});
|
|
@@ -2002,7 +2006,11 @@ Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a new mes
|
|
|
2002
2006
|
|
|
2003
2007
|
// src/commands/boards.ts
|
|
2004
2008
|
var enc8 = encodeURIComponent;
|
|
2005
|
-
async function boardsCmd(sub, a1, a2, v) {
|
|
2009
|
+
async function boardsCmd(sub, a1, a2, v, context) {
|
|
2010
|
+
if (context && sub !== "assign") {
|
|
2011
|
+
if ((sub === "ls" || sub === "list" || !sub || sub === "move-cards") && !a1) a1 = context;
|
|
2012
|
+
else if ((sub === "create" || sub === "rm") && a1 && !a2) [a1, a2] = [context, a1];
|
|
2013
|
+
}
|
|
2006
2014
|
switch (sub ?? "ls") {
|
|
2007
2015
|
case "ls":
|
|
2008
2016
|
case "list": {
|
|
@@ -2339,6 +2347,14 @@ HELPS.channels = {
|
|
|
2339
2347
|
],
|
|
2340
2348
|
learn: `${REF}#canales-de-whatsapp`
|
|
2341
2349
|
};
|
|
2350
|
+
HELPS.use = {
|
|
2351
|
+
summary: "A default workspace for this session (profile): boards take it without typing it, agents create is born there and agents ls lists only its agents.",
|
|
2352
|
+
usage: ["ghosty use # which one", "ghosty use <workspace>", "ghosty use --clear"],
|
|
2353
|
+
flags: [["--clear", "no default workspace"]],
|
|
2354
|
+
examples: ["ghosty use siiqtec", "ghosty boards ls", "ghosty agents create --name tania-mkt --like pia-0 --board new", "ghosty agents ls --all"],
|
|
2355
|
+
notes: ["Agents can be named instead of using their id anywhere; with several of the same name, the one in this workspace wins.", "agents create --workspace none makes a personal agent even with a default workspace."],
|
|
2356
|
+
learn: `${REF}#sesion`
|
|
2357
|
+
};
|
|
2342
2358
|
HELPS.boards = {
|
|
2343
2359
|
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.",
|
|
2344
2360
|
usage: [
|
|
@@ -2414,28 +2430,56 @@ function newer(a, b) {
|
|
|
2414
2430
|
}
|
|
2415
2431
|
async function latestVersion() {
|
|
2416
2432
|
if (process.env.GHOSTY_NO_UPDATE_CHECK || process.env.CI) return null;
|
|
2417
|
-
let
|
|
2433
|
+
let cache2 = {};
|
|
2418
2434
|
try {
|
|
2419
|
-
|
|
2435
|
+
cache2 = JSON.parse(await readFile9(FILE, "utf8"));
|
|
2420
2436
|
} catch {
|
|
2421
2437
|
}
|
|
2422
|
-
if (!
|
|
2438
|
+
if (!cache2.checkedAt || Date.now() - cache2.checkedAt > DAY) {
|
|
2423
2439
|
try {
|
|
2424
2440
|
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
2425
2441
|
const latest = (await r.json()).version;
|
|
2426
|
-
if (latest)
|
|
2442
|
+
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
2427
2443
|
await mkdir4(dirname3(FILE), { recursive: true });
|
|
2428
|
-
await writeFile4(FILE, JSON.stringify(
|
|
2444
|
+
await writeFile4(FILE, JSON.stringify(cache2));
|
|
2429
2445
|
} catch {
|
|
2430
2446
|
}
|
|
2431
2447
|
}
|
|
2432
|
-
return
|
|
2448
|
+
return cache2.latest ?? null;
|
|
2433
2449
|
}
|
|
2434
2450
|
async function updateNotice(current) {
|
|
2435
2451
|
const latest = await latestVersion();
|
|
2436
2452
|
return latest && newer(latest, current) ? `Update available: ${current} \u2192 ${latest}. Run: ${UPGRADE}` : null;
|
|
2437
2453
|
}
|
|
2438
2454
|
|
|
2455
|
+
// src/resolve.ts
|
|
2456
|
+
var looksLikeId = (s) => /^c[a-z0-9]{20,}$/.test(s) || /^[0-9a-f]{24}$/.test(s);
|
|
2457
|
+
function pickAgent(list, ref, workspace) {
|
|
2458
|
+
const want = ref.trim().toLowerCase();
|
|
2459
|
+
let hits = list.filter((a) => a.nombre.trim().toLowerCase() === want);
|
|
2460
|
+
if (hits.length > 1 && workspace) {
|
|
2461
|
+
const here = hits.filter((a) => a.espacio?.nombre === workspace);
|
|
2462
|
+
if (here.length) hits = here;
|
|
2463
|
+
}
|
|
2464
|
+
if (hits.length === 1) return hits[0].id;
|
|
2465
|
+
if (!hits.length) {
|
|
2466
|
+
const near = list.filter((a) => a.nombre.toLowerCase().includes(want)).slice(0, 5);
|
|
2467
|
+
throw new CliError(`No agent named "${ref}".`, EXIT.USAGE, near.length ? `Did you mean: ${near.map((a) => `${a.nombre} (${a.id})`).join(", ")}?` : "List them: ghosty agents ls");
|
|
2468
|
+
}
|
|
2469
|
+
const where = (a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "?";
|
|
2470
|
+
throw new CliError(`${hits.length} agents are named "${ref}".`, EXIT.USAGE, `Use the id:
|
|
2471
|
+
${hits.map((a) => ` ${a.id} ${a.motor ?? ""} ${where(a)}`).join("\n")}`);
|
|
2472
|
+
}
|
|
2473
|
+
var cache = null;
|
|
2474
|
+
function listAgents() {
|
|
2475
|
+
cache ??= api("/api/v2/me/agents?client=cli").then((r) => r.agentes);
|
|
2476
|
+
return cache;
|
|
2477
|
+
}
|
|
2478
|
+
async function resolveAgent(ref, workspace) {
|
|
2479
|
+
if (!ref || looksLikeId(ref)) return ref;
|
|
2480
|
+
return pickAgent(await listAgents(), ref, workspace);
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2439
2483
|
// src/index.ts
|
|
2440
2484
|
var HELP = `${bold("Usage:")} ghosty <command> [options]
|
|
2441
2485
|
|
|
@@ -2444,6 +2488,7 @@ ${bold("Session")}
|
|
|
2444
2488
|
--device Sign in from another device with a link + code (auto over SSH/CI)
|
|
2445
2489
|
logout Sign out and revoke this device's session
|
|
2446
2490
|
whoami Show who you are signed in as
|
|
2491
|
+
use [workspace] Default workspace for boards, agents create and agents ls [--clear]
|
|
2447
2492
|
usage Your plan and how much you've used this week and month
|
|
2448
2493
|
|
|
2449
2494
|
${bold("Agents")}
|
|
@@ -2513,6 +2558,7 @@ ${bold("Your account")}
|
|
|
2513
2558
|
docs [topic] Open the docs
|
|
2514
2559
|
|
|
2515
2560
|
${bold("Global options")}
|
|
2561
|
+
<agent> Everywhere: an agent's NAME (case-insensitive) or its id
|
|
2516
2562
|
--json Machine-readable output (stdout is JSON only)
|
|
2517
2563
|
--token <token> Use this token (or env GHOSTY_TOKEN). A gat_\u2026 agent token reaches only that agent.
|
|
2518
2564
|
--profile <name> Use a separate saved session (or env GHOSTY_PROFILE), e.g. one per account
|
|
@@ -2523,7 +2569,7 @@ ${bold("Global options")}
|
|
|
2523
2569
|
${bold("Exit codes")} 0 ok \xB7 1 API error \xB7 2 usage error \xB7 3 not signed in
|
|
2524
2570
|
${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.")}
|
|
2525
2571
|
${dim("Docs: https://www.ghosty.studio/docs/cli")}`;
|
|
2526
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
2572
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
2527
2573
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
2528
2574
|
function unknown(what, input, known, helpCmd) {
|
|
2529
2575
|
const s = suggest(input, known);
|
|
@@ -2533,6 +2579,45 @@ function showHelp() {
|
|
|
2533
2579
|
if (process.stdout.isTTY && !process.env.NO_COLOR && !globals.json) process.stdout.write(BANNER + "\n\n");
|
|
2534
2580
|
process.stdout.write(HELP + "\n");
|
|
2535
2581
|
}
|
|
2582
|
+
function agentArgIndex(pos) {
|
|
2583
|
+
const [cmd, sub] = pos;
|
|
2584
|
+
switch (cmd) {
|
|
2585
|
+
case "agents":
|
|
2586
|
+
if (sub === "docs" || sub === "documents") return 3;
|
|
2587
|
+
return sub && !["ls", "list", "create", "new"].includes(sub) ? 2 : null;
|
|
2588
|
+
case "files":
|
|
2589
|
+
case "skills":
|
|
2590
|
+
case "mcp":
|
|
2591
|
+
case "dbs":
|
|
2592
|
+
case "db":
|
|
2593
|
+
case "turns":
|
|
2594
|
+
case "board":
|
|
2595
|
+
case "conversations":
|
|
2596
|
+
case "schedule":
|
|
2597
|
+
return 2;
|
|
2598
|
+
case "try":
|
|
2599
|
+
case "chat":
|
|
2600
|
+
return 1;
|
|
2601
|
+
case "channels":
|
|
2602
|
+
return pos[2] === "groups" ? 4 : 3;
|
|
2603
|
+
case "boards":
|
|
2604
|
+
return sub === "assign" ? 2 : null;
|
|
2605
|
+
default:
|
|
2606
|
+
return null;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
async function useCmd(slug, v) {
|
|
2610
|
+
const cfg = await readConfig();
|
|
2611
|
+
if (v.clear) {
|
|
2612
|
+
delete cfg.workspace;
|
|
2613
|
+
await writeConfig(cfg);
|
|
2614
|
+
return out({ workspace: null }, () => `${lavender("\u2713")} No default workspace.`);
|
|
2615
|
+
}
|
|
2616
|
+
if (!slug) return out({ workspace: cfg.workspace ?? null }, () => cfg.workspace ? `Workspace: ${bold(cfg.workspace)}` : dim("No default workspace. Set one: ghosty use <workspace>"));
|
|
2617
|
+
await api(`/api/v2/workspaces/${encodeURIComponent(slug)}/boards`);
|
|
2618
|
+
await writeConfig({ ...cfg, workspace: slug });
|
|
2619
|
+
return out({ workspace: slug }, () => `${lavender("\u2713")} Using ${bold(slug)}: boards, agents create and agents ls default to it. ${dim("(ghosty use --clear to stop)")}`);
|
|
2620
|
+
}
|
|
2536
2621
|
async function main() {
|
|
2537
2622
|
const { values: v, positionals: pos } = parseArgs({
|
|
2538
2623
|
allowPositionals: true,
|
|
@@ -2614,6 +2699,7 @@ async function main() {
|
|
|
2614
2699
|
"columns-from": { type: "string" },
|
|
2615
2700
|
to: { type: "string" },
|
|
2616
2701
|
all: { type: "boolean" },
|
|
2702
|
+
clear: { type: "boolean" },
|
|
2617
2703
|
q: { type: "string" },
|
|
2618
2704
|
"prompt-out": { type: "string" },
|
|
2619
2705
|
toolsets: { type: "string" },
|
|
@@ -2654,7 +2740,16 @@ async function main() {
|
|
|
2654
2740
|
const tokens = splitTokens(pos, v);
|
|
2655
2741
|
globals.token = tokens.session;
|
|
2656
2742
|
globals.profile = v.profile;
|
|
2657
|
-
if (v.version) return out({ version: "0.
|
|
2743
|
+
if (v.version) return out({ version: "0.17.0" }, () => "0.17.0");
|
|
2744
|
+
const context = (await readConfig()).workspace;
|
|
2745
|
+
if (!explicitToken()?.startsWith("gat_")) {
|
|
2746
|
+
const at = agentArgIndex(pos);
|
|
2747
|
+
if (at !== null && pos[at]) pos[at] = await resolveAgent(pos[at], context);
|
|
2748
|
+
for (const f of ["like", "prompt-base"]) {
|
|
2749
|
+
const val = v[f];
|
|
2750
|
+
if (typeof val === "string" && val !== "none") v[f] = await resolveAgent(val, context);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2658
2753
|
const [cmd, sub, a1, a2] = pos;
|
|
2659
2754
|
if (cmd === "partner" && (v.help || !sub || sub === "help")) return out({ help: PARTNER_HELP }, () => PARTNER_HELP);
|
|
2660
2755
|
const topic = cmd === "help" ? sub : v.help ? cmd : void 0;
|
|
@@ -2680,9 +2775,18 @@ async function main() {
|
|
|
2680
2775
|
const me = await api("/api/v2/me");
|
|
2681
2776
|
return out(me, () => `${bold(me.email)}${dim(` \xB7 ${new URL(process.env.GHOSTY_URL || "https://www.ghosty.studio").host}`)}`);
|
|
2682
2777
|
}
|
|
2778
|
+
case "use":
|
|
2779
|
+
return useCmd(sub, v);
|
|
2683
2780
|
case "agents":
|
|
2684
|
-
if (sub === "ls" || sub === "list" || !sub) return agentsLs();
|
|
2685
|
-
if (sub === "create" || sub === "new")
|
|
2781
|
+
if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : context);
|
|
2782
|
+
if (sub === "create" || sub === "new") {
|
|
2783
|
+
if (v.workspace === "none") delete v.workspace;
|
|
2784
|
+
else if (!v.workspace && context) {
|
|
2785
|
+
v.workspace = context;
|
|
2786
|
+
if (!globals.json) info(dim(`workspace ${context} (from ghosty use; --workspace none for a personal agent)`));
|
|
2787
|
+
}
|
|
2788
|
+
return agentsCreate(v);
|
|
2789
|
+
}
|
|
2686
2790
|
if (sub === "get") return agentsGet(a1, v.fields, v["prompt-out"]);
|
|
2687
2791
|
if (sub === "share" || sub === "unshare" || sub === "shares") return agentsShare(sub, a1, a2, v);
|
|
2688
2792
|
if (sub === "set") return agentsSet(a1, v);
|
|
@@ -2707,7 +2811,7 @@ async function main() {
|
|
|
2707
2811
|
case "board":
|
|
2708
2812
|
return boardCmd(sub, a1, v, a2);
|
|
2709
2813
|
case "boards":
|
|
2710
|
-
return boardsCmd(sub, a1, a2, v);
|
|
2814
|
+
return boardsCmd(sub, a1, a2, v, context);
|
|
2711
2815
|
case "channels":
|
|
2712
2816
|
if (a1 === "groups") return groupsCmd(pos.slice(3).filter((x) => x !== void 0), v);
|
|
2713
2817
|
return channelsCmd(sub, a1, a2, pos.slice(4).join(" ") || void 0, v);
|
|
@@ -2742,7 +2846,7 @@ async function main() {
|
|
|
2742
2846
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
2743
2847
|
}
|
|
2744
2848
|
}
|
|
2745
|
-
var notice = updateNotice("0.
|
|
2849
|
+
var notice = updateNotice("0.17.0").catch(() => null);
|
|
2746
2850
|
main().then(
|
|
2747
2851
|
async () => {
|
|
2748
2852
|
const n = await notice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostystudio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --banner:js='#!/usr/bin/env node' --define:__VERSION__=\\\"$npm_package_version\\\"",
|
|
18
18
|
"prepack": "npm run build",
|
|
19
|
-
"test": "for t in flags prompt api rows wa-auth; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
19
|
+
"test": "for t in flags prompt api rows wa-auth resolve; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"ghosty",
|