@ghostystudio/cli 0.94.0 → 0.95.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 +4 -0
- package/dist/index.js +18 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,6 +155,10 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
155
155
|
|
|
156
156
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
157
157
|
|
|
158
|
+
### Changes in 0.95.0
|
|
159
|
+
|
|
160
|
+
- `factory runs resume|stop|cancel <run>`: what the card offers for a run that stopped moving — its role picks up where it left off, stop the running turn, or cancel the run. `resume` refuses while a role is working (it suggests `stop`). Ghosty staff can use them in workspaces they're not a member of; approving and merging stay with the client.
|
|
161
|
+
|
|
158
162
|
### Changes in 0.94.0
|
|
159
163
|
|
|
160
164
|
- `brief`: your morning brief is now the only morning notice (the full «morning briefing» turn is gone). It arrives at your time with a voice note (em_santa) on top of the lines. `ghosty brief [on|off] [--voice on|off] [--at HH:MM] [--days 1-5|all|0,6]` sets it; `brief show` prints voice, time and days; `brief run --dry-run` also makes the voice note and prints its link so you can hear it.
|
package/dist/index.js
CHANGED
|
@@ -1175,7 +1175,7 @@ async function postLong(path2, body, timeoutMs = 9e5) {
|
|
|
1175
1175
|
const req = httpsRequest(`${base}${path2}`, {
|
|
1176
1176
|
method: "POST",
|
|
1177
1177
|
timeout: timeoutMs,
|
|
1178
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
1178
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.95.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
1179
1179
|
}, (res) => {
|
|
1180
1180
|
let buf = "";
|
|
1181
1181
|
res.on("data", (c) => buf += c);
|
|
@@ -1217,7 +1217,7 @@ async function request(path2, opts = {}, retried = false) {
|
|
|
1217
1217
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
1218
1218
|
headers: {
|
|
1219
1219
|
authorization: `Bearer ${token}`,
|
|
1220
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1220
|
+
"user-agent": `ghosty-cli/${"0.95.0"}`,
|
|
1221
1221
|
"accept-language": lang(),
|
|
1222
1222
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
1223
1223
|
...opts.headers
|
|
@@ -11278,7 +11278,7 @@ async function call(path2, v, init = {}) {
|
|
|
11278
11278
|
"x-ghosty-ts": ts,
|
|
11279
11279
|
"x-ghosty-sig": sig,
|
|
11280
11280
|
"content-type": "application/json",
|
|
11281
|
-
"user-agent": `ghosty-cli/${"0.
|
|
11281
|
+
"user-agent": `ghosty-cli/${"0.95.0"}`
|
|
11282
11282
|
},
|
|
11283
11283
|
body: method === "GET" ? void 0 : raw
|
|
11284
11284
|
});
|
|
@@ -14127,6 +14127,7 @@ var HELPS_ES = {
|
|
|
14127
14127
|
"rooms connect revisa que TU instalaci\xF3n de la GitHub App vea el repo (si no, te da la liga para darle acceso) y dice qu\xE9 tan listo est\xE1 para agentes (niveles 1\u20133 y lo que falta). Cualquier miembro puede conectar; desconectar, quien lo conect\xF3 o el due\xF1o. Queda en bit\xE1cora (factory.room_connect / factory.room_disconnect).",
|
|
14128
14128
|
"run publica \xAB@plan <tu texto>\xBB en el room A TU NOMBRE, como si lo escribieras en Teams: el mismo plan, firmas y tarjeta. Escr\xEDbelo como ticket: el objetivo, por qu\xE9 y c\xF3mo se sabe que qued\xF3. Cualquier miembro puede pedir, firmar, pedir cambios y mergear; todo queda en bit\xE1cora (factory.run, factory.run_approve\u2026).",
|
|
14129
14129
|
"card baja la tarjeta viva del pedido al final de su hilo; la anterior queda como huella quieta con liga a la viva. Los roles tambi\xE9n pueden (factory_card).",
|
|
14130
|
+
"resume|stop|cancel: lo que ofrece la tarjeta de un pedido que dej\xF3 de avanzar \u2014 su rol sigue donde se qued\xF3, detener el turno que corre ahora o cancelar el pedido. Cualquier miembro; staff de Ghosty tambi\xE9n en espacios donde no es miembro (s\xF3lo esto, nunca firmar ni mergear). Queda en bit\xE1cora (factory.run_resume\u2026).",
|
|
14130
14131
|
"Leer pide ser miembro del espacio; renombrar, ser due\xF1o, admin o staff de Ghosty, y queda en bit\xE1cora (factory.run_rename). La tarjeta del room toma el t\xEDtulo nuevo en su siguiente refresco.",
|
|
14131
14132
|
"Qui\xE9n es @plan o @build: ghosty agents ls --handle plan --workspace SLUG.",
|
|
14132
14133
|
"stats: pedidos, % mezclados, mediana de minutos hasta el PR y hasta el merge, vueltas @check\u2192@build y CI rojo por pedido, y costo por pedido y por PR mezclado. El costo incluye el primer turno de @plan en el hilo, antes de que naciera el pedido.",
|
|
@@ -15121,6 +15122,7 @@ HELPS.factory = {
|
|
|
15121
15122
|
"ghosty factory env ls [OWNER/REPO] [--workspace SLUG]",
|
|
15122
15123
|
"ghosty factory env push [OWNER/REPO] [FILE] [--gen KEY,KEY] [--replace] [--yes] [--workspace SLUG]",
|
|
15123
15124
|
"ghosty factory runs recheck <run> [--yes] [--workspace SLUG]",
|
|
15125
|
+
"ghosty factory runs resume|stop|cancel <run> [--yes] [--workspace SLUG]",
|
|
15124
15126
|
"ghosty factory runs choose <run> [a|b|c] [--yes] [--workspace SLUG]",
|
|
15125
15127
|
`ghosty factory run "What, why and how we know it's done" --repo OWNER/REPO [--room R] [--wait] [--timeout 1800] [--workspace SLUG]`,
|
|
15126
15128
|
"ghosty factory runs watch <run> | --thread <id> [--timeout 1800] [--workspace SLUG]",
|
|
@@ -15161,7 +15163,7 @@ HELPS.factory = {
|
|
|
15161
15163
|
["--yes", "rename, approve, changes, merge: skip the confirmation (required without a terminal)"]
|
|
15162
15164
|
],
|
|
15163
15165
|
examples: ["ghosty factory rooms connect blissito/ghosty-studio --room studio --create --workspace business", 'ghosty factory run "Add a COLLABORATOR role: invited by room, lands in the room, no personal agent. Done when an invite to #factory logs in straight there" --repo blissito/ghosty-studio --wait --workspace business', "ghosty factory runs approve 31 --plan-version 2 --workspace business", "ghosty factory runs watch 31 --workspace business", "ghosty factory runs merge 31 --workspace business", "ghosty factory runs ls --workspace business --room mailmask", "ghosty factory runs show 15 --workspace business", "ghosty factory runs show 13 --timing --workspace mercadito-verde", "ghosty factory runs why 13 --workspace mercadito-verde", "ghosty factory runs advise 115 --workspace business", "ghosty factory runs stats --since 7d --workspace mercadito-verde", 'ghosty factory runs rename 15 "Docs del CLI al d\xEDa" --workspace business --dry-run'],
|
|
15164
|
-
notes: ["env: the preview's environment variables live on your computer, so your terminal agent pushes them. ls lists saved and missing names (from .env.example and from runs escalated because the preview won't start); push adds them (only the workspace owner). Use disposable values, never production: the preview runs the repo's code. Values never come back. Then runs recheck <run>: @check reviews the same PR again without another @build loop. Logged as factory.env_set and factory.run_recheck.", "sprints: when the goal is too big for one run, @plan proposes a sprint (3\u20138 tickets in order, with dependencies) as a draft. approve signs every included ticket at once; they start one at a time as their dependencies merge. Only the owner or whoever asked can approve. Logged as factory.sprint_approve.", "rooms repos lists the repos your GitHub App sees (most recent first) and which rooms each one is already in.", "rooms connect checks that YOUR GitHub App installation sees the repo (if not, it gives you the link to grant access) and tells you how ready the repo is for agents (levels 1\u20133 and what's missing). Any member can connect; only whoever connected it, or the owner, can disconnect. Logged as factory.room_connect / factory.room_disconnect.", "run posts \xAB@plan <your text>\xBB in the room under YOUR name, like typing it in Teams: the same plan, approvals and card. Write it like a ticket: the goal, why, and how you'll know it's done. Any member can run, approve, ask for changes and merge; each one is logged (factory.run, factory.run_approve\u2026).", "card brings the run's live card down to the bottom of its thread; the old one stays as a still footprint linking to the live one. The roles can do it too (factory_card).", "Reading needs membership in the workspace; rename needs owner, admin or Ghosty staff, and is logged (factory.run_rename). The room's card picks the new title up on its next refresh.", "Who is @plan or @build: ghosty agents ls --handle plan --workspace SLUG.", "stats: runs, % merged, median minutes to PR and to merge, @check\u2192@build loops and red CI per run, and cost per run and per merged PR. The cost counts @plan's first turn in the thread, before the run existed.", "--timing reads each role's transcript: engines without one (ACP) show minutes and cost but no commands. It also lists stuck patterns: a command open 8+ min, the same command 4 times, the same error 3 times, going back and forth, or apt install inside a box.", "why: if the run wasn't healthy (stuck patterns, @build over 30 min, loops, red CI, escalated), a category (missing_service, unresolved_build_or_tests, fixed_strategy\u2026), the critical step with its evidence and what to change next time. First it checks the approved plan's criteria against the evidence: approved without the preview a criterion asked for, merged without preview, smoke failed on the merged commit, or a CI criterion with CI none \u2192 approved_unmet_criterion, above any stuck pattern. A healthy run doesn't call the model. When a run closes unhealthy, the room gets this summary in its thread.", "advise: for an ESCALATED run, what to do \u2014 let @build fix it or redo the plan \u2014 with the note for @build point by point (each one cites the finding or plan step behind it) and what @check should review. It compares @check's findings against the signed plan. Nothing is applied: it prints the exact approve/changes command. show tells you why it was escalated.", "While a role works, if it gets stuck the room gets a \u23F3 message in the thread and the role is told what to check; from the second time in the same run, whoever asked gets a push.", "choose: when @check escalates a decision it leaves 2\u20133 concrete paths (a, b, c), each one an action: build (@build does it on the branch), recheck, replan, close, merge_as_is or fix_main (opens a separate run that fixes the main branch; this run waits without counting as \xAByour turn\xBB and is updated and re-reviewed on its own when the fix merges). Without a letter it lists them; with one it applies it and the choice reaches the next role word for word. merge_as_is merges with CI as it is (red checks included): use it when what's red isn't caused by the PR; it asks for confirmation and is logged (merge_override). show lists the options too."],
|
|
15166
|
+
notes: ["env: the preview's environment variables live on your computer, so your terminal agent pushes them. ls lists saved and missing names (from .env.example and from runs escalated because the preview won't start); push adds them (only the workspace owner). Use disposable values, never production: the preview runs the repo's code. Values never come back. Then runs recheck <run>: @check reviews the same PR again without another @build loop. Logged as factory.env_set and factory.run_recheck.", "sprints: when the goal is too big for one run, @plan proposes a sprint (3\u20138 tickets in order, with dependencies) as a draft. approve signs every included ticket at once; they start one at a time as their dependencies merge. Only the owner or whoever asked can approve. Logged as factory.sprint_approve.", "rooms repos lists the repos your GitHub App sees (most recent first) and which rooms each one is already in.", "rooms connect checks that YOUR GitHub App installation sees the repo (if not, it gives you the link to grant access) and tells you how ready the repo is for agents (levels 1\u20133 and what's missing). Any member can connect; only whoever connected it, or the owner, can disconnect. Logged as factory.room_connect / factory.room_disconnect.", "run posts \xAB@plan <your text>\xBB in the room under YOUR name, like typing it in Teams: the same plan, approvals and card. Write it like a ticket: the goal, why, and how you'll know it's done. Any member can run, approve, ask for changes and merge; each one is logged (factory.run, factory.run_approve\u2026).", "card brings the run's live card down to the bottom of its thread; the old one stays as a still footprint linking to the live one. The roles can do it too (factory_card).", "resume|stop|cancel: what the card offers for a run that stopped moving \u2014 its role picks up where it left off, stop the turn running now, or cancel the run. Any member; Ghosty staff too, even in a workspace they're not a member of (only these, never approve or merge). Logged (factory.run_resume\u2026).", "Reading needs membership in the workspace; rename needs owner, admin or Ghosty staff, and is logged (factory.run_rename). The room's card picks the new title up on its next refresh.", "Who is @plan or @build: ghosty agents ls --handle plan --workspace SLUG.", "stats: runs, % merged, median minutes to PR and to merge, @check\u2192@build loops and red CI per run, and cost per run and per merged PR. The cost counts @plan's first turn in the thread, before the run existed.", "--timing reads each role's transcript: engines without one (ACP) show minutes and cost but no commands. It also lists stuck patterns: a command open 8+ min, the same command 4 times, the same error 3 times, going back and forth, or apt install inside a box.", "why: if the run wasn't healthy (stuck patterns, @build over 30 min, loops, red CI, escalated), a category (missing_service, unresolved_build_or_tests, fixed_strategy\u2026), the critical step with its evidence and what to change next time. First it checks the approved plan's criteria against the evidence: approved without the preview a criterion asked for, merged without preview, smoke failed on the merged commit, or a CI criterion with CI none \u2192 approved_unmet_criterion, above any stuck pattern. A healthy run doesn't call the model. When a run closes unhealthy, the room gets this summary in its thread.", "advise: for an ESCALATED run, what to do \u2014 let @build fix it or redo the plan \u2014 with the note for @build point by point (each one cites the finding or plan step behind it) and what @check should review. It compares @check's findings against the signed plan. Nothing is applied: it prints the exact approve/changes command. show tells you why it was escalated.", "While a role works, if it gets stuck the room gets a \u23F3 message in the thread and the role is told what to check; from the second time in the same run, whoever asked gets a push.", "choose: when @check escalates a decision it leaves 2\u20133 concrete paths (a, b, c), each one an action: build (@build does it on the branch), recheck, replan, close, merge_as_is or fix_main (opens a separate run that fixes the main branch; this run waits without counting as \xAByour turn\xBB and is updated and re-reviewed on its own when the fix merges). Without a letter it lists them; with one it applies it and the choice reaches the next role word for word. merge_as_is merges with CI as it is (red checks included): use it when what's red isn't caused by the PR; it asks for confirmation and is logged (merge_override). show lists the options too."],
|
|
15165
15167
|
learn: `${REF}#fabrica`
|
|
15166
15168
|
};
|
|
15167
15169
|
function helpText(cmd) {
|
|
@@ -16620,7 +16622,7 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
16620
16622
|
const r = await api(path2, { body: { title } });
|
|
16621
16623
|
return out(r, () => `${lavender("\u2713")} ${line2} ${dim(t("(logged; the room card picks it up on its next refresh)", "(queda en bit\xE1cora; la tarjeta del room lo toma en su siguiente refresco)"))}`);
|
|
16622
16624
|
}
|
|
16623
|
-
if (verb === "approve" || verb === "changes" || verb === "recheck" || verb === "merge" || verb === "card") {
|
|
16625
|
+
if (verb === "approve" || verb === "changes" || verb === "recheck" || verb === "merge" || verb === "card" || verb === "resume" || verb === "stop" || verb === "cancel") {
|
|
16624
16626
|
const id = Number(ref.replace(/^#/, ""));
|
|
16625
16627
|
const note = v.note?.trim() ?? title?.trim();
|
|
16626
16628
|
const scope = verb === "approve" || verb === "changes" ? v["check-scope"]?.trim() : void 0;
|
|
@@ -16629,7 +16631,9 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
16629
16631
|
if (verb !== "card") {
|
|
16630
16632
|
requireYesIfHeadless(v.yes, retry);
|
|
16631
16633
|
const cur = (await api(`${base}/${id}`)).run;
|
|
16632
|
-
|
|
16634
|
+
if (verb === "resume" && cur.running?.length)
|
|
16635
|
+
throw usage(t(`@${cur.running[0].role ?? "?"} is working on #${id} right now: there's nothing to resume.`, `@${cur.running[0].role ?? "?"} est\xE1 trabajando en el #${id} ahora: no hay nada que retomar.`), `ghosty factory runs stop ${id} --workspace ${ws}`);
|
|
16636
|
+
const what = { approve: t(`Approve #${id} (plan v${cur.planVersion})`, `\xBFFirmar el #${id} (plan v${cur.planVersion})`), changes: t(`Ask for changes on #${id}`, `\xBFPedir cambios en el #${id}`), recheck: t(`Have @check review #${id} again (no @build loop)`, `\xBFQue @check revise otra vez el #${id} (sin vuelta de @build)`), merge: t(`Merge the PR of #${id} (${cur.pr ?? "\u2014"})`, `\xBFMergear el PR del #${id} (${cur.pr ?? "\u2014"})`), resume: t(`Resume #${id} (its role picks up where it stopped)`, `\xBFRetomar el #${id} (su rol sigue donde se qued\xF3)`), stop: t(`Stop the turn running on #${id}`, `\xBFDetener el turno que corre en el #${id}`), cancel: t(`Cancel #${id} \xAB${cur.title}\xBB (it can't be undone)`, `\xBFCancelar el #${id} \xAB${cur.title}\xBB (no se deshace)`) }[verb];
|
|
16633
16637
|
await confirm(`${what}?`, { yes: v.yes, retry });
|
|
16634
16638
|
}
|
|
16635
16639
|
const r = await api(`${base}/${id}/act`, {
|
|
@@ -16640,9 +16644,12 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
16640
16644
|
changes: t(`#${id}: changes sent to @plan.`, `#${id}: cambios enviados a @plan.`),
|
|
16641
16645
|
recheck: t(`#${id}: @check is reviewing the same PR again.`, `#${id}: @check revisa otra vez el mismo PR.`),
|
|
16642
16646
|
merge: r.queued ? t(`#${id}: merge queued, it goes in when CI passes.`, `#${id}: merge en cola, entra cuando pase el CI.`) : t(`#${id} merged.`, `#${id} mergeado.`),
|
|
16643
|
-
card: t(`#${id}: the card is at the bottom of its thread again.`, `#${id}: la tarjeta volvi\xF3 al final de su hilo.`)
|
|
16647
|
+
card: t(`#${id}: the card is at the bottom of its thread again.`, `#${id}: la tarjeta volvi\xF3 al final de su hilo.`),
|
|
16648
|
+
resume: t(`#${id}: its role is back on it (${stage(r.status)}).`, `#${id}: su rol lo retom\xF3 (${stage(r.status)}).`),
|
|
16649
|
+
stop: t(`#${id}: turn stopped.`, `#${id}: turno detenido.`),
|
|
16650
|
+
cancel: t(`#${id} cancelled.`, `#${id} cancelado.`)
|
|
16644
16651
|
}[verb];
|
|
16645
|
-
return out(r, () => `${lavender("\u2713")} ${msg}${verb === "approve" || verb === "changes" || verb === "recheck" ? `
|
|
16652
|
+
return out(r, () => `${lavender("\u2713")} ${msg}${verb === "approve" || verb === "changes" || verb === "recheck" || verb === "resume" ? `
|
|
16646
16653
|
${dim(t(`Follow it: ghosty factory runs watch ${id} --workspace ${ws}`, `S\xEDguelo: ghosty factory runs watch ${id} --workspace ${ws}`))}` : ""}`);
|
|
16647
16654
|
}
|
|
16648
16655
|
if (verb === "choose") {
|
|
@@ -16669,7 +16676,7 @@ ${dim(t(`Follow it: ghosty factory runs watch ${id} --workspace ${ws}`, `S\xEDgu
|
|
|
16669
16676
|
${dim(t(`Follow it: ghosty factory runs watch ${id} --workspace ${ws}`, `S\xEDguelo: ghosty factory runs watch ${id} --workspace ${ws}`))}`)
|
|
16670
16677
|
);
|
|
16671
16678
|
}
|
|
16672
|
-
throw usage(t(`Unknown runs subcommand: ${verb}.`, `Subcomando de runs desconocido: ${verb}.`), 'ghosty factory runs ls | show <run> [--timing] | why <run> [--refresh] | advise <run> | stats [--since 30d] | rename <run> "title" | watch <run> | approve <run> --plan-version N [--note "\u2026"] [--check-scope "\u2026"] | changes <run> --note "\u2026" | recheck <run> | choose <run> [a|b|c] | merge <run> | card <run>');
|
|
16679
|
+
throw usage(t(`Unknown runs subcommand: ${verb}.`, `Subcomando de runs desconocido: ${verb}.`), 'ghosty factory runs ls | show <run> [--timing] | why <run> [--refresh] | advise <run> | stats [--since 30d] | rename <run> "title" | watch <run> | approve <run> --plan-version N [--note "\u2026"] [--check-scope "\u2026"] | changes <run> --note "\u2026" | recheck <run> | resume|stop|cancel <run> | choose <run> [a|b|c] | merge <run> | card <run>');
|
|
16673
16680
|
}
|
|
16674
16681
|
async function agentsByHandle(handle, workspace) {
|
|
16675
16682
|
const qs = new URLSearchParams({ handle, ...workspace ? { workspace } : {} });
|
|
@@ -17968,7 +17975,7 @@ async function main() {
|
|
|
17968
17975
|
const tokens = splitTokens(pos, v);
|
|
17969
17976
|
globals.token = tokens.session;
|
|
17970
17977
|
globals.profile = v.profile;
|
|
17971
|
-
if (v.version) return out({ version: "0.
|
|
17978
|
+
if (v.version) return out({ version: "0.95.0" }, () => "0.95.0");
|
|
17972
17979
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
17973
17980
|
const context = (await readConfig()).workspace;
|
|
17974
17981
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -18175,7 +18182,7 @@ async function main() {
|
|
|
18175
18182
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
18176
18183
|
}
|
|
18177
18184
|
}
|
|
18178
|
-
var notice = updateNotice("0.
|
|
18185
|
+
var notice = updateNotice("0.95.0").catch(() => null);
|
|
18179
18186
|
main().then(
|
|
18180
18187
|
async () => {
|
|
18181
18188
|
const n = process.stderr.isTTY ? await notice : null;
|
package/package.json
CHANGED