@workser/cli 0.2.6 → 0.2.7
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/index.js +337 -85
- package/package.json +1 -1
- package/skills/workser/SKILL.md +12 -13
- package/skills/workser/reference/version-control.md +66 -0
package/dist/index.js
CHANGED
|
@@ -4404,6 +4404,73 @@ workser storage get <key> [dest] # download an object (or print its URL)
|
|
|
4404
4404
|
infrastructure on the project's own Neon branch, available only on dedicated tenancy
|
|
4405
4405
|
in a supported region. They are different stores \u2014 a file put in one is not visible
|
|
4406
4406
|
in the other. See \`reference/neon-backend.md\`.
|
|
4407
|
+
`
|
|
4408
|
+
},
|
|
4409
|
+
{
|
|
4410
|
+
topic: "version-control",
|
|
4411
|
+
title: "Saving, undoing & syncing your work",
|
|
4412
|
+
summary: "Checkpoint before risky edits, restore when they go wrong, sync with Workser.",
|
|
4413
|
+
commands: ["checkpoint", "restore", "sync"],
|
|
4414
|
+
source: "skills/workser/reference/version-control.md",
|
|
4415
|
+
body: `# Saving, undoing & syncing your work
|
|
4416
|
+
|
|
4417
|
+
\`\`\`
|
|
4418
|
+
workser checkpoint ["what you're about to try"] # save the folder as it stands now
|
|
4419
|
+
workser restore # go back to the newest checkpoint
|
|
4420
|
+
workser restore --list # see the checkpoints you can go back to
|
|
4421
|
+
workser restore <ref> # go back to a specific one
|
|
4422
|
+
workser sync [--branch dev] [--app <id>] # reconcile this folder with Workser
|
|
4423
|
+
\`\`\`
|
|
4424
|
+
|
|
4425
|
+
## Why these exist instead of git
|
|
4426
|
+
|
|
4427
|
+
This folder is a git repository, but **the history is Workser's**. There is no
|
|
4428
|
+
remote: code moves to and from Workser over its API as git bundles, using the
|
|
4429
|
+
same session everything else here uses. No credential is read, and the user's
|
|
4430
|
+
own git keys and identity are never touched.
|
|
4431
|
+
|
|
4432
|
+
That is what makes any of this work on a computer with no access to the
|
|
4433
|
+
Workser-managed repository \u2014 which is every computer. Nobody has repo access;
|
|
4434
|
+
the API does.
|
|
4435
|
+
|
|
4436
|
+
So the ordinary git verbs are wrong here, and these three replace them:
|
|
4437
|
+
|
|
4438
|
+
| Instead of | Run |
|
|
4439
|
+
|---|---|
|
|
4440
|
+
| \`git stash\` | \`workser checkpoint\` |
|
|
4441
|
+
| \`git commit\` | \`workser checkpoint\` |
|
|
4442
|
+
| \`git reset --hard\` / \`git checkout .\` | \`workser restore\` |
|
|
4443
|
+
| \`git pull\` / \`git push\` | \`workser sync\` |
|
|
4444
|
+
|
|
4445
|
+
Reading is always fine: \`git log\`, \`git diff\`, \`git show\`, \`git status\`.
|
|
4446
|
+
|
|
4447
|
+
## Notes that matter
|
|
4448
|
+
|
|
4449
|
+
- **Checkpoint before anything you might want to take back** \u2014 a refactor across
|
|
4450
|
+
many files, a dependency upgrade, deleting something large. It costs a second.
|
|
4451
|
+
- **\`git stash\` is the one that actually loses work.** Workser publishes what is
|
|
4452
|
+
on disk, so a deploy while your changes are stashed ships the version *without
|
|
4453
|
+
them* \u2014 and if the run is stopped between \`stash\` and \`stash pop\`, the user
|
|
4454
|
+
opens their folder and their work is gone. \`workser checkpoint\` has neither
|
|
4455
|
+
failure mode.
|
|
4456
|
+
- **Restoring never discards anything.** The current state is saved first, then
|
|
4457
|
+
the older state is restored on top. If you restore the wrong thing, restore
|
|
4458
|
+
again. Say this to the user \u2014 it is the reason they can afford to say yes.
|
|
4459
|
+
- **These are the same checkpoints as the app's Undo button.** One history, not
|
|
4460
|
+
two: a checkpoint you take here appears in Workser, and vice versa.
|
|
4461
|
+
- **\`sync\` pulls before it pushes.** If it reports \`diverged\`, both sides changed
|
|
4462
|
+
and it stops rather than picking a winner \u2014 tell the user to resolve it in
|
|
4463
|
+
Workser instead of forcing it.
|
|
4464
|
+
- **These three need the Workser app running on this computer**, because they
|
|
4465
|
+
work with files. If you get \`needs_local_app\`, you are on a machine that only
|
|
4466
|
+
has the CLI and a token \u2014 say so rather than looking for a workaround. Every
|
|
4467
|
+
read-only command (\`projects\`, \`env\`, \`db\`, \`logs\`, \`status\`) still works.
|
|
4468
|
+
|
|
4469
|
+
## Shipping is separate
|
|
4470
|
+
|
|
4471
|
+
\`workser sync\` reconciles the folder. \`workser deploy\` puts it in front of
|
|
4472
|
+
users. Syncing does not deploy, and deploying does not require you to sync
|
|
4473
|
+
first \u2014 deploy publishes what is on disk. See \`reference/deploy.md\`.
|
|
4407
4474
|
`
|
|
4408
4475
|
}
|
|
4409
4476
|
];
|
|
@@ -4570,6 +4637,21 @@ function buildContext(opts) {
|
|
|
4570
4637
|
function runTarget(ctx) {
|
|
4571
4638
|
return ctx.runId || "current";
|
|
4572
4639
|
}
|
|
4640
|
+
function requireLocalApp(ctx, what) {
|
|
4641
|
+
if (ctx.mode === "daemon") return;
|
|
4642
|
+
throw new WorkserError(
|
|
4643
|
+
`\`workser ${what}\` works with the code in this folder, so it needs the Workser app running on this computer.
|
|
4644
|
+
|
|
4645
|
+
This shell is talking to ${ctx.endpoint} instead of a local app.
|
|
4646
|
+
|
|
4647
|
+
\u2022 On your own computer: open Workser and try again.
|
|
4648
|
+
\u2022 On a computer without Workser: install it from https://workser.ai/download,
|
|
4649
|
+
sign in, and open this project \u2014 that is what puts the code here.
|
|
4650
|
+
|
|
4651
|
+
Commands that only read your account (projects, env, db, logs, status) work as normal.`,
|
|
4652
|
+
{ code: "needs_local_app" }
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4573
4655
|
function requireProject(ctx) {
|
|
4574
4656
|
if (!ctx.projectId) {
|
|
4575
4657
|
throw new WorkserError(
|
|
@@ -5521,6 +5603,7 @@ function registerDeploy(program3) {
|
|
|
5521
5603
|
"which app to publish (default: the app this folder is linked to)"
|
|
5522
5604
|
).action(
|
|
5523
5605
|
action(async ({ ctx, opts }) => {
|
|
5606
|
+
requireLocalApp(ctx, "deploy");
|
|
5524
5607
|
const projectId = requireProject(ctx);
|
|
5525
5608
|
const dep = await api(ctx, `/v1/projects/${projectId}/deploy`, {
|
|
5526
5609
|
body: {
|
|
@@ -5675,6 +5758,7 @@ function openUrl(url) {
|
|
|
5675
5758
|
|
|
5676
5759
|
// src/commands/doctor.ts
|
|
5677
5760
|
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
5761
|
+
import { spawnSync } from "child_process";
|
|
5678
5762
|
function registerDoctor(program3) {
|
|
5679
5763
|
program3.command("doctor").description("Print the resolved endpoint, mode, token presence (masked), and current project").action(
|
|
5680
5764
|
action(({ ctx, opts }) => {
|
|
@@ -5685,12 +5769,14 @@ function registerDoctor(program3) {
|
|
|
5685
5769
|
const tokenSource = opts.token ? "--token" : process.env.WORKSER_TOKEN ? "$WORKSER_TOKEN" : session.token ? "session" : void 0;
|
|
5686
5770
|
const endpointSource = opts.endpoint ? "--endpoint" : process.env.WORKSER_DAEMON_URL ? "$WORKSER_DAEMON_URL" : session.endpoint ? "session" : process.env.WORKSER_API_URL ? "$WORKSER_API_URL" : `cloud-default: ${env}`;
|
|
5687
5771
|
const projectSource = opts.project ? "--project" : link?.projectId ? ".workser link" : session.defaultProjectId ? "session" : void 0;
|
|
5772
|
+
const git = gitVersion();
|
|
5688
5773
|
const report = {
|
|
5689
5774
|
endpoint: ctx.endpoint,
|
|
5690
5775
|
endpointSource,
|
|
5691
5776
|
env,
|
|
5692
5777
|
envIgnored,
|
|
5693
5778
|
mode: ctx.mode,
|
|
5779
|
+
git: { present: git !== null, version: git },
|
|
5694
5780
|
token: {
|
|
5695
5781
|
present: Boolean(ctx.token),
|
|
5696
5782
|
masked: ctx.token ? maskToken(ctx.token) : null,
|
|
@@ -5718,6 +5804,20 @@ function registerDoctor(program3) {
|
|
|
5718
5804
|
` project: ${ctx.projectId ?? import_picocolors13.default.dim("none")}` + (link?.name ? ` ${import_picocolors13.default.dim(`(${link.name})`)}` : "") + (projectSource ? import_picocolors13.default.dim(` [${projectSource}]`) : "")
|
|
5719
5805
|
);
|
|
5720
5806
|
line(` cwd: ${ctx.cwd}`);
|
|
5807
|
+
line(` git: ${git ?? import_picocolors13.default.dim("not on this shell's PATH")}`);
|
|
5808
|
+
if (!git) {
|
|
5809
|
+
line("");
|
|
5810
|
+
line(
|
|
5811
|
+
import_picocolors13.default.dim(
|
|
5812
|
+
" Workser brings its own git, so syncing and publishing still work."
|
|
5813
|
+
)
|
|
5814
|
+
);
|
|
5815
|
+
line(
|
|
5816
|
+
import_picocolors13.default.dim(
|
|
5817
|
+
` Only needed if you want to run git yourself here. ${GIT_INSTALL_HINT}`
|
|
5818
|
+
)
|
|
5819
|
+
);
|
|
5820
|
+
}
|
|
5721
5821
|
if (envIgnored) {
|
|
5722
5822
|
line("");
|
|
5723
5823
|
line(
|
|
@@ -5736,6 +5836,24 @@ function registerDoctor(program3) {
|
|
|
5736
5836
|
})
|
|
5737
5837
|
);
|
|
5738
5838
|
}
|
|
5839
|
+
function gitVersion() {
|
|
5840
|
+
try {
|
|
5841
|
+
const res = spawnSync("git", ["--version"], {
|
|
5842
|
+
encoding: "utf8",
|
|
5843
|
+
timeout: 5e3,
|
|
5844
|
+
// A macOS box with no Command Line Tools has a `git` SHIM that pops a GUI
|
|
5845
|
+
// installer when run. Inheriting stdio would surface that dialog from a
|
|
5846
|
+
// command the user expects to just print a report.
|
|
5847
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5848
|
+
});
|
|
5849
|
+
if (res.error || res.status !== 0) return null;
|
|
5850
|
+
const out = (res.stdout || "").toString().trim();
|
|
5851
|
+
return out || null;
|
|
5852
|
+
} catch {
|
|
5853
|
+
return null;
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
var GIT_INSTALL_HINT = process.platform === "darwin" ? "Install it with: xcode-select --install" : process.platform === "win32" ? "Install it from: https://git-scm.com/download/win" : "Install it with your package manager, e.g. apt install git";
|
|
5739
5857
|
function maskToken(token) {
|
|
5740
5858
|
if (token.length <= 8) return "\u2022".repeat(token.length);
|
|
5741
5859
|
return `${token.slice(0, 3)}${"\u2022".repeat(Math.min(token.length - 6, 12))}${token.slice(-3)}`;
|
|
@@ -5863,6 +5981,7 @@ function registerVerify(program3) {
|
|
|
5863
5981
|
"comma-separated subset, e.g. --only typecheck,build"
|
|
5864
5982
|
).action(
|
|
5865
5983
|
action(async ({ ctx, opts }) => {
|
|
5984
|
+
requireLocalApp(ctx, "verify");
|
|
5866
5985
|
const only = opts.only ? String(opts.only).split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
5867
5986
|
const res = await api(ctx, `/v1/verify`, {
|
|
5868
5987
|
body: { cwd: ctx.cwd, ...only ? { only } : {} }
|
|
@@ -5890,8 +6009,139 @@ function printVerify(res) {
|
|
|
5890
6009
|
);
|
|
5891
6010
|
}
|
|
5892
6011
|
|
|
5893
|
-
// src/commands/
|
|
6012
|
+
// src/commands/checkpoint.ts
|
|
5894
6013
|
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
6014
|
+
function registerCheckpoint(program3) {
|
|
6015
|
+
program3.command("checkpoint [label]").description(
|
|
6016
|
+
"Save the current state of this folder so you can come back to it"
|
|
6017
|
+
).addHelpText(
|
|
6018
|
+
"after",
|
|
6019
|
+
"\nUse this before a risky change. To come back: `workser restore`.\nNever use `git stash` or `git commit` here \u2014 Workser owns this history.\n"
|
|
6020
|
+
).action(
|
|
6021
|
+
action(async ({ ctx, args }) => {
|
|
6022
|
+
requireLocalApp(ctx, "checkpoint");
|
|
6023
|
+
const label = typeof args[0] === "string" ? args[0] : "";
|
|
6024
|
+
const res = await api(ctx, `/v1/checkpoints`, {
|
|
6025
|
+
body: { cwd: ctx.cwd, ...label ? { label } : {} }
|
|
6026
|
+
});
|
|
6027
|
+
ok(res, () => {
|
|
6028
|
+
const p = res?.point;
|
|
6029
|
+
success(`Saved a checkpoint${p?.label ? `: ${p.label}` : ""}`);
|
|
6030
|
+
if (p?.ref) line(import_picocolors16.default.dim(` ${p.ref.slice(0, 7)}`));
|
|
6031
|
+
line(import_picocolors16.default.dim(" Come back to it with `workser restore`."));
|
|
6032
|
+
});
|
|
6033
|
+
})
|
|
6034
|
+
);
|
|
6035
|
+
const restore = program3.command("restore [ref]").description(
|
|
6036
|
+
"Put this folder back to a saved checkpoint (default: the most recent)"
|
|
6037
|
+
).option("--list", "show the checkpoints you can go back to", false).action(
|
|
6038
|
+
action(async ({ ctx, args, opts }) => {
|
|
6039
|
+
requireLocalApp(ctx, "restore");
|
|
6040
|
+
if (opts.list) {
|
|
6041
|
+
const res2 = await api(ctx, `/v1/checkpoints`, {
|
|
6042
|
+
query: { cwd: ctx.cwd }
|
|
6043
|
+
});
|
|
6044
|
+
const points = res2?.points ?? [];
|
|
6045
|
+
return ok(res2, () => printPoints(points));
|
|
6046
|
+
}
|
|
6047
|
+
const ref = typeof args[0] === "string" ? args[0].trim() : "";
|
|
6048
|
+
const res = await api(ctx, `/v1/undo`, {
|
|
6049
|
+
body: { cwd: ctx.cwd, ...ref ? { ref } : {} }
|
|
6050
|
+
});
|
|
6051
|
+
ok(res, () => {
|
|
6052
|
+
success(
|
|
6053
|
+
`Put the folder back${res?.restoredTo ? ` to ${String(res.restoredTo).slice(0, 7)}` : ""}`
|
|
6054
|
+
);
|
|
6055
|
+
if (res?.filesChanged) {
|
|
6056
|
+
line(
|
|
6057
|
+
import_picocolors16.default.dim(
|
|
6058
|
+
` ${res.filesChanged} file${res.filesChanged === 1 ? "" : "s"} changed`
|
|
6059
|
+
)
|
|
6060
|
+
);
|
|
6061
|
+
}
|
|
6062
|
+
line(import_picocolors16.default.dim(" This is reversible: `workser restore` again."));
|
|
6063
|
+
});
|
|
6064
|
+
})
|
|
6065
|
+
);
|
|
6066
|
+
restore.command("list").description("Show the checkpoints you can go back to").action(
|
|
6067
|
+
action(async ({ ctx }) => {
|
|
6068
|
+
requireLocalApp(ctx, "restore list");
|
|
6069
|
+
const res = await api(ctx, `/v1/checkpoints`, {
|
|
6070
|
+
query: { cwd: ctx.cwd }
|
|
6071
|
+
});
|
|
6072
|
+
ok(res, () => printPoints(res?.points ?? []));
|
|
6073
|
+
})
|
|
6074
|
+
);
|
|
6075
|
+
}
|
|
6076
|
+
function printPoints(points) {
|
|
6077
|
+
if (!points.length) {
|
|
6078
|
+
info("No checkpoints yet for this folder.");
|
|
6079
|
+
line(import_picocolors16.default.dim(" Take one with `workser checkpoint`."));
|
|
6080
|
+
return;
|
|
6081
|
+
}
|
|
6082
|
+
line(import_picocolors16.default.bold("Checkpoints"));
|
|
6083
|
+
for (const p of points) {
|
|
6084
|
+
const when = p.at ? new Date(p.at).toLocaleString() : "";
|
|
6085
|
+
line(
|
|
6086
|
+
` ${import_picocolors16.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors16.default.dim(` ${when}`) : ""}`
|
|
6087
|
+
);
|
|
6088
|
+
}
|
|
6089
|
+
line(
|
|
6090
|
+
import_picocolors16.default.dim(
|
|
6091
|
+
"\nGo back with `workser restore <ref>`, or just `workser restore` for the newest."
|
|
6092
|
+
)
|
|
6093
|
+
);
|
|
6094
|
+
}
|
|
6095
|
+
|
|
6096
|
+
// src/commands/sync.ts
|
|
6097
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
6098
|
+
function registerSync(program3) {
|
|
6099
|
+
program3.command("sync").description(
|
|
6100
|
+
"Reconcile this folder with the copy Workser holds (pull, then push)"
|
|
6101
|
+
).option("--branch <name>", "which branch to sync (default: this folder's)").option(
|
|
6102
|
+
"--app <webAppId>",
|
|
6103
|
+
"which app's code this folder holds (default: resolved from the folder)"
|
|
6104
|
+
).addHelpText(
|
|
6105
|
+
"after",
|
|
6106
|
+
"\nUse this instead of `git pull` / `git push` \u2014 this folder has no remote.\n"
|
|
6107
|
+
).action(
|
|
6108
|
+
action(async ({ ctx, opts }) => {
|
|
6109
|
+
requireLocalApp(ctx, "sync");
|
|
6110
|
+
const projectId = requireProject(ctx);
|
|
6111
|
+
const res = await api(ctx, `/v1/projects/${projectId}/git/sync`, {
|
|
6112
|
+
body: {
|
|
6113
|
+
cwd: ctx.cwd,
|
|
6114
|
+
...opts.branch ? { branch: opts.branch } : {},
|
|
6115
|
+
...opts.app ? { webAppId: opts.app } : {}
|
|
6116
|
+
}
|
|
6117
|
+
});
|
|
6118
|
+
const refused = res?.ok === false || res?.state === "diverged";
|
|
6119
|
+
ok(res, () => {
|
|
6120
|
+
if (refused) {
|
|
6121
|
+
warn(res?.message ?? "Couldn't sync this folder.");
|
|
6122
|
+
if (res?.state === "diverged") {
|
|
6123
|
+
line(
|
|
6124
|
+
import_picocolors17.default.dim(
|
|
6125
|
+
" This folder and Workser's copy have both changed. Open Workser to resolve it."
|
|
6126
|
+
)
|
|
6127
|
+
);
|
|
6128
|
+
}
|
|
6129
|
+
return;
|
|
6130
|
+
}
|
|
6131
|
+
if (res?.state === "empty") {
|
|
6132
|
+
line("Nothing to sync yet \u2014 there's no code here or on Workser.");
|
|
6133
|
+
return;
|
|
6134
|
+
}
|
|
6135
|
+
success("Synced");
|
|
6136
|
+
if (res?.ref) line(import_picocolors17.default.dim(` ${String(res.ref).slice(0, 7)}`));
|
|
6137
|
+
});
|
|
6138
|
+
if (refused) process.exitCode = 1;
|
|
6139
|
+
})
|
|
6140
|
+
);
|
|
6141
|
+
}
|
|
6142
|
+
|
|
6143
|
+
// src/commands/workflow.ts
|
|
6144
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
5895
6145
|
function registerWorkflow(program3) {
|
|
5896
6146
|
const wf = program3.command("workflow").description("Create, run, and inspect workflow automations for the project");
|
|
5897
6147
|
wf.command("list").description("List the project's workflows").action(
|
|
@@ -5899,10 +6149,10 @@ function registerWorkflow(program3) {
|
|
|
5899
6149
|
const projectId = requireProject(ctx);
|
|
5900
6150
|
const items = await api(ctx, `/v1/projects/${projectId}/workflows`);
|
|
5901
6151
|
ok(items, () => {
|
|
5902
|
-
if (!items?.length) return line(
|
|
6152
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No workflows yet. `workser workflow create`."));
|
|
5903
6153
|
for (const w of items) {
|
|
5904
|
-
const status = w.is_active ?
|
|
5905
|
-
line(`${w.id} ${
|
|
6154
|
+
const status = w.is_active ? import_picocolors18.default.green("active") : import_picocolors18.default.dim("inactive");
|
|
6155
|
+
line(`${w.id} ${import_picocolors18.default.bold(w.name ?? "Untitled")} ${status}`);
|
|
5906
6156
|
}
|
|
5907
6157
|
});
|
|
5908
6158
|
})
|
|
@@ -5914,7 +6164,7 @@ function registerWorkflow(program3) {
|
|
|
5914
6164
|
const res = await api(ctx, `/v1/projects/${projectId}/workflows`, {
|
|
5915
6165
|
body: { name: args[0], ...extra }
|
|
5916
6166
|
});
|
|
5917
|
-
ok(res, () => line(`Created workflow ${
|
|
6167
|
+
ok(res, () => line(`Created workflow ${import_picocolors18.default.bold(res.id)}.`));
|
|
5918
6168
|
})
|
|
5919
6169
|
);
|
|
5920
6170
|
wf.command("get <id>").description("Show a workflow's full definition").action(
|
|
@@ -5949,8 +6199,8 @@ function registerWorkflow(program3) {
|
|
|
5949
6199
|
action(async ({ ctx, args }) => {
|
|
5950
6200
|
const items = await api(ctx, `/v1/workflows/${args[0]}/executions`);
|
|
5951
6201
|
ok(items, () => {
|
|
5952
|
-
if (!items?.length) return line(
|
|
5953
|
-
for (const e of items) line(`${e.id} ${e.status ?? ""} ${
|
|
6202
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No runs yet."));
|
|
6203
|
+
for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors18.default.dim(e.started_at ?? "")}`);
|
|
5954
6204
|
});
|
|
5955
6205
|
})
|
|
5956
6206
|
);
|
|
@@ -5958,15 +6208,15 @@ function registerWorkflow(program3) {
|
|
|
5958
6208
|
action(async ({ ctx, args }) => {
|
|
5959
6209
|
const items = await api(ctx, `/v1/node-types`, { query: { q: args[0] } });
|
|
5960
6210
|
ok(items, () => {
|
|
5961
|
-
if (!items?.length) return line(
|
|
5962
|
-
for (const n of items) line(`${n.name ?? n.type} ${
|
|
6211
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No matching node types."));
|
|
6212
|
+
for (const n of items) line(`${n.name ?? n.type} ${import_picocolors18.default.dim(n.category ?? "")}`);
|
|
5963
6213
|
});
|
|
5964
6214
|
})
|
|
5965
6215
|
);
|
|
5966
6216
|
}
|
|
5967
6217
|
|
|
5968
6218
|
// src/commands/app.ts
|
|
5969
|
-
var
|
|
6219
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
5970
6220
|
function registerApp(program3) {
|
|
5971
6221
|
const appCmd = program3.command("app").description("Connect and use third-party app integrations (Gmail, Slack, Stripe, ...)");
|
|
5972
6222
|
appCmd.command("list").description("List connectable toolkits and this project's existing connections").option("--toolkit <slug>", "filter connections to one toolkit").action(
|
|
@@ -5979,8 +6229,8 @@ function registerApp(program3) {
|
|
|
5979
6229
|
ok({ catalog, connections }, () => {
|
|
5980
6230
|
const connected = new Set((connections ?? []).map((c) => c.toolkit ?? c.composio_app));
|
|
5981
6231
|
for (const t of catalog ?? []) {
|
|
5982
|
-
const status = connected.has(t.slug) ?
|
|
5983
|
-
line(`${t.slug} ${
|
|
6232
|
+
const status = connected.has(t.slug) ? import_picocolors19.default.green("connected") : import_picocolors19.default.dim("not connected");
|
|
6233
|
+
line(`${t.slug} ${import_picocolors19.default.bold(t.name ?? t.slug)} ${status}`);
|
|
5984
6234
|
}
|
|
5985
6235
|
});
|
|
5986
6236
|
})
|
|
@@ -5997,7 +6247,7 @@ function registerApp(program3) {
|
|
|
5997
6247
|
});
|
|
5998
6248
|
ok(
|
|
5999
6249
|
res,
|
|
6000
|
-
() => res.oauth_url ? line(`Open this URL to finish connecting: ${
|
|
6250
|
+
() => res.oauth_url ? line(`Open this URL to finish connecting: ${import_picocolors19.default.underline(res.oauth_url)}`) : line(`Connection ${res.connection_id} is ${res.status}.`)
|
|
6001
6251
|
);
|
|
6002
6252
|
})
|
|
6003
6253
|
);
|
|
@@ -6015,8 +6265,8 @@ function registerApp(program3) {
|
|
|
6015
6265
|
const projectId = requireProject(ctx);
|
|
6016
6266
|
const items = await api(ctx, `/v1/projects/${projectId}/integrations/${args[0]}/tools`);
|
|
6017
6267
|
ok(items, () => {
|
|
6018
|
-
if (!items?.length) return line(
|
|
6019
|
-
for (const t of items) line(`${t.slug} ${
|
|
6268
|
+
if (!items?.length) return line(import_picocolors19.default.dim("No tools found."));
|
|
6269
|
+
for (const t of items) line(`${t.slug} ${import_picocolors19.default.dim(t.description ?? "")}`);
|
|
6020
6270
|
});
|
|
6021
6271
|
})
|
|
6022
6272
|
);
|
|
@@ -6032,7 +6282,7 @@ function registerApp(program3) {
|
|
|
6032
6282
|
}
|
|
6033
6283
|
|
|
6034
6284
|
// src/commands/tool.ts
|
|
6035
|
-
var
|
|
6285
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
6036
6286
|
function registerTool(program3) {
|
|
6037
6287
|
const tool = program3.command("tool").description(
|
|
6038
6288
|
"Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
|
|
@@ -6041,7 +6291,7 @@ function registerTool(program3) {
|
|
|
6041
6291
|
action(async ({ ctx }) => {
|
|
6042
6292
|
const tools = await api(ctx, "/v1/tool/list");
|
|
6043
6293
|
ok(tools, () => {
|
|
6044
|
-
if (!tools?.length) return line(
|
|
6294
|
+
if (!tools?.length) return line(import_picocolors20.default.dim("No tools available."));
|
|
6045
6295
|
const byCategory = /* @__PURE__ */ new Map();
|
|
6046
6296
|
for (const t of tools) {
|
|
6047
6297
|
const list = byCategory.get(t.category) ?? [];
|
|
@@ -6049,9 +6299,9 @@ function registerTool(program3) {
|
|
|
6049
6299
|
byCategory.set(t.category, list);
|
|
6050
6300
|
}
|
|
6051
6301
|
for (const [category, items] of byCategory) {
|
|
6052
|
-
line(
|
|
6302
|
+
line(import_picocolors20.default.bold(category) + ":");
|
|
6053
6303
|
for (const t of items) {
|
|
6054
|
-
line(` ${t.name} ${
|
|
6304
|
+
line(` ${t.name} ${import_picocolors20.default.dim(t.description ?? "")}`);
|
|
6055
6305
|
}
|
|
6056
6306
|
}
|
|
6057
6307
|
});
|
|
@@ -6069,7 +6319,7 @@ function registerTool(program3) {
|
|
|
6069
6319
|
}
|
|
6070
6320
|
|
|
6071
6321
|
// src/commands/memory.ts
|
|
6072
|
-
var
|
|
6322
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
6073
6323
|
function registerMemory(program3) {
|
|
6074
6324
|
const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
|
|
6075
6325
|
memory.command("add <content>").description("Store something worth remembering across future conversations").option("--metadata <json>", "extra metadata for filtering, as a JSON string").option("--id <customId>", "custom id for dedup/updates").action(
|
|
@@ -6093,9 +6343,9 @@ function registerMemory(program3) {
|
|
|
6093
6343
|
});
|
|
6094
6344
|
ok(res, () => {
|
|
6095
6345
|
const results = res?.results ?? res ?? [];
|
|
6096
|
-
if (!results?.length) return line(
|
|
6346
|
+
if (!results?.length) return line(import_picocolors21.default.dim("No matching memories."));
|
|
6097
6347
|
for (const r of results) {
|
|
6098
|
-
line(`${
|
|
6348
|
+
line(`${import_picocolors21.default.dim(r.id ?? "?")} ${r.memory ?? r.content ?? ""}`);
|
|
6099
6349
|
}
|
|
6100
6350
|
});
|
|
6101
6351
|
})
|
|
@@ -6112,7 +6362,7 @@ function registerMemory(program3) {
|
|
|
6112
6362
|
}
|
|
6113
6363
|
|
|
6114
6364
|
// src/commands/business.ts
|
|
6115
|
-
var
|
|
6365
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
6116
6366
|
var RESOURCE_PATHS = {
|
|
6117
6367
|
"business-config": "business-config",
|
|
6118
6368
|
"business-settings": "business-settings",
|
|
@@ -6172,7 +6422,7 @@ function registerBusiness(program3) {
|
|
|
6172
6422
|
const projectId = requireProject(ctx);
|
|
6173
6423
|
const [resource] = args;
|
|
6174
6424
|
const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
|
|
6175
|
-
ok(res, () => line(`Created ${resource} ${
|
|
6425
|
+
ok(res, () => line(`Created ${resource} ${import_picocolors22.default.bold(res?.id ?? "")}.`));
|
|
6176
6426
|
})
|
|
6177
6427
|
);
|
|
6178
6428
|
biz.command("update <resource> <id>").description("Update a record by id (PATCH/PUT \u2014 matches the underlying route)").option("--body <json>", "changed fields as a JSON object string", "{}").action(
|
|
@@ -6214,7 +6464,7 @@ function businessPath(projectId, resource, subpath) {
|
|
|
6214
6464
|
}
|
|
6215
6465
|
|
|
6216
6466
|
// src/commands/artifact.ts
|
|
6217
|
-
var
|
|
6467
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
6218
6468
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
6219
6469
|
import { resolve as resolve2, basename as basename3 } from "path";
|
|
6220
6470
|
var KINDS = [
|
|
@@ -6280,7 +6530,7 @@ function registerArtifact(program3) {
|
|
|
6280
6530
|
ok(
|
|
6281
6531
|
res,
|
|
6282
6532
|
() => success(
|
|
6283
|
-
`Recorded ${
|
|
6533
|
+
`Recorded ${import_picocolors23.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors23.default.dim(` (${res.kind})`) : ""}`
|
|
6284
6534
|
)
|
|
6285
6535
|
);
|
|
6286
6536
|
})
|
|
@@ -6294,11 +6544,11 @@ function registerArtifact(program3) {
|
|
|
6294
6544
|
}
|
|
6295
6545
|
function printRun(run) {
|
|
6296
6546
|
if (!run) return;
|
|
6297
|
-
line(` run ${
|
|
6547
|
+
line(` run ${import_picocolors23.default.bold(run.runId)}`);
|
|
6298
6548
|
if (run.taskId) line(` task ${run.taskId}`);
|
|
6299
6549
|
if (run.conversationId) line(` chat ${run.conversationId}`);
|
|
6300
6550
|
if (run.projectId) line(` project ${run.projectId}`);
|
|
6301
|
-
if (run.cwd) line(` folder ${
|
|
6551
|
+
if (run.cwd) line(` folder ${import_picocolors23.default.dim(run.cwd)}`);
|
|
6302
6552
|
}
|
|
6303
6553
|
|
|
6304
6554
|
// src/commands/image.ts
|
|
@@ -6372,7 +6622,7 @@ async function download(url, output) {
|
|
|
6372
6622
|
}
|
|
6373
6623
|
|
|
6374
6624
|
// src/commands/ask.ts
|
|
6375
|
-
var
|
|
6625
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
6376
6626
|
var TYPES = [
|
|
6377
6627
|
"input",
|
|
6378
6628
|
"choice",
|
|
@@ -6423,7 +6673,7 @@ function registerAsk(program3) {
|
|
|
6423
6673
|
code: "bad_request"
|
|
6424
6674
|
});
|
|
6425
6675
|
}
|
|
6426
|
-
info(
|
|
6676
|
+
info(import_picocolors24.default.dim("Waiting for the user to answer\u2026"));
|
|
6427
6677
|
const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
|
|
6428
6678
|
body: {
|
|
6429
6679
|
type,
|
|
@@ -6449,12 +6699,12 @@ function deriveTitle(message) {
|
|
|
6449
6699
|
function printAnswer(res) {
|
|
6450
6700
|
if (!res) return;
|
|
6451
6701
|
if (res.status === "answered") {
|
|
6452
|
-
line(` ${
|
|
6702
|
+
line(` ${import_picocolors24.default.green("answered")}`);
|
|
6453
6703
|
const value = extract(res.response);
|
|
6454
6704
|
if (value) line(` ${value}`);
|
|
6455
6705
|
return;
|
|
6456
6706
|
}
|
|
6457
|
-
line(` ${
|
|
6707
|
+
line(` ${import_picocolors24.default.yellow(res.status)} ${import_picocolors24.default.dim(res.reason ?? "")}`);
|
|
6458
6708
|
}
|
|
6459
6709
|
function extract(response) {
|
|
6460
6710
|
if (response == null) return "";
|
|
@@ -6473,7 +6723,7 @@ function extract(response) {
|
|
|
6473
6723
|
}
|
|
6474
6724
|
|
|
6475
6725
|
// src/commands/search.ts
|
|
6476
|
-
var
|
|
6726
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
6477
6727
|
function registerSearch(program3) {
|
|
6478
6728
|
program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
|
|
6479
6729
|
action(async ({ ctx, args, opts }) => {
|
|
@@ -6486,9 +6736,9 @@ function registerSearch(program3) {
|
|
|
6486
6736
|
line("");
|
|
6487
6737
|
}
|
|
6488
6738
|
const results = res?.results ?? [];
|
|
6489
|
-
if (!results.length) return line(
|
|
6739
|
+
if (!results.length) return line(import_picocolors25.default.dim("No results."));
|
|
6490
6740
|
for (const r of results) {
|
|
6491
|
-
line(`${r.title ||
|
|
6741
|
+
line(`${r.title || import_picocolors25.default.dim("(untitled)")} ${import_picocolors25.default.dim(r.url)}`);
|
|
6492
6742
|
}
|
|
6493
6743
|
});
|
|
6494
6744
|
})
|
|
@@ -6496,7 +6746,7 @@ function registerSearch(program3) {
|
|
|
6496
6746
|
}
|
|
6497
6747
|
|
|
6498
6748
|
// src/commands/board.ts
|
|
6499
|
-
var
|
|
6749
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
6500
6750
|
|
|
6501
6751
|
// src/commands/record-step.ts
|
|
6502
6752
|
async function recordEntityStep(ctx, opts) {
|
|
@@ -6535,7 +6785,7 @@ function registerBoard(program3) {
|
|
|
6535
6785
|
}
|
|
6536
6786
|
ok(rows, () => {
|
|
6537
6787
|
if (!rows.length) {
|
|
6538
|
-
line(
|
|
6788
|
+
line(import_picocolors26.default.dim("No cards on the Board yet."));
|
|
6539
6789
|
return;
|
|
6540
6790
|
}
|
|
6541
6791
|
for (const r of rows) line(formatRow(r));
|
|
@@ -6550,7 +6800,7 @@ function registerBoard(program3) {
|
|
|
6550
6800
|
`/v1/projects/${projectId}/work-items/${args[0]}`
|
|
6551
6801
|
);
|
|
6552
6802
|
ok(row, () => {
|
|
6553
|
-
line(`${
|
|
6803
|
+
line(`${import_picocolors26.default.bold(row.title)} ${import_picocolors26.default.dim(row.id)}`);
|
|
6554
6804
|
line(`${statusTag(row.status)} priority ${row.priority}`);
|
|
6555
6805
|
if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
|
|
6556
6806
|
if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
|
|
@@ -6591,7 +6841,7 @@ ${row.description}`);
|
|
|
6591
6841
|
refId: row?.id,
|
|
6592
6842
|
output: { workItem: row }
|
|
6593
6843
|
});
|
|
6594
|
-
ok(row, () => line(`Created work item ${
|
|
6844
|
+
ok(row, () => line(`Created work item ${import_picocolors26.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6595
6845
|
})
|
|
6596
6846
|
);
|
|
6597
6847
|
board.command("update <id>").description("Change fields on a card \u2014 pass only what changes").option("--title <text>", "new title").option("--description <text>", "new description").option("--status <value>", STATUSES.join(" | ")).option("--priority <value>", PRIORITIES.join(" | ")).option(
|
|
@@ -6625,7 +6875,7 @@ ${row.description}`);
|
|
|
6625
6875
|
);
|
|
6626
6876
|
}
|
|
6627
6877
|
const row = await patchItem(ctx, projectId, String(args[0]), body);
|
|
6628
|
-
ok(row, () => line(`Updated ${
|
|
6878
|
+
ok(row, () => line(`Updated ${import_picocolors26.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
|
|
6629
6879
|
})
|
|
6630
6880
|
);
|
|
6631
6881
|
board.command("move <id> <status>").description(
|
|
@@ -6636,14 +6886,14 @@ ${row.description}`);
|
|
|
6636
6886
|
const status = String(args[1]);
|
|
6637
6887
|
assertStatus(status);
|
|
6638
6888
|
const row = await patchItem(ctx, projectId, String(args[0]), { status });
|
|
6639
|
-
ok(row, () => line(`Moved ${
|
|
6889
|
+
ok(row, () => line(`Moved ${import_picocolors26.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
|
|
6640
6890
|
})
|
|
6641
6891
|
);
|
|
6642
6892
|
board.command("close <id>").description("Shorthand for `board move <id> done`").action(
|
|
6643
6893
|
action(async ({ ctx, args }) => {
|
|
6644
6894
|
const projectId = requireProject(ctx);
|
|
6645
6895
|
const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
|
|
6646
|
-
ok(row, () => line(`Closed ${
|
|
6896
|
+
ok(row, () => line(`Closed ${import_picocolors26.default.bold(row.title)} ${statusTag(row.status)}`));
|
|
6647
6897
|
})
|
|
6648
6898
|
);
|
|
6649
6899
|
}
|
|
@@ -6676,23 +6926,23 @@ function assertPriority(value) {
|
|
|
6676
6926
|
}
|
|
6677
6927
|
}
|
|
6678
6928
|
function formatRow(r) {
|
|
6679
|
-
const labels = r.labels?.length ?
|
|
6680
|
-
const owner = r.ownerHuman ?
|
|
6681
|
-
return `${
|
|
6929
|
+
const labels = r.labels?.length ? import_picocolors26.default.dim(` [${r.labels.join(", ")}]`) : "";
|
|
6930
|
+
const owner = r.ownerHuman ? import_picocolors26.default.dim(` @${r.ownerHuman}`) : "";
|
|
6931
|
+
return `${import_picocolors26.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
|
|
6682
6932
|
}
|
|
6683
6933
|
function statusTag(status) {
|
|
6684
6934
|
const label = status.padEnd(11);
|
|
6685
|
-
if (status === "done") return
|
|
6686
|
-
if (status === "in-progress") return
|
|
6687
|
-
if (status === "in-review") return
|
|
6688
|
-
return
|
|
6935
|
+
if (status === "done") return import_picocolors26.default.green(label);
|
|
6936
|
+
if (status === "in-progress") return import_picocolors26.default.yellow(label);
|
|
6937
|
+
if (status === "in-review") return import_picocolors26.default.cyan(label);
|
|
6938
|
+
return import_picocolors26.default.dim(label);
|
|
6689
6939
|
}
|
|
6690
6940
|
function collect2(value, previous) {
|
|
6691
6941
|
return [...previous, value];
|
|
6692
6942
|
}
|
|
6693
6943
|
|
|
6694
6944
|
// src/commands/decision.ts
|
|
6695
|
-
var
|
|
6945
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
6696
6946
|
function registerDecision(program3) {
|
|
6697
6947
|
const decision = program3.command("decision").description("Read and record the project's architecture decisions");
|
|
6698
6948
|
decision.command("list").description("Every decision on record \u2014 read this before changing how something works").option("--limit <n>", "cap the number returned (newest first)").action(
|
|
@@ -6705,11 +6955,11 @@ function registerDecision(program3) {
|
|
|
6705
6955
|
rows = applyLimit(rows, opts.limit);
|
|
6706
6956
|
ok(rows, () => {
|
|
6707
6957
|
if (!rows.length) {
|
|
6708
|
-
line(
|
|
6958
|
+
line(import_picocolors27.default.dim("No decisions recorded yet."));
|
|
6709
6959
|
return;
|
|
6710
6960
|
}
|
|
6711
6961
|
for (const r of rows) {
|
|
6712
|
-
line(`${
|
|
6962
|
+
line(`${import_picocolors27.default.dim(r.id)} ${import_picocolors27.default.dim(shortDate(r.createdAt))} ${r.title}`);
|
|
6713
6963
|
line(` ${truncate(r.decision, 100)}`);
|
|
6714
6964
|
}
|
|
6715
6965
|
});
|
|
@@ -6723,16 +6973,16 @@ function registerDecision(program3) {
|
|
|
6723
6973
|
`/v1/projects/${projectId}/architecture-decisions/${args[0]}`
|
|
6724
6974
|
);
|
|
6725
6975
|
ok(row, () => {
|
|
6726
|
-
line(`${
|
|
6727
|
-
line(
|
|
6976
|
+
line(`${import_picocolors27.default.bold(row.title)} ${import_picocolors27.default.dim(row.id)}`);
|
|
6977
|
+
line(import_picocolors27.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
|
|
6728
6978
|
line(`
|
|
6729
|
-
${
|
|
6979
|
+
${import_picocolors27.default.bold("Context")}
|
|
6730
6980
|
${row.context}`);
|
|
6731
6981
|
line(`
|
|
6732
|
-
${
|
|
6982
|
+
${import_picocolors27.default.bold("Decision")}
|
|
6733
6983
|
${row.decision}`);
|
|
6734
6984
|
if (row.consequences) line(`
|
|
6735
|
-
${
|
|
6985
|
+
${import_picocolors27.default.bold("Consequences")}
|
|
6736
6986
|
${row.consequences}`);
|
|
6737
6987
|
});
|
|
6738
6988
|
})
|
|
@@ -6761,7 +7011,7 @@ ${row.consequences}`);
|
|
|
6761
7011
|
refId: row?.id,
|
|
6762
7012
|
output: { decision: row }
|
|
6763
7013
|
});
|
|
6764
|
-
ok(row, () => line(`Recorded decision ${
|
|
7014
|
+
ok(row, () => line(`Recorded decision ${import_picocolors27.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6765
7015
|
})
|
|
6766
7016
|
);
|
|
6767
7017
|
const requirement = program3.command("requirement").description("Read and record the project's requirements");
|
|
@@ -6773,11 +7023,11 @@ ${row.consequences}`);
|
|
|
6773
7023
|
rows = applyLimit(rows, opts.limit);
|
|
6774
7024
|
ok(rows, () => {
|
|
6775
7025
|
if (!rows.length) {
|
|
6776
|
-
line(
|
|
7026
|
+
line(import_picocolors27.default.dim("No requirements recorded yet."));
|
|
6777
7027
|
return;
|
|
6778
7028
|
}
|
|
6779
7029
|
for (const r of rows) {
|
|
6780
|
-
line(`${
|
|
7030
|
+
line(`${import_picocolors27.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
|
|
6781
7031
|
}
|
|
6782
7032
|
});
|
|
6783
7033
|
})
|
|
@@ -6790,8 +7040,8 @@ ${row.consequences}`);
|
|
|
6790
7040
|
`/v1/projects/${projectId}/requirements/${args[0]}`
|
|
6791
7041
|
);
|
|
6792
7042
|
ok(row, () => {
|
|
6793
|
-
line(`${
|
|
6794
|
-
line(
|
|
7043
|
+
line(`${import_picocolors27.default.bold(row.title)} ${import_picocolors27.default.dim(row.id)}`);
|
|
7044
|
+
line(import_picocolors27.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
|
|
6795
7045
|
line(`
|
|
6796
7046
|
${row.body}`);
|
|
6797
7047
|
});
|
|
@@ -6817,7 +7067,7 @@ ${row.body}`);
|
|
|
6817
7067
|
refId: row?.id,
|
|
6818
7068
|
output: { requirement: row }
|
|
6819
7069
|
});
|
|
6820
|
-
ok(row, () => line(`Recorded requirement ${
|
|
7070
|
+
ok(row, () => line(`Recorded requirement ${import_picocolors27.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6821
7071
|
})
|
|
6822
7072
|
);
|
|
6823
7073
|
requirement.command("update <id>").description(
|
|
@@ -6846,7 +7096,7 @@ ${row.body}`);
|
|
|
6846
7096
|
code: "bad_request"
|
|
6847
7097
|
});
|
|
6848
7098
|
}
|
|
6849
|
-
ok(row, () => line(`Updated requirement ${
|
|
7099
|
+
ok(row, () => line(`Updated requirement ${import_picocolors27.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
|
|
6850
7100
|
})
|
|
6851
7101
|
);
|
|
6852
7102
|
}
|
|
@@ -6869,7 +7119,7 @@ function shortDate(iso) {
|
|
|
6869
7119
|
}
|
|
6870
7120
|
|
|
6871
7121
|
// src/commands/doc.ts
|
|
6872
|
-
var
|
|
7122
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
6873
7123
|
function registerDoc(program3) {
|
|
6874
7124
|
const doc = program3.command("doc").description("Read and write project documents");
|
|
6875
7125
|
doc.command("list").description("List the project's documents \u2014 check here before writing a new one").option("--work-item <id>", "the document linked to this card, if there is one").action(
|
|
@@ -6880,13 +7130,13 @@ function registerDoc(program3) {
|
|
|
6880
7130
|
}) ?? [];
|
|
6881
7131
|
ok(rows, () => {
|
|
6882
7132
|
if (!rows.length) {
|
|
6883
|
-
line(
|
|
7133
|
+
line(import_picocolors28.default.dim("No documents yet."));
|
|
6884
7134
|
return;
|
|
6885
7135
|
}
|
|
6886
7136
|
for (const r of rows) {
|
|
6887
|
-
const link = r.workItemId ?
|
|
6888
|
-
const file = r.filePath ?
|
|
6889
|
-
line(`${
|
|
7137
|
+
const link = r.workItemId ? import_picocolors28.default.dim(` \u21B3 ${r.workItemId}`) : "";
|
|
7138
|
+
const file = r.filePath ? import_picocolors28.default.dim(` ${r.filePath}`) : "";
|
|
7139
|
+
line(`${import_picocolors28.default.dim(r.id)} ${r.title}${link}${file}`);
|
|
6890
7140
|
}
|
|
6891
7141
|
});
|
|
6892
7142
|
})
|
|
@@ -6900,17 +7150,17 @@ function registerDoc(program3) {
|
|
|
6900
7150
|
);
|
|
6901
7151
|
if (opts.markdown) {
|
|
6902
7152
|
ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
|
|
6903
|
-
line(`${
|
|
7153
|
+
line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.id)}`);
|
|
6904
7154
|
line(
|
|
6905
|
-
row.filePath ? `Read it at ${
|
|
7155
|
+
row.filePath ? `Read it at ${import_picocolors28.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors28.default.dim("This document has no markdown mirror on disk yet.")
|
|
6906
7156
|
);
|
|
6907
7157
|
});
|
|
6908
7158
|
return;
|
|
6909
7159
|
}
|
|
6910
7160
|
ok(row, () => {
|
|
6911
|
-
line(`${
|
|
6912
|
-
if (row.workItemId) line(
|
|
6913
|
-
if (row.filePath) line(
|
|
7161
|
+
line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.id)}`);
|
|
7162
|
+
if (row.workItemId) line(import_picocolors28.default.dim(`linked to work item ${row.workItemId}`));
|
|
7163
|
+
if (row.filePath) line(import_picocolors28.default.dim(`markdown mirror: ${row.filePath}`));
|
|
6914
7164
|
line("");
|
|
6915
7165
|
line(row.contentJson);
|
|
6916
7166
|
});
|
|
@@ -6939,7 +7189,7 @@ function registerDoc(program3) {
|
|
|
6939
7189
|
refId: row?.id,
|
|
6940
7190
|
output: { document: row }
|
|
6941
7191
|
});
|
|
6942
|
-
ok(row, () => line(`Created document ${
|
|
7192
|
+
ok(row, () => line(`Created document ${import_picocolors28.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6943
7193
|
})
|
|
6944
7194
|
);
|
|
6945
7195
|
doc.command("update <id>").description("Revise an existing document rather than creating a second copy of it").option("--title <text>", "new title").option("--markdown <text>", "replace the body with this markdown").option("--content-json <json>", "replace the body with this rich-text content JSON").action(
|
|
@@ -6966,13 +7216,13 @@ function registerDoc(program3) {
|
|
|
6966
7216
|
code: "bad_request"
|
|
6967
7217
|
});
|
|
6968
7218
|
}
|
|
6969
|
-
ok(row, () => line(`Updated document ${
|
|
7219
|
+
ok(row, () => line(`Updated document ${import_picocolors28.default.bold(row.id)} \u2014 ${row.title}`));
|
|
6970
7220
|
})
|
|
6971
7221
|
);
|
|
6972
7222
|
}
|
|
6973
7223
|
|
|
6974
7224
|
// src/commands/design.ts
|
|
6975
|
-
var
|
|
7225
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
6976
7226
|
function registerDesign(program3) {
|
|
6977
7227
|
const design = program3.command("design").description("Read the project's brand (colours, fonts, logo)");
|
|
6978
7228
|
design.command("show").description("Show this project's brand \u2014 read it before writing any UI").option("--raw", "print the generated token files verbatim instead of a summary").action(
|
|
@@ -6986,11 +7236,11 @@ function registerDesign(program3) {
|
|
|
6986
7236
|
if (opts.raw) {
|
|
6987
7237
|
ok(files, () => {
|
|
6988
7238
|
if (!files.length) {
|
|
6989
|
-
line(
|
|
7239
|
+
line(import_picocolors29.default.dim("No brand set for this project."));
|
|
6990
7240
|
return;
|
|
6991
7241
|
}
|
|
6992
7242
|
for (const f of files) {
|
|
6993
|
-
line(
|
|
7243
|
+
line(import_picocolors29.default.bold(f.path));
|
|
6994
7244
|
line(f.contents);
|
|
6995
7245
|
line("");
|
|
6996
7246
|
}
|
|
@@ -7007,21 +7257,21 @@ function registerDesign(program3) {
|
|
|
7007
7257
|
} : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
|
|
7008
7258
|
ok(summary, () => {
|
|
7009
7259
|
if (!tokens) {
|
|
7010
|
-
line(
|
|
7260
|
+
line(import_picocolors29.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
|
|
7011
7261
|
return;
|
|
7012
7262
|
}
|
|
7013
7263
|
for (const [name, value] of Object.entries(tokens.brand)) {
|
|
7014
|
-
line(`${
|
|
7264
|
+
line(`${import_picocolors29.default.dim(name.padEnd(12))} ${value}`);
|
|
7015
7265
|
}
|
|
7016
7266
|
for (const [name, value] of Object.entries(tokens.color)) {
|
|
7017
|
-
line(`${
|
|
7267
|
+
line(`${import_picocolors29.default.dim(`color.${name}`.padEnd(12))} ${value}`);
|
|
7018
7268
|
}
|
|
7019
7269
|
for (const [name, value] of Object.entries(tokens.font)) {
|
|
7020
|
-
line(`${
|
|
7270
|
+
line(`${import_picocolors29.default.dim(`font.${name}`.padEnd(12))} ${value}`);
|
|
7021
7271
|
}
|
|
7022
7272
|
line("");
|
|
7023
7273
|
line(
|
|
7024
|
-
|
|
7274
|
+
import_picocolors29.default.dim(
|
|
7025
7275
|
`Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
|
|
7026
7276
|
)
|
|
7027
7277
|
);
|
|
@@ -7053,7 +7303,7 @@ function unwrap(group) {
|
|
|
7053
7303
|
|
|
7054
7304
|
// src/index.ts
|
|
7055
7305
|
var pkg = {
|
|
7056
|
-
version: true ? "0.2.
|
|
7306
|
+
version: true ? "0.2.7" : "0.0.0-dev"
|
|
7057
7307
|
};
|
|
7058
7308
|
var program2 = new Command();
|
|
7059
7309
|
program2.name("workser").description(
|
|
@@ -7086,6 +7336,8 @@ registerOpen(program2);
|
|
|
7086
7336
|
registerDoctor(program2);
|
|
7087
7337
|
registerAgent(program2);
|
|
7088
7338
|
registerVerify(program2);
|
|
7339
|
+
registerCheckpoint(program2);
|
|
7340
|
+
registerSync(program2);
|
|
7089
7341
|
registerWorkflow(program2);
|
|
7090
7342
|
registerApp(program2);
|
|
7091
7343
|
registerTool(program2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workser/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Workser CLI — give your local AI agent native DevOps & infrastructure on Workser. The agent runs `workser …` to provision, deploy, and manage real apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/skills/workser/SKILL.md
CHANGED
|
@@ -5,19 +5,16 @@ description: Provision, deploy, and operate the current Workser app. Use when th
|
|
|
5
5
|
|
|
6
6
|
# Workser — ship & run the current app from the terminal
|
|
7
7
|
|
|
8
|
-
You have the `workser` CLI.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(which handles auth and shows the user what you're doing).
|
|
13
|
-
|
|
14
|
-
For writing code, keep using your normal tools. Use `workser` for the last mile.
|
|
8
|
+
You have the `workser` CLI. Use it to operate the **one project** Workser Orbit has
|
|
9
|
+
linked to this directory — infrastructure, config, shipping — **on the user's own
|
|
10
|
+
account**, through the Orbit app, which handles auth and shows them what you're
|
|
11
|
+
doing. For writing code, keep using your normal tools.
|
|
15
12
|
|
|
16
13
|
## Read one guide, not all of them
|
|
17
14
|
|
|
18
|
-
This page is the index.
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
This page is the index. `workser help <topic>` prints one focused guide, about a
|
|
16
|
+
screen long. Find your row, run that **one** command — you pay for every line you
|
|
17
|
+
load.
|
|
21
18
|
|
|
22
19
|
| You need to… | Commands | Run |
|
|
23
20
|
| --- | --- | --- |
|
|
@@ -25,6 +22,7 @@ command. Don't print them all; you are paying for every line you load.
|
|
|
25
22
|
| Follow the project's brand — colours, fonts, logo | `design …` | `workser help brand` |
|
|
26
23
|
| Provision or query Postgres; list end users | `db …`, `auth …` | `workser help database` |
|
|
27
24
|
| Deploy, set env vars, read logs, check a domain | `deploy`, `env …`, `logs`, `versions`, `domain`, `open` | `workser help deploy` |
|
|
25
|
+
| Save work before a risky change, undo it, sync this folder | `checkpoint`, `restore`, `sync` | `workser help version-control` |
|
|
28
26
|
| Put files in the project's bucket | `storage …` | `workser help storage` |
|
|
29
27
|
| Read or write products, orders, customers, deals | `business …` | `workser help business` |
|
|
30
28
|
| Use the project's own Neon buckets or functions | `neon …` | `workser help neon` |
|
|
@@ -35,9 +33,8 @@ command. Don't print them all; you are paying for every line you load.
|
|
|
35
33
|
| Record finished output, or ask the user a question | `artifact …`, `ask` | `workser help deliverables` |
|
|
36
34
|
| Control this machine — files, shell, screen, browser | `tool …` | `workser help computer-use` |
|
|
37
35
|
|
|
38
|
-
`workser help`
|
|
39
|
-
|
|
40
|
-
`workser <command> --help` is generated from the implementation.
|
|
36
|
+
`workser help` lists them all; `workser <command> --help` gives exact flags. Both
|
|
37
|
+
come from the CLI itself, so they match the version you are running.
|
|
41
38
|
|
|
42
39
|
## Orientation (no guide needed)
|
|
43
40
|
|
|
@@ -123,6 +120,8 @@ workser board close <id> --json # 9. the Board now matches reality
|
|
|
123
120
|
- `no_project` → no project is linked here; the user links it in Orbit.
|
|
124
121
|
- `owner_only` → an owner action; tell the user to do it in Orbit, then continue.
|
|
125
122
|
- `awaiting_approval` → user must approve in Orbit; then retry.
|
|
123
|
+
- `needs_local_app` → this machine has no Workser app, so folder commands
|
|
124
|
+
can't run. Say so; don't reach for `git` instead.
|
|
126
125
|
|
|
127
126
|
Keep the user informed in plain language ("Provisioned a database and deployed —
|
|
128
127
|
it's live at <url>"), not raw JSON.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
topic: version-control
|
|
3
|
+
title: Saving, undoing & syncing your work
|
|
4
|
+
summary: Checkpoint before risky edits, restore when they go wrong, sync with Workser.
|
|
5
|
+
commands: [checkpoint, restore, sync]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Saving, undoing & syncing your work
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
workser checkpoint ["what you're about to try"] # save the folder as it stands now
|
|
12
|
+
workser restore # go back to the newest checkpoint
|
|
13
|
+
workser restore --list # see the checkpoints you can go back to
|
|
14
|
+
workser restore <ref> # go back to a specific one
|
|
15
|
+
workser sync [--branch dev] [--app <id>] # reconcile this folder with Workser
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Why these exist instead of git
|
|
19
|
+
|
|
20
|
+
This folder is a git repository, but **the history is Workser's**. There is no
|
|
21
|
+
remote: code moves to and from Workser over its API as git bundles, using the
|
|
22
|
+
same session everything else here uses. No credential is read, and the user's
|
|
23
|
+
own git keys and identity are never touched.
|
|
24
|
+
|
|
25
|
+
That is what makes any of this work on a computer with no access to the
|
|
26
|
+
Workser-managed repository — which is every computer. Nobody has repo access;
|
|
27
|
+
the API does.
|
|
28
|
+
|
|
29
|
+
So the ordinary git verbs are wrong here, and these three replace them:
|
|
30
|
+
|
|
31
|
+
| Instead of | Run |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `git stash` | `workser checkpoint` |
|
|
34
|
+
| `git commit` | `workser checkpoint` |
|
|
35
|
+
| `git reset --hard` / `git checkout .` | `workser restore` |
|
|
36
|
+
| `git pull` / `git push` | `workser sync` |
|
|
37
|
+
|
|
38
|
+
Reading is always fine: `git log`, `git diff`, `git show`, `git status`.
|
|
39
|
+
|
|
40
|
+
## Notes that matter
|
|
41
|
+
|
|
42
|
+
- **Checkpoint before anything you might want to take back** — a refactor across
|
|
43
|
+
many files, a dependency upgrade, deleting something large. It costs a second.
|
|
44
|
+
- **`git stash` is the one that actually loses work.** Workser publishes what is
|
|
45
|
+
on disk, so a deploy while your changes are stashed ships the version *without
|
|
46
|
+
them* — and if the run is stopped between `stash` and `stash pop`, the user
|
|
47
|
+
opens their folder and their work is gone. `workser checkpoint` has neither
|
|
48
|
+
failure mode.
|
|
49
|
+
- **Restoring never discards anything.** The current state is saved first, then
|
|
50
|
+
the older state is restored on top. If you restore the wrong thing, restore
|
|
51
|
+
again. Say this to the user — it is the reason they can afford to say yes.
|
|
52
|
+
- **These are the same checkpoints as the app's Undo button.** One history, not
|
|
53
|
+
two: a checkpoint you take here appears in Workser, and vice versa.
|
|
54
|
+
- **`sync` pulls before it pushes.** If it reports `diverged`, both sides changed
|
|
55
|
+
and it stops rather than picking a winner — tell the user to resolve it in
|
|
56
|
+
Workser instead of forcing it.
|
|
57
|
+
- **These three need the Workser app running on this computer**, because they
|
|
58
|
+
work with files. If you get `needs_local_app`, you are on a machine that only
|
|
59
|
+
has the CLI and a token — say so rather than looking for a workaround. Every
|
|
60
|
+
read-only command (`projects`, `env`, `db`, `logs`, `status`) still works.
|
|
61
|
+
|
|
62
|
+
## Shipping is separate
|
|
63
|
+
|
|
64
|
+
`workser sync` reconciles the folder. `workser deploy` puts it in front of
|
|
65
|
+
users. Syncing does not deploy, and deploying does not require you to sync
|
|
66
|
+
first — deploy publishes what is on disk. See `reference/deploy.md`.
|