@workser/cli 0.2.6 → 0.3.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/dist/index.js +703 -86
- package/package.json +1 -1
- package/skills/workser/SKILL.md +12 -13
- package/skills/workser/reference/tasks.md +82 -0
- package/skills/workser/reference/version-control.md +66 -0
package/dist/index.js
CHANGED
|
@@ -4404,6 +4404,156 @@ 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: "tasks",
|
|
4411
|
+
title: "Project tasks & subtasks",
|
|
4412
|
+
summary: "The ticket you are working inside: read it, break it into steps, and ask before starting.",
|
|
4413
|
+
commands: ["task"],
|
|
4414
|
+
source: "skills/workser/reference/tasks.md",
|
|
4415
|
+
body: `# Project tasks & subtasks
|
|
4416
|
+
|
|
4417
|
+
A **project task** is a ticket the owner filed. You are usually running inside
|
|
4418
|
+
one \u2014 Orbit sets \`WORKSER_PROJECT_TASK_ID\` on your process, so every command
|
|
4419
|
+
below defaults to it and you rarely pass an id at all.
|
|
4420
|
+
|
|
4421
|
+
\`\`\`
|
|
4422
|
+
workser task list [--status <value>] [--label <value>] [--limit <n>]
|
|
4423
|
+
workser task show [id] # the task you are in, with its steps
|
|
4424
|
+
|
|
4425
|
+
workser task subtask add <title> [--role <value>] [--kind <value>]
|
|
4426
|
+
[--note <text>] [--app <id...>]
|
|
4427
|
+
[--infra <ref...>] [--scope <path...>]
|
|
4428
|
+
[--depends-on <key...>]
|
|
4429
|
+
workser task subtask list [taskId]
|
|
4430
|
+
workser task subtask update <id> [--title|--note|--role|--kind|--scope]
|
|
4431
|
+
workser task subtask remove <id>
|
|
4432
|
+
|
|
4433
|
+
workser task can-start [id] # may work begin? refuses until approved
|
|
4434
|
+
workser task approval request # tell the owner the plan is ready
|
|
4435
|
+
workser task move <id> <status>
|
|
4436
|
+
workser task done [id] --summary <text>
|
|
4437
|
+
\`\`\`
|
|
4438
|
+
|
|
4439
|
+
## This is not \`workser board\`
|
|
4440
|
+
|
|
4441
|
+
\`board\` is the Orbit Board \u2014 a human's list of work items. \`task\` is the AI Tech
|
|
4442
|
+
Team's own table. Filing your plan on the Board puts it somewhere the owner's
|
|
4443
|
+
task page never reads: they see "created work item" and an empty plan. Use
|
|
4444
|
+
\`task subtask add\`.
|
|
4445
|
+
|
|
4446
|
+
## Planning a task
|
|
4447
|
+
|
|
4448
|
+
Read the project first, then propose. One \`subtask add\` per step:
|
|
4449
|
+
|
|
4450
|
+
\`\`\`
|
|
4451
|
+
workser task subtask add "Build the upload endpoint" \\
|
|
4452
|
+
--role api --kind service \\
|
|
4453
|
+
--note "Accept a file, work out its type, hand it to the right analyzer." \\
|
|
4454
|
+
--scope src/app/api/analyze/route.ts
|
|
4455
|
+
|
|
4456
|
+
workser task subtask add "Check every supported file type end to end" \\
|
|
4457
|
+
--role qa --depends-on RIZZ-15
|
|
4458
|
+
\`\`\`
|
|
4459
|
+
|
|
4460
|
+
\`--role\` is one of: pm, architect, web, api, automation, qa.
|
|
4461
|
+
\`--kind\` is what the step produces: data_reports, web, mobile, service,
|
|
4462
|
+
automation, docs. It is not the same fact as the role \u2014 the same engineer
|
|
4463
|
+
writing a screen, the docs for it and the service behind it is three kinds of
|
|
4464
|
+
work.
|
|
4465
|
+
|
|
4466
|
+
\`--scope\` is what that step OWNS. Two steps naming the same file cannot run at
|
|
4467
|
+
the same time, so keeping scopes apart is what lets the team work in parallel.
|
|
4468
|
+
\`--depends-on\` takes the keys you read off \`task show\`.
|
|
4469
|
+
|
|
4470
|
+
Between three and six steps. If it needs more, say the task is too big instead.
|
|
4471
|
+
The step that CHECKS work must not be the same role as the one that built it.
|
|
4472
|
+
|
|
4473
|
+
## Nothing runs until the owner approves
|
|
4474
|
+
|
|
4475
|
+
\`\`\`
|
|
4476
|
+
workser task can-start
|
|
4477
|
+
\`\`\`
|
|
4478
|
+
|
|
4479
|
+
This exits non-zero, with the reason, until they have approved the plan \u2014 that
|
|
4480
|
+
refusal is the product working, not an error to route around. Ask with
|
|
4481
|
+
\`workser task approval request\`; only a person can answer.
|
|
4482
|
+
|
|
4483
|
+
## Finishing a step
|
|
4484
|
+
|
|
4485
|
+
\`\`\`
|
|
4486
|
+
workser task done --summary "The report now shows cost per KOL, with six months of history."
|
|
4487
|
+
\`\`\`
|
|
4488
|
+
|
|
4489
|
+
Write the summary for someone who runs a business and does not read code.
|
|
4490
|
+
`
|
|
4491
|
+
},
|
|
4492
|
+
{
|
|
4493
|
+
topic: "version-control",
|
|
4494
|
+
title: "Saving, undoing & syncing your work",
|
|
4495
|
+
summary: "Checkpoint before risky edits, restore when they go wrong, sync with Workser.",
|
|
4496
|
+
commands: ["checkpoint", "restore", "sync"],
|
|
4497
|
+
source: "skills/workser/reference/version-control.md",
|
|
4498
|
+
body: `# Saving, undoing & syncing your work
|
|
4499
|
+
|
|
4500
|
+
\`\`\`
|
|
4501
|
+
workser checkpoint ["what you're about to try"] # save the folder as it stands now
|
|
4502
|
+
workser restore # go back to the newest checkpoint
|
|
4503
|
+
workser restore --list # see the checkpoints you can go back to
|
|
4504
|
+
workser restore <ref> # go back to a specific one
|
|
4505
|
+
workser sync [--branch dev] [--app <id>] # reconcile this folder with Workser
|
|
4506
|
+
\`\`\`
|
|
4507
|
+
|
|
4508
|
+
## Why these exist instead of git
|
|
4509
|
+
|
|
4510
|
+
This folder is a git repository, but **the history is Workser's**. There is no
|
|
4511
|
+
remote: code moves to and from Workser over its API as git bundles, using the
|
|
4512
|
+
same session everything else here uses. No credential is read, and the user's
|
|
4513
|
+
own git keys and identity are never touched.
|
|
4514
|
+
|
|
4515
|
+
That is what makes any of this work on a computer with no access to the
|
|
4516
|
+
Workser-managed repository \u2014 which is every computer. Nobody has repo access;
|
|
4517
|
+
the API does.
|
|
4518
|
+
|
|
4519
|
+
So the ordinary git verbs are wrong here, and these three replace them:
|
|
4520
|
+
|
|
4521
|
+
| Instead of | Run |
|
|
4522
|
+
|---|---|
|
|
4523
|
+
| \`git stash\` | \`workser checkpoint\` |
|
|
4524
|
+
| \`git commit\` | \`workser checkpoint\` |
|
|
4525
|
+
| \`git reset --hard\` / \`git checkout .\` | \`workser restore\` |
|
|
4526
|
+
| \`git pull\` / \`git push\` | \`workser sync\` |
|
|
4527
|
+
|
|
4528
|
+
Reading is always fine: \`git log\`, \`git diff\`, \`git show\`, \`git status\`.
|
|
4529
|
+
|
|
4530
|
+
## Notes that matter
|
|
4531
|
+
|
|
4532
|
+
- **Checkpoint before anything you might want to take back** \u2014 a refactor across
|
|
4533
|
+
many files, a dependency upgrade, deleting something large. It costs a second.
|
|
4534
|
+
- **\`git stash\` is the one that actually loses work.** Workser publishes what is
|
|
4535
|
+
on disk, so a deploy while your changes are stashed ships the version *without
|
|
4536
|
+
them* \u2014 and if the run is stopped between \`stash\` and \`stash pop\`, the user
|
|
4537
|
+
opens their folder and their work is gone. \`workser checkpoint\` has neither
|
|
4538
|
+
failure mode.
|
|
4539
|
+
- **Restoring never discards anything.** The current state is saved first, then
|
|
4540
|
+
the older state is restored on top. If you restore the wrong thing, restore
|
|
4541
|
+
again. Say this to the user \u2014 it is the reason they can afford to say yes.
|
|
4542
|
+
- **These are the same checkpoints as the app's Undo button.** One history, not
|
|
4543
|
+
two: a checkpoint you take here appears in Workser, and vice versa.
|
|
4544
|
+
- **\`sync\` pulls before it pushes.** If it reports \`diverged\`, both sides changed
|
|
4545
|
+
and it stops rather than picking a winner \u2014 tell the user to resolve it in
|
|
4546
|
+
Workser instead of forcing it.
|
|
4547
|
+
- **These three need the Workser app running on this computer**, because they
|
|
4548
|
+
work with files. If you get \`needs_local_app\`, you are on a machine that only
|
|
4549
|
+
has the CLI and a token \u2014 say so rather than looking for a workaround. Every
|
|
4550
|
+
read-only command (\`projects\`, \`env\`, \`db\`, \`logs\`, \`status\`) still works.
|
|
4551
|
+
|
|
4552
|
+
## Shipping is separate
|
|
4553
|
+
|
|
4554
|
+
\`workser sync\` reconciles the folder. \`workser deploy\` puts it in front of
|
|
4555
|
+
users. Syncing does not deploy, and deploying does not require you to sync
|
|
4556
|
+
first \u2014 deploy publishes what is on disk. See \`reference/deploy.md\`.
|
|
4407
4557
|
`
|
|
4408
4558
|
}
|
|
4409
4559
|
];
|
|
@@ -4565,11 +4715,37 @@ function buildContext(opts) {
|
|
|
4565
4715
|
const projectId = opts.project || process.env.WORKSER_PROJECT_ID || link?.projectId || session.defaultProjectId;
|
|
4566
4716
|
const runId = process.env.WORKSER_RUN_ID || void 0;
|
|
4567
4717
|
const conversationId = process.env.WORKSER_CONVERSATION_ID || void 0;
|
|
4568
|
-
|
|
4718
|
+
const projectTaskId = process.env.WORKSER_PROJECT_TASK_ID || void 0;
|
|
4719
|
+
return {
|
|
4720
|
+
endpoint,
|
|
4721
|
+
socketPath,
|
|
4722
|
+
token,
|
|
4723
|
+
mode: mode2,
|
|
4724
|
+
cwd,
|
|
4725
|
+
projectId,
|
|
4726
|
+
runId,
|
|
4727
|
+
conversationId,
|
|
4728
|
+
projectTaskId
|
|
4729
|
+
};
|
|
4569
4730
|
}
|
|
4570
4731
|
function runTarget(ctx) {
|
|
4571
4732
|
return ctx.runId || "current";
|
|
4572
4733
|
}
|
|
4734
|
+
function requireLocalApp(ctx, what) {
|
|
4735
|
+
if (ctx.mode === "daemon") return;
|
|
4736
|
+
throw new WorkserError(
|
|
4737
|
+
`\`workser ${what}\` works with the code in this folder, so it needs the Workser app running on this computer.
|
|
4738
|
+
|
|
4739
|
+
This shell is talking to ${ctx.endpoint} instead of a local app.
|
|
4740
|
+
|
|
4741
|
+
\u2022 On your own computer: open Workser and try again.
|
|
4742
|
+
\u2022 On a computer without Workser: install it from https://workser.ai/download,
|
|
4743
|
+
sign in, and open this project \u2014 that is what puts the code here.
|
|
4744
|
+
|
|
4745
|
+
Commands that only read your account (projects, env, db, logs, status) work as normal.`,
|
|
4746
|
+
{ code: "needs_local_app" }
|
|
4747
|
+
);
|
|
4748
|
+
}
|
|
4573
4749
|
function requireProject(ctx) {
|
|
4574
4750
|
if (!ctx.projectId) {
|
|
4575
4751
|
throw new WorkserError(
|
|
@@ -5521,6 +5697,7 @@ function registerDeploy(program3) {
|
|
|
5521
5697
|
"which app to publish (default: the app this folder is linked to)"
|
|
5522
5698
|
).action(
|
|
5523
5699
|
action(async ({ ctx, opts }) => {
|
|
5700
|
+
requireLocalApp(ctx, "deploy");
|
|
5524
5701
|
const projectId = requireProject(ctx);
|
|
5525
5702
|
const dep = await api(ctx, `/v1/projects/${projectId}/deploy`, {
|
|
5526
5703
|
body: {
|
|
@@ -5675,6 +5852,7 @@ function openUrl(url) {
|
|
|
5675
5852
|
|
|
5676
5853
|
// src/commands/doctor.ts
|
|
5677
5854
|
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
5855
|
+
import { spawnSync } from "child_process";
|
|
5678
5856
|
function registerDoctor(program3) {
|
|
5679
5857
|
program3.command("doctor").description("Print the resolved endpoint, mode, token presence (masked), and current project").action(
|
|
5680
5858
|
action(({ ctx, opts }) => {
|
|
@@ -5685,12 +5863,14 @@ function registerDoctor(program3) {
|
|
|
5685
5863
|
const tokenSource = opts.token ? "--token" : process.env.WORKSER_TOKEN ? "$WORKSER_TOKEN" : session.token ? "session" : void 0;
|
|
5686
5864
|
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
5865
|
const projectSource = opts.project ? "--project" : link?.projectId ? ".workser link" : session.defaultProjectId ? "session" : void 0;
|
|
5866
|
+
const git = gitVersion();
|
|
5688
5867
|
const report = {
|
|
5689
5868
|
endpoint: ctx.endpoint,
|
|
5690
5869
|
endpointSource,
|
|
5691
5870
|
env,
|
|
5692
5871
|
envIgnored,
|
|
5693
5872
|
mode: ctx.mode,
|
|
5873
|
+
git: { present: git !== null, version: git },
|
|
5694
5874
|
token: {
|
|
5695
5875
|
present: Boolean(ctx.token),
|
|
5696
5876
|
masked: ctx.token ? maskToken(ctx.token) : null,
|
|
@@ -5718,6 +5898,20 @@ function registerDoctor(program3) {
|
|
|
5718
5898
|
` project: ${ctx.projectId ?? import_picocolors13.default.dim("none")}` + (link?.name ? ` ${import_picocolors13.default.dim(`(${link.name})`)}` : "") + (projectSource ? import_picocolors13.default.dim(` [${projectSource}]`) : "")
|
|
5719
5899
|
);
|
|
5720
5900
|
line(` cwd: ${ctx.cwd}`);
|
|
5901
|
+
line(` git: ${git ?? import_picocolors13.default.dim("not on this shell's PATH")}`);
|
|
5902
|
+
if (!git) {
|
|
5903
|
+
line("");
|
|
5904
|
+
line(
|
|
5905
|
+
import_picocolors13.default.dim(
|
|
5906
|
+
" Workser brings its own git, so syncing and publishing still work."
|
|
5907
|
+
)
|
|
5908
|
+
);
|
|
5909
|
+
line(
|
|
5910
|
+
import_picocolors13.default.dim(
|
|
5911
|
+
` Only needed if you want to run git yourself here. ${GIT_INSTALL_HINT}`
|
|
5912
|
+
)
|
|
5913
|
+
);
|
|
5914
|
+
}
|
|
5721
5915
|
if (envIgnored) {
|
|
5722
5916
|
line("");
|
|
5723
5917
|
line(
|
|
@@ -5736,6 +5930,24 @@ function registerDoctor(program3) {
|
|
|
5736
5930
|
})
|
|
5737
5931
|
);
|
|
5738
5932
|
}
|
|
5933
|
+
function gitVersion() {
|
|
5934
|
+
try {
|
|
5935
|
+
const res = spawnSync("git", ["--version"], {
|
|
5936
|
+
encoding: "utf8",
|
|
5937
|
+
timeout: 5e3,
|
|
5938
|
+
// A macOS box with no Command Line Tools has a `git` SHIM that pops a GUI
|
|
5939
|
+
// installer when run. Inheriting stdio would surface that dialog from a
|
|
5940
|
+
// command the user expects to just print a report.
|
|
5941
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5942
|
+
});
|
|
5943
|
+
if (res.error || res.status !== 0) return null;
|
|
5944
|
+
const out = (res.stdout || "").toString().trim();
|
|
5945
|
+
return out || null;
|
|
5946
|
+
} catch {
|
|
5947
|
+
return null;
|
|
5948
|
+
}
|
|
5949
|
+
}
|
|
5950
|
+
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
5951
|
function maskToken(token) {
|
|
5740
5952
|
if (token.length <= 8) return "\u2022".repeat(token.length);
|
|
5741
5953
|
return `${token.slice(0, 3)}${"\u2022".repeat(Math.min(token.length - 6, 12))}${token.slice(-3)}`;
|
|
@@ -5863,6 +6075,7 @@ function registerVerify(program3) {
|
|
|
5863
6075
|
"comma-separated subset, e.g. --only typecheck,build"
|
|
5864
6076
|
).action(
|
|
5865
6077
|
action(async ({ ctx, opts }) => {
|
|
6078
|
+
requireLocalApp(ctx, "verify");
|
|
5866
6079
|
const only = opts.only ? String(opts.only).split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
5867
6080
|
const res = await api(ctx, `/v1/verify`, {
|
|
5868
6081
|
body: { cwd: ctx.cwd, ...only ? { only } : {} }
|
|
@@ -5890,8 +6103,139 @@ function printVerify(res) {
|
|
|
5890
6103
|
);
|
|
5891
6104
|
}
|
|
5892
6105
|
|
|
5893
|
-
// src/commands/
|
|
6106
|
+
// src/commands/checkpoint.ts
|
|
5894
6107
|
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
6108
|
+
function registerCheckpoint(program3) {
|
|
6109
|
+
program3.command("checkpoint [label]").description(
|
|
6110
|
+
"Save the current state of this folder so you can come back to it"
|
|
6111
|
+
).addHelpText(
|
|
6112
|
+
"after",
|
|
6113
|
+
"\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"
|
|
6114
|
+
).action(
|
|
6115
|
+
action(async ({ ctx, args }) => {
|
|
6116
|
+
requireLocalApp(ctx, "checkpoint");
|
|
6117
|
+
const label = typeof args[0] === "string" ? args[0] : "";
|
|
6118
|
+
const res = await api(ctx, `/v1/checkpoints`, {
|
|
6119
|
+
body: { cwd: ctx.cwd, ...label ? { label } : {} }
|
|
6120
|
+
});
|
|
6121
|
+
ok(res, () => {
|
|
6122
|
+
const p = res?.point;
|
|
6123
|
+
success(`Saved a checkpoint${p?.label ? `: ${p.label}` : ""}`);
|
|
6124
|
+
if (p?.ref) line(import_picocolors16.default.dim(` ${p.ref.slice(0, 7)}`));
|
|
6125
|
+
line(import_picocolors16.default.dim(" Come back to it with `workser restore`."));
|
|
6126
|
+
});
|
|
6127
|
+
})
|
|
6128
|
+
);
|
|
6129
|
+
const restore = program3.command("restore [ref]").description(
|
|
6130
|
+
"Put this folder back to a saved checkpoint (default: the most recent)"
|
|
6131
|
+
).option("--list", "show the checkpoints you can go back to", false).action(
|
|
6132
|
+
action(async ({ ctx, args, opts }) => {
|
|
6133
|
+
requireLocalApp(ctx, "restore");
|
|
6134
|
+
if (opts.list) {
|
|
6135
|
+
const res2 = await api(ctx, `/v1/checkpoints`, {
|
|
6136
|
+
query: { cwd: ctx.cwd }
|
|
6137
|
+
});
|
|
6138
|
+
const points = res2?.points ?? [];
|
|
6139
|
+
return ok(res2, () => printPoints(points));
|
|
6140
|
+
}
|
|
6141
|
+
const ref = typeof args[0] === "string" ? args[0].trim() : "";
|
|
6142
|
+
const res = await api(ctx, `/v1/undo`, {
|
|
6143
|
+
body: { cwd: ctx.cwd, ...ref ? { ref } : {} }
|
|
6144
|
+
});
|
|
6145
|
+
ok(res, () => {
|
|
6146
|
+
success(
|
|
6147
|
+
`Put the folder back${res?.restoredTo ? ` to ${String(res.restoredTo).slice(0, 7)}` : ""}`
|
|
6148
|
+
);
|
|
6149
|
+
if (res?.filesChanged) {
|
|
6150
|
+
line(
|
|
6151
|
+
import_picocolors16.default.dim(
|
|
6152
|
+
` ${res.filesChanged} file${res.filesChanged === 1 ? "" : "s"} changed`
|
|
6153
|
+
)
|
|
6154
|
+
);
|
|
6155
|
+
}
|
|
6156
|
+
line(import_picocolors16.default.dim(" This is reversible: `workser restore` again."));
|
|
6157
|
+
});
|
|
6158
|
+
})
|
|
6159
|
+
);
|
|
6160
|
+
restore.command("list").description("Show the checkpoints you can go back to").action(
|
|
6161
|
+
action(async ({ ctx }) => {
|
|
6162
|
+
requireLocalApp(ctx, "restore list");
|
|
6163
|
+
const res = await api(ctx, `/v1/checkpoints`, {
|
|
6164
|
+
query: { cwd: ctx.cwd }
|
|
6165
|
+
});
|
|
6166
|
+
ok(res, () => printPoints(res?.points ?? []));
|
|
6167
|
+
})
|
|
6168
|
+
);
|
|
6169
|
+
}
|
|
6170
|
+
function printPoints(points) {
|
|
6171
|
+
if (!points.length) {
|
|
6172
|
+
info("No checkpoints yet for this folder.");
|
|
6173
|
+
line(import_picocolors16.default.dim(" Take one with `workser checkpoint`."));
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
6176
|
+
line(import_picocolors16.default.bold("Checkpoints"));
|
|
6177
|
+
for (const p of points) {
|
|
6178
|
+
const when = p.at ? new Date(p.at).toLocaleString() : "";
|
|
6179
|
+
line(
|
|
6180
|
+
` ${import_picocolors16.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors16.default.dim(` ${when}`) : ""}`
|
|
6181
|
+
);
|
|
6182
|
+
}
|
|
6183
|
+
line(
|
|
6184
|
+
import_picocolors16.default.dim(
|
|
6185
|
+
"\nGo back with `workser restore <ref>`, or just `workser restore` for the newest."
|
|
6186
|
+
)
|
|
6187
|
+
);
|
|
6188
|
+
}
|
|
6189
|
+
|
|
6190
|
+
// src/commands/sync.ts
|
|
6191
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
6192
|
+
function registerSync(program3) {
|
|
6193
|
+
program3.command("sync").description(
|
|
6194
|
+
"Reconcile this folder with the copy Workser holds (pull, then push)"
|
|
6195
|
+
).option("--branch <name>", "which branch to sync (default: this folder's)").option(
|
|
6196
|
+
"--app <webAppId>",
|
|
6197
|
+
"which app's code this folder holds (default: resolved from the folder)"
|
|
6198
|
+
).addHelpText(
|
|
6199
|
+
"after",
|
|
6200
|
+
"\nUse this instead of `git pull` / `git push` \u2014 this folder has no remote.\n"
|
|
6201
|
+
).action(
|
|
6202
|
+
action(async ({ ctx, opts }) => {
|
|
6203
|
+
requireLocalApp(ctx, "sync");
|
|
6204
|
+
const projectId = requireProject(ctx);
|
|
6205
|
+
const res = await api(ctx, `/v1/projects/${projectId}/git/sync`, {
|
|
6206
|
+
body: {
|
|
6207
|
+
cwd: ctx.cwd,
|
|
6208
|
+
...opts.branch ? { branch: opts.branch } : {},
|
|
6209
|
+
...opts.app ? { webAppId: opts.app } : {}
|
|
6210
|
+
}
|
|
6211
|
+
});
|
|
6212
|
+
const refused = res?.ok === false || res?.state === "diverged";
|
|
6213
|
+
ok(res, () => {
|
|
6214
|
+
if (refused) {
|
|
6215
|
+
warn(res?.message ?? "Couldn't sync this folder.");
|
|
6216
|
+
if (res?.state === "diverged") {
|
|
6217
|
+
line(
|
|
6218
|
+
import_picocolors17.default.dim(
|
|
6219
|
+
" This folder and Workser's copy have both changed. Open Workser to resolve it."
|
|
6220
|
+
)
|
|
6221
|
+
);
|
|
6222
|
+
}
|
|
6223
|
+
return;
|
|
6224
|
+
}
|
|
6225
|
+
if (res?.state === "empty") {
|
|
6226
|
+
line("Nothing to sync yet \u2014 there's no code here or on Workser.");
|
|
6227
|
+
return;
|
|
6228
|
+
}
|
|
6229
|
+
success("Synced");
|
|
6230
|
+
if (res?.ref) line(import_picocolors17.default.dim(` ${String(res.ref).slice(0, 7)}`));
|
|
6231
|
+
});
|
|
6232
|
+
if (refused) process.exitCode = 1;
|
|
6233
|
+
})
|
|
6234
|
+
);
|
|
6235
|
+
}
|
|
6236
|
+
|
|
6237
|
+
// src/commands/workflow.ts
|
|
6238
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
5895
6239
|
function registerWorkflow(program3) {
|
|
5896
6240
|
const wf = program3.command("workflow").description("Create, run, and inspect workflow automations for the project");
|
|
5897
6241
|
wf.command("list").description("List the project's workflows").action(
|
|
@@ -5899,10 +6243,10 @@ function registerWorkflow(program3) {
|
|
|
5899
6243
|
const projectId = requireProject(ctx);
|
|
5900
6244
|
const items = await api(ctx, `/v1/projects/${projectId}/workflows`);
|
|
5901
6245
|
ok(items, () => {
|
|
5902
|
-
if (!items?.length) return line(
|
|
6246
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No workflows yet. `workser workflow create`."));
|
|
5903
6247
|
for (const w of items) {
|
|
5904
|
-
const status = w.is_active ?
|
|
5905
|
-
line(`${w.id} ${
|
|
6248
|
+
const status = w.is_active ? import_picocolors18.default.green("active") : import_picocolors18.default.dim("inactive");
|
|
6249
|
+
line(`${w.id} ${import_picocolors18.default.bold(w.name ?? "Untitled")} ${status}`);
|
|
5906
6250
|
}
|
|
5907
6251
|
});
|
|
5908
6252
|
})
|
|
@@ -5914,7 +6258,7 @@ function registerWorkflow(program3) {
|
|
|
5914
6258
|
const res = await api(ctx, `/v1/projects/${projectId}/workflows`, {
|
|
5915
6259
|
body: { name: args[0], ...extra }
|
|
5916
6260
|
});
|
|
5917
|
-
ok(res, () => line(`Created workflow ${
|
|
6261
|
+
ok(res, () => line(`Created workflow ${import_picocolors18.default.bold(res.id)}.`));
|
|
5918
6262
|
})
|
|
5919
6263
|
);
|
|
5920
6264
|
wf.command("get <id>").description("Show a workflow's full definition").action(
|
|
@@ -5949,8 +6293,8 @@ function registerWorkflow(program3) {
|
|
|
5949
6293
|
action(async ({ ctx, args }) => {
|
|
5950
6294
|
const items = await api(ctx, `/v1/workflows/${args[0]}/executions`);
|
|
5951
6295
|
ok(items, () => {
|
|
5952
|
-
if (!items?.length) return line(
|
|
5953
|
-
for (const e of items) line(`${e.id} ${e.status ?? ""} ${
|
|
6296
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No runs yet."));
|
|
6297
|
+
for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors18.default.dim(e.started_at ?? "")}`);
|
|
5954
6298
|
});
|
|
5955
6299
|
})
|
|
5956
6300
|
);
|
|
@@ -5958,15 +6302,15 @@ function registerWorkflow(program3) {
|
|
|
5958
6302
|
action(async ({ ctx, args }) => {
|
|
5959
6303
|
const items = await api(ctx, `/v1/node-types`, { query: { q: args[0] } });
|
|
5960
6304
|
ok(items, () => {
|
|
5961
|
-
if (!items?.length) return line(
|
|
5962
|
-
for (const n of items) line(`${n.name ?? n.type} ${
|
|
6305
|
+
if (!items?.length) return line(import_picocolors18.default.dim("No matching node types."));
|
|
6306
|
+
for (const n of items) line(`${n.name ?? n.type} ${import_picocolors18.default.dim(n.category ?? "")}`);
|
|
5963
6307
|
});
|
|
5964
6308
|
})
|
|
5965
6309
|
);
|
|
5966
6310
|
}
|
|
5967
6311
|
|
|
5968
6312
|
// src/commands/app.ts
|
|
5969
|
-
var
|
|
6313
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
5970
6314
|
function registerApp(program3) {
|
|
5971
6315
|
const appCmd = program3.command("app").description("Connect and use third-party app integrations (Gmail, Slack, Stripe, ...)");
|
|
5972
6316
|
appCmd.command("list").description("List connectable toolkits and this project's existing connections").option("--toolkit <slug>", "filter connections to one toolkit").action(
|
|
@@ -5979,8 +6323,8 @@ function registerApp(program3) {
|
|
|
5979
6323
|
ok({ catalog, connections }, () => {
|
|
5980
6324
|
const connected = new Set((connections ?? []).map((c) => c.toolkit ?? c.composio_app));
|
|
5981
6325
|
for (const t of catalog ?? []) {
|
|
5982
|
-
const status = connected.has(t.slug) ?
|
|
5983
|
-
line(`${t.slug} ${
|
|
6326
|
+
const status = connected.has(t.slug) ? import_picocolors19.default.green("connected") : import_picocolors19.default.dim("not connected");
|
|
6327
|
+
line(`${t.slug} ${import_picocolors19.default.bold(t.name ?? t.slug)} ${status}`);
|
|
5984
6328
|
}
|
|
5985
6329
|
});
|
|
5986
6330
|
})
|
|
@@ -5997,7 +6341,7 @@ function registerApp(program3) {
|
|
|
5997
6341
|
});
|
|
5998
6342
|
ok(
|
|
5999
6343
|
res,
|
|
6000
|
-
() => res.oauth_url ? line(`Open this URL to finish connecting: ${
|
|
6344
|
+
() => 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
6345
|
);
|
|
6002
6346
|
})
|
|
6003
6347
|
);
|
|
@@ -6015,8 +6359,8 @@ function registerApp(program3) {
|
|
|
6015
6359
|
const projectId = requireProject(ctx);
|
|
6016
6360
|
const items = await api(ctx, `/v1/projects/${projectId}/integrations/${args[0]}/tools`);
|
|
6017
6361
|
ok(items, () => {
|
|
6018
|
-
if (!items?.length) return line(
|
|
6019
|
-
for (const t of items) line(`${t.slug} ${
|
|
6362
|
+
if (!items?.length) return line(import_picocolors19.default.dim("No tools found."));
|
|
6363
|
+
for (const t of items) line(`${t.slug} ${import_picocolors19.default.dim(t.description ?? "")}`);
|
|
6020
6364
|
});
|
|
6021
6365
|
})
|
|
6022
6366
|
);
|
|
@@ -6032,7 +6376,7 @@ function registerApp(program3) {
|
|
|
6032
6376
|
}
|
|
6033
6377
|
|
|
6034
6378
|
// src/commands/tool.ts
|
|
6035
|
-
var
|
|
6379
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
6036
6380
|
function registerTool(program3) {
|
|
6037
6381
|
const tool = program3.command("tool").description(
|
|
6038
6382
|
"Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
|
|
@@ -6041,7 +6385,7 @@ function registerTool(program3) {
|
|
|
6041
6385
|
action(async ({ ctx }) => {
|
|
6042
6386
|
const tools = await api(ctx, "/v1/tool/list");
|
|
6043
6387
|
ok(tools, () => {
|
|
6044
|
-
if (!tools?.length) return line(
|
|
6388
|
+
if (!tools?.length) return line(import_picocolors20.default.dim("No tools available."));
|
|
6045
6389
|
const byCategory = /* @__PURE__ */ new Map();
|
|
6046
6390
|
for (const t of tools) {
|
|
6047
6391
|
const list = byCategory.get(t.category) ?? [];
|
|
@@ -6049,9 +6393,9 @@ function registerTool(program3) {
|
|
|
6049
6393
|
byCategory.set(t.category, list);
|
|
6050
6394
|
}
|
|
6051
6395
|
for (const [category, items] of byCategory) {
|
|
6052
|
-
line(
|
|
6396
|
+
line(import_picocolors20.default.bold(category) + ":");
|
|
6053
6397
|
for (const t of items) {
|
|
6054
|
-
line(` ${t.name} ${
|
|
6398
|
+
line(` ${t.name} ${import_picocolors20.default.dim(t.description ?? "")}`);
|
|
6055
6399
|
}
|
|
6056
6400
|
}
|
|
6057
6401
|
});
|
|
@@ -6069,7 +6413,7 @@ function registerTool(program3) {
|
|
|
6069
6413
|
}
|
|
6070
6414
|
|
|
6071
6415
|
// src/commands/memory.ts
|
|
6072
|
-
var
|
|
6416
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
6073
6417
|
function registerMemory(program3) {
|
|
6074
6418
|
const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
|
|
6075
6419
|
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 +6437,9 @@ function registerMemory(program3) {
|
|
|
6093
6437
|
});
|
|
6094
6438
|
ok(res, () => {
|
|
6095
6439
|
const results = res?.results ?? res ?? [];
|
|
6096
|
-
if (!results?.length) return line(
|
|
6440
|
+
if (!results?.length) return line(import_picocolors21.default.dim("No matching memories."));
|
|
6097
6441
|
for (const r of results) {
|
|
6098
|
-
line(`${
|
|
6442
|
+
line(`${import_picocolors21.default.dim(r.id ?? "?")} ${r.memory ?? r.content ?? ""}`);
|
|
6099
6443
|
}
|
|
6100
6444
|
});
|
|
6101
6445
|
})
|
|
@@ -6112,7 +6456,7 @@ function registerMemory(program3) {
|
|
|
6112
6456
|
}
|
|
6113
6457
|
|
|
6114
6458
|
// src/commands/business.ts
|
|
6115
|
-
var
|
|
6459
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
6116
6460
|
var RESOURCE_PATHS = {
|
|
6117
6461
|
"business-config": "business-config",
|
|
6118
6462
|
"business-settings": "business-settings",
|
|
@@ -6172,7 +6516,7 @@ function registerBusiness(program3) {
|
|
|
6172
6516
|
const projectId = requireProject(ctx);
|
|
6173
6517
|
const [resource] = args;
|
|
6174
6518
|
const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
|
|
6175
|
-
ok(res, () => line(`Created ${resource} ${
|
|
6519
|
+
ok(res, () => line(`Created ${resource} ${import_picocolors22.default.bold(res?.id ?? "")}.`));
|
|
6176
6520
|
})
|
|
6177
6521
|
);
|
|
6178
6522
|
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 +6558,7 @@ function businessPath(projectId, resource, subpath) {
|
|
|
6214
6558
|
}
|
|
6215
6559
|
|
|
6216
6560
|
// src/commands/artifact.ts
|
|
6217
|
-
var
|
|
6561
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
6218
6562
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
6219
6563
|
import { resolve as resolve2, basename as basename3 } from "path";
|
|
6220
6564
|
var KINDS = [
|
|
@@ -6280,7 +6624,7 @@ function registerArtifact(program3) {
|
|
|
6280
6624
|
ok(
|
|
6281
6625
|
res,
|
|
6282
6626
|
() => success(
|
|
6283
|
-
`Recorded ${
|
|
6627
|
+
`Recorded ${import_picocolors23.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors23.default.dim(` (${res.kind})`) : ""}`
|
|
6284
6628
|
)
|
|
6285
6629
|
);
|
|
6286
6630
|
})
|
|
@@ -6294,11 +6638,11 @@ function registerArtifact(program3) {
|
|
|
6294
6638
|
}
|
|
6295
6639
|
function printRun(run) {
|
|
6296
6640
|
if (!run) return;
|
|
6297
|
-
line(` run ${
|
|
6641
|
+
line(` run ${import_picocolors23.default.bold(run.runId)}`);
|
|
6298
6642
|
if (run.taskId) line(` task ${run.taskId}`);
|
|
6299
6643
|
if (run.conversationId) line(` chat ${run.conversationId}`);
|
|
6300
6644
|
if (run.projectId) line(` project ${run.projectId}`);
|
|
6301
|
-
if (run.cwd) line(` folder ${
|
|
6645
|
+
if (run.cwd) line(` folder ${import_picocolors23.default.dim(run.cwd)}`);
|
|
6302
6646
|
}
|
|
6303
6647
|
|
|
6304
6648
|
// src/commands/image.ts
|
|
@@ -6372,7 +6716,7 @@ async function download(url, output) {
|
|
|
6372
6716
|
}
|
|
6373
6717
|
|
|
6374
6718
|
// src/commands/ask.ts
|
|
6375
|
-
var
|
|
6719
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
6376
6720
|
var TYPES = [
|
|
6377
6721
|
"input",
|
|
6378
6722
|
"choice",
|
|
@@ -6423,7 +6767,7 @@ function registerAsk(program3) {
|
|
|
6423
6767
|
code: "bad_request"
|
|
6424
6768
|
});
|
|
6425
6769
|
}
|
|
6426
|
-
info(
|
|
6770
|
+
info(import_picocolors24.default.dim("Waiting for the user to answer\u2026"));
|
|
6427
6771
|
const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
|
|
6428
6772
|
body: {
|
|
6429
6773
|
type,
|
|
@@ -6449,12 +6793,12 @@ function deriveTitle(message) {
|
|
|
6449
6793
|
function printAnswer(res) {
|
|
6450
6794
|
if (!res) return;
|
|
6451
6795
|
if (res.status === "answered") {
|
|
6452
|
-
line(` ${
|
|
6796
|
+
line(` ${import_picocolors24.default.green("answered")}`);
|
|
6453
6797
|
const value = extract(res.response);
|
|
6454
6798
|
if (value) line(` ${value}`);
|
|
6455
6799
|
return;
|
|
6456
6800
|
}
|
|
6457
|
-
line(` ${
|
|
6801
|
+
line(` ${import_picocolors24.default.yellow(res.status)} ${import_picocolors24.default.dim(res.reason ?? "")}`);
|
|
6458
6802
|
}
|
|
6459
6803
|
function extract(response) {
|
|
6460
6804
|
if (response == null) return "";
|
|
@@ -6473,7 +6817,7 @@ function extract(response) {
|
|
|
6473
6817
|
}
|
|
6474
6818
|
|
|
6475
6819
|
// src/commands/search.ts
|
|
6476
|
-
var
|
|
6820
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
6477
6821
|
function registerSearch(program3) {
|
|
6478
6822
|
program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
|
|
6479
6823
|
action(async ({ ctx, args, opts }) => {
|
|
@@ -6486,9 +6830,9 @@ function registerSearch(program3) {
|
|
|
6486
6830
|
line("");
|
|
6487
6831
|
}
|
|
6488
6832
|
const results = res?.results ?? [];
|
|
6489
|
-
if (!results.length) return line(
|
|
6833
|
+
if (!results.length) return line(import_picocolors25.default.dim("No results."));
|
|
6490
6834
|
for (const r of results) {
|
|
6491
|
-
line(`${r.title ||
|
|
6835
|
+
line(`${r.title || import_picocolors25.default.dim("(untitled)")} ${import_picocolors25.default.dim(r.url)}`);
|
|
6492
6836
|
}
|
|
6493
6837
|
});
|
|
6494
6838
|
})
|
|
@@ -6496,7 +6840,7 @@ function registerSearch(program3) {
|
|
|
6496
6840
|
}
|
|
6497
6841
|
|
|
6498
6842
|
// src/commands/board.ts
|
|
6499
|
-
var
|
|
6843
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
6500
6844
|
|
|
6501
6845
|
// src/commands/record-step.ts
|
|
6502
6846
|
async function recordEntityStep(ctx, opts) {
|
|
@@ -6535,7 +6879,7 @@ function registerBoard(program3) {
|
|
|
6535
6879
|
}
|
|
6536
6880
|
ok(rows, () => {
|
|
6537
6881
|
if (!rows.length) {
|
|
6538
|
-
line(
|
|
6882
|
+
line(import_picocolors26.default.dim("No cards on the Board yet."));
|
|
6539
6883
|
return;
|
|
6540
6884
|
}
|
|
6541
6885
|
for (const r of rows) line(formatRow(r));
|
|
@@ -6550,7 +6894,7 @@ function registerBoard(program3) {
|
|
|
6550
6894
|
`/v1/projects/${projectId}/work-items/${args[0]}`
|
|
6551
6895
|
);
|
|
6552
6896
|
ok(row, () => {
|
|
6553
|
-
line(`${
|
|
6897
|
+
line(`${import_picocolors26.default.bold(row.title)} ${import_picocolors26.default.dim(row.id)}`);
|
|
6554
6898
|
line(`${statusTag(row.status)} priority ${row.priority}`);
|
|
6555
6899
|
if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
|
|
6556
6900
|
if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
|
|
@@ -6591,7 +6935,7 @@ ${row.description}`);
|
|
|
6591
6935
|
refId: row?.id,
|
|
6592
6936
|
output: { workItem: row }
|
|
6593
6937
|
});
|
|
6594
|
-
ok(row, () => line(`Created work item ${
|
|
6938
|
+
ok(row, () => line(`Created work item ${import_picocolors26.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6595
6939
|
})
|
|
6596
6940
|
);
|
|
6597
6941
|
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 +6969,7 @@ ${row.description}`);
|
|
|
6625
6969
|
);
|
|
6626
6970
|
}
|
|
6627
6971
|
const row = await patchItem(ctx, projectId, String(args[0]), body);
|
|
6628
|
-
ok(row, () => line(`Updated ${
|
|
6972
|
+
ok(row, () => line(`Updated ${import_picocolors26.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
|
|
6629
6973
|
})
|
|
6630
6974
|
);
|
|
6631
6975
|
board.command("move <id> <status>").description(
|
|
@@ -6636,14 +6980,14 @@ ${row.description}`);
|
|
|
6636
6980
|
const status = String(args[1]);
|
|
6637
6981
|
assertStatus(status);
|
|
6638
6982
|
const row = await patchItem(ctx, projectId, String(args[0]), { status });
|
|
6639
|
-
ok(row, () => line(`Moved ${
|
|
6983
|
+
ok(row, () => line(`Moved ${import_picocolors26.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
|
|
6640
6984
|
})
|
|
6641
6985
|
);
|
|
6642
6986
|
board.command("close <id>").description("Shorthand for `board move <id> done`").action(
|
|
6643
6987
|
action(async ({ ctx, args }) => {
|
|
6644
6988
|
const projectId = requireProject(ctx);
|
|
6645
6989
|
const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
|
|
6646
|
-
ok(row, () => line(`Closed ${
|
|
6990
|
+
ok(row, () => line(`Closed ${import_picocolors26.default.bold(row.title)} ${statusTag(row.status)}`));
|
|
6647
6991
|
})
|
|
6648
6992
|
);
|
|
6649
6993
|
}
|
|
@@ -6676,23 +7020,293 @@ function assertPriority(value) {
|
|
|
6676
7020
|
}
|
|
6677
7021
|
}
|
|
6678
7022
|
function formatRow(r) {
|
|
6679
|
-
const labels = r.labels?.length ?
|
|
6680
|
-
const owner = r.ownerHuman ?
|
|
6681
|
-
return `${
|
|
7023
|
+
const labels = r.labels?.length ? import_picocolors26.default.dim(` [${r.labels.join(", ")}]`) : "";
|
|
7024
|
+
const owner = r.ownerHuman ? import_picocolors26.default.dim(` @${r.ownerHuman}`) : "";
|
|
7025
|
+
return `${import_picocolors26.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
|
|
6682
7026
|
}
|
|
6683
7027
|
function statusTag(status) {
|
|
6684
7028
|
const label = status.padEnd(11);
|
|
6685
|
-
if (status === "done") return
|
|
6686
|
-
if (status === "in-progress") return
|
|
6687
|
-
if (status === "in-review") return
|
|
6688
|
-
return
|
|
7029
|
+
if (status === "done") return import_picocolors26.default.green(label);
|
|
7030
|
+
if (status === "in-progress") return import_picocolors26.default.yellow(label);
|
|
7031
|
+
if (status === "in-review") return import_picocolors26.default.cyan(label);
|
|
7032
|
+
return import_picocolors26.default.dim(label);
|
|
6689
7033
|
}
|
|
6690
7034
|
function collect2(value, previous) {
|
|
6691
7035
|
return [...previous, value];
|
|
6692
7036
|
}
|
|
6693
7037
|
|
|
7038
|
+
// src/commands/task.ts
|
|
7039
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
7040
|
+
var STATUSES2 = ["todo", "working", "checking", "ready", "accepted", "archived"];
|
|
7041
|
+
var ROLES = ["pm", "architect", "web", "api", "automation", "qa"];
|
|
7042
|
+
var KINDS2 = ["data_reports", "web", "mobile", "service", "automation", "docs"];
|
|
7043
|
+
function registerTask(program3) {
|
|
7044
|
+
const task = program3.command("task").description("The project's tasks and their subtasks (AI Tech Team)");
|
|
7045
|
+
task.command("list").description("List the board's tasks \u2014 run this before starting anything").option("--status <value>", `only tasks in this status (${STATUSES2.join(" | ")})`).option("--label <value>", "only tasks carrying this label").option("--limit <n>", "cap the number of tasks returned").action(
|
|
7046
|
+
action(async ({ ctx, opts }) => {
|
|
7047
|
+
requireProject(ctx);
|
|
7048
|
+
if (opts.status !== void 0) assertOneOf("--status", opts.status, STATUSES2);
|
|
7049
|
+
const rows = await api(ctx, "/v1/project-tasks", {
|
|
7050
|
+
query: {
|
|
7051
|
+
status: opts.status,
|
|
7052
|
+
label: opts.label,
|
|
7053
|
+
limit: opts.limit
|
|
7054
|
+
}
|
|
7055
|
+
}) ?? [];
|
|
7056
|
+
ok(rows, () => {
|
|
7057
|
+
if (!rows.length) {
|
|
7058
|
+
line(import_picocolors27.default.dim("No tasks on the board yet."));
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7061
|
+
for (const r of rows) line(formatRow2(r));
|
|
7062
|
+
});
|
|
7063
|
+
})
|
|
7064
|
+
);
|
|
7065
|
+
task.command("show [id]").description(
|
|
7066
|
+
"Show one task with its subtasks. Defaults to the task this run is inside."
|
|
7067
|
+
).action(
|
|
7068
|
+
action(async ({ ctx, args }) => {
|
|
7069
|
+
const id = resolveTaskId(ctx, args[0]);
|
|
7070
|
+
const row = await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}`);
|
|
7071
|
+
ok(row, () => printTask(row));
|
|
7072
|
+
})
|
|
7073
|
+
);
|
|
7074
|
+
const subtask = task.command("subtask").description("The steps a task is broken into");
|
|
7075
|
+
subtask.command("add <title>").description(
|
|
7076
|
+
'Add one step, e.g. `workser task subtask add "Build the upload screen" --role web`'
|
|
7077
|
+
).option("--task <id>", "the parent task (defaults to the task this run is inside)").option("--role <value>", `who does it (${ROLES.join(" | ")})`).option("--kind <value>", `what it produces (${KINDS2.join(" | ")})`).option("--note <text>", "one sentence on what this step does").option("--app <id...>", "app ids this step touches").option("--infra <ref...>", "shared setup it touches (database | storage | auth | hosting | jobs)").option("--scope <path...>", "files or folders THIS step owns").option("--depends-on <id...>", "steps that must finish first (key or id)").action(
|
|
7078
|
+
action(async ({ ctx, args, opts }) => {
|
|
7079
|
+
const parent = resolveTaskId(ctx, opts.task);
|
|
7080
|
+
if (opts.role !== void 0) assertOneOf("--role", opts.role, ROLES);
|
|
7081
|
+
if (opts.kind !== void 0) assertOneOf("--kind", opts.kind, KINDS2);
|
|
7082
|
+
const dependsOn = await resolveDeps(ctx, parent, opts.dependsOn ?? []);
|
|
7083
|
+
const row = await api(ctx, "/v1/project-tasks", {
|
|
7084
|
+
body: {
|
|
7085
|
+
parentTaskId: parent,
|
|
7086
|
+
title: args[0],
|
|
7087
|
+
summary: opts.note,
|
|
7088
|
+
role: opts.role,
|
|
7089
|
+
category: opts.kind,
|
|
7090
|
+
scopePaths: opts.scope,
|
|
7091
|
+
dependsOn,
|
|
7092
|
+
targets: [
|
|
7093
|
+
...(opts.app ?? []).map((appId) => ({ kind: "app", appId })),
|
|
7094
|
+
...(opts.infra ?? []).map((ref) => ({ kind: "infra", ref }))
|
|
7095
|
+
]
|
|
7096
|
+
}
|
|
7097
|
+
});
|
|
7098
|
+
ok(row, () => {
|
|
7099
|
+
line(`${import_picocolors27.default.green("added")} ${import_picocolors27.default.bold(row.title)} ${import_picocolors27.default.dim(row.key ?? row.id)}`);
|
|
7100
|
+
if (row.role) line(import_picocolors27.default.dim(`role: ${row.role}`));
|
|
7101
|
+
});
|
|
7102
|
+
})
|
|
7103
|
+
);
|
|
7104
|
+
subtask.command("list [taskId]").description("The steps of a task, in order").action(
|
|
7105
|
+
action(async ({ ctx, args }) => {
|
|
7106
|
+
const id = resolveTaskId(ctx, args[0]);
|
|
7107
|
+
const row = await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}`);
|
|
7108
|
+
const rows = row.subtasks ?? [];
|
|
7109
|
+
ok(rows, () => {
|
|
7110
|
+
if (!rows.length) {
|
|
7111
|
+
line(import_picocolors27.default.dim("No steps yet."));
|
|
7112
|
+
return;
|
|
7113
|
+
}
|
|
7114
|
+
rows.forEach((r, i) => line(formatSubtask(r, i + 1)));
|
|
7115
|
+
});
|
|
7116
|
+
})
|
|
7117
|
+
);
|
|
7118
|
+
subtask.command("update <id>").description("Change a step \u2014 its title, its role, what it touches").option("--title <text>").option("--note <text>").option("--role <value>", ROLES.join(" | ")).option("--kind <value>", KINDS2.join(" | ")).option("--scope <path...>", "replaces the step's scope entirely").action(
|
|
7119
|
+
action(async ({ ctx, args, opts }) => {
|
|
7120
|
+
if (opts.role !== void 0) assertOneOf("--role", opts.role, ROLES);
|
|
7121
|
+
if (opts.kind !== void 0) assertOneOf("--kind", opts.kind, KINDS2);
|
|
7122
|
+
const row = await api(
|
|
7123
|
+
ctx,
|
|
7124
|
+
`/v1/project-tasks/${encodeURIComponent(args[0])}`,
|
|
7125
|
+
{
|
|
7126
|
+
method: "PATCH",
|
|
7127
|
+
body: {
|
|
7128
|
+
title: opts.title,
|
|
7129
|
+
summary: opts.note,
|
|
7130
|
+
role: opts.role,
|
|
7131
|
+
category: opts.kind,
|
|
7132
|
+
scopePaths: opts.scope
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
);
|
|
7136
|
+
ok(row, () => line(`${import_picocolors27.default.green("updated")} ${import_picocolors27.default.bold(row.title)}`));
|
|
7137
|
+
})
|
|
7138
|
+
);
|
|
7139
|
+
subtask.command("remove <id>").description("Drop a step from the plan (only before the work starts)").action(
|
|
7140
|
+
action(async ({ ctx, args }) => {
|
|
7141
|
+
await api(ctx, `/v1/project-tasks/${encodeURIComponent(args[0])}`, {
|
|
7142
|
+
method: "DELETE"
|
|
7143
|
+
});
|
|
7144
|
+
ok({ removed: args[0] }, () => line(import_picocolors27.default.green("removed")));
|
|
7145
|
+
})
|
|
7146
|
+
);
|
|
7147
|
+
task.command("move <id> <status>").description(`Move a task or step along the board (${STATUSES2.join(" | ")})`).action(
|
|
7148
|
+
action(async ({ ctx, args }) => {
|
|
7149
|
+
assertOneOf("<status>", args[1], STATUSES2);
|
|
7150
|
+
const row = await api(
|
|
7151
|
+
ctx,
|
|
7152
|
+
`/v1/project-tasks/${encodeURIComponent(args[0])}/move`,
|
|
7153
|
+
{ body: { status: args[1] } }
|
|
7154
|
+
);
|
|
7155
|
+
ok(row, () => line(`${import_picocolors27.default.green("moved")} ${import_picocolors27.default.bold(row.title)} \u2192 ${args[1]}`));
|
|
7156
|
+
})
|
|
7157
|
+
);
|
|
7158
|
+
task.command("can-start [id]").description("Ask whether work on this task may begin. Refuses until the owner approves.").action(
|
|
7159
|
+
action(async ({ ctx, args }) => {
|
|
7160
|
+
const id = resolveTaskId(ctx, args[0]);
|
|
7161
|
+
const row = await api(
|
|
7162
|
+
ctx,
|
|
7163
|
+
`/v1/project-tasks/${encodeURIComponent(id)}/dispatch-check`,
|
|
7164
|
+
{ method: "POST" }
|
|
7165
|
+
);
|
|
7166
|
+
ok(row, () => line(import_picocolors27.default.green("approved \u2014 you may start")));
|
|
7167
|
+
})
|
|
7168
|
+
);
|
|
7169
|
+
task.command("approval").description("Ask the owner to approve the plan, or record their decision").argument("<request|approve|decline>").option("--task <id>", "defaults to the task this run is inside").option("--note <text>", "why").action(
|
|
7170
|
+
action(async ({ ctx, args, opts }) => {
|
|
7171
|
+
const id = resolveTaskId(ctx, opts.task);
|
|
7172
|
+
const what = args[0];
|
|
7173
|
+
if (what === "request") {
|
|
7174
|
+
const row2 = await api(
|
|
7175
|
+
ctx,
|
|
7176
|
+
`/v1/project-tasks/${encodeURIComponent(id)}`
|
|
7177
|
+
);
|
|
7178
|
+
ok({ awaiting: row2.approval_state === "awaiting", task: row2 }, () => {
|
|
7179
|
+
line(
|
|
7180
|
+
row2.approval_state === "awaiting" ? import_picocolors27.default.yellow("The plan is waiting on the owner. They see it in the task.") : `Already ${row2.approval_state}.`
|
|
7181
|
+
);
|
|
7182
|
+
});
|
|
7183
|
+
return;
|
|
7184
|
+
}
|
|
7185
|
+
if (what !== "approve" && what !== "decline") {
|
|
7186
|
+
throw new WorkserError(
|
|
7187
|
+
`Expected "request", "approve" or "decline", got "${what}".`,
|
|
7188
|
+
{ code: "bad_request" }
|
|
7189
|
+
);
|
|
7190
|
+
}
|
|
7191
|
+
const row = await api(
|
|
7192
|
+
ctx,
|
|
7193
|
+
`/v1/project-tasks/${encodeURIComponent(id)}/approval`,
|
|
7194
|
+
{
|
|
7195
|
+
body: {
|
|
7196
|
+
decision: what === "approve" ? "approved" : "declined",
|
|
7197
|
+
note: opts.note
|
|
7198
|
+
}
|
|
7199
|
+
}
|
|
7200
|
+
);
|
|
7201
|
+
ok(row, () => line(`${import_picocolors27.default.green(row.approval_state)} ${import_picocolors27.default.bold(row.title)}`));
|
|
7202
|
+
})
|
|
7203
|
+
);
|
|
7204
|
+
task.command("done [id]").description("Record what a step produced, and move it to ready").option("--summary <text>", "what changed, in the owner's words").action(
|
|
7205
|
+
action(async ({ ctx, args, opts }) => {
|
|
7206
|
+
const id = resolveTaskId(ctx, args[0]);
|
|
7207
|
+
await api(ctx, `/v1/project-tasks/${encodeURIComponent(id)}`, {
|
|
7208
|
+
method: "PATCH",
|
|
7209
|
+
body: { resultSummary: opts.summary }
|
|
7210
|
+
});
|
|
7211
|
+
const row = await api(
|
|
7212
|
+
ctx,
|
|
7213
|
+
`/v1/project-tasks/${encodeURIComponent(id)}/move`,
|
|
7214
|
+
{ body: { status: "ready" } }
|
|
7215
|
+
);
|
|
7216
|
+
ok(row, () => line(`${import_picocolors27.default.green("ready")} ${import_picocolors27.default.bold(row.title)}`));
|
|
7217
|
+
})
|
|
7218
|
+
);
|
|
7219
|
+
}
|
|
7220
|
+
function resolveTaskId(ctx, given) {
|
|
7221
|
+
const id = given || ctx.projectTaskId;
|
|
7222
|
+
if (!id) {
|
|
7223
|
+
throw new WorkserError(
|
|
7224
|
+
"No task. This run isn't inside one (WORKSER_PROJECT_TASK_ID is unset), so pass --task <id or key>.",
|
|
7225
|
+
{ code: "no_task" }
|
|
7226
|
+
);
|
|
7227
|
+
}
|
|
7228
|
+
return id;
|
|
7229
|
+
}
|
|
7230
|
+
async function resolveDeps(ctx, parentId, given) {
|
|
7231
|
+
if (!given.length) return void 0;
|
|
7232
|
+
const parent = await api(
|
|
7233
|
+
ctx,
|
|
7234
|
+
`/v1/project-tasks/${encodeURIComponent(parentId)}`
|
|
7235
|
+
);
|
|
7236
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7237
|
+
for (const s of parent.subtasks ?? []) {
|
|
7238
|
+
byKey.set(s.id, s.id);
|
|
7239
|
+
if (s.key) byKey.set(s.key, s.id);
|
|
7240
|
+
}
|
|
7241
|
+
return given.map((g) => {
|
|
7242
|
+
const id = byKey.get(g);
|
|
7243
|
+
if (!id) {
|
|
7244
|
+
throw new WorkserError(
|
|
7245
|
+
`"${g}" is not a step of this task. Run \`workser task subtask list\` to see them.`,
|
|
7246
|
+
{ code: "bad_request" }
|
|
7247
|
+
);
|
|
7248
|
+
}
|
|
7249
|
+
return id;
|
|
7250
|
+
});
|
|
7251
|
+
}
|
|
7252
|
+
function assertOneOf(flag, value, allowed) {
|
|
7253
|
+
if (!allowed.includes(value)) {
|
|
7254
|
+
throw new WorkserError(
|
|
7255
|
+
`${flag} must be one of: ${allowed.join(", ")} \u2014 got "${value}".`,
|
|
7256
|
+
{ code: "bad_request" }
|
|
7257
|
+
);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
function formatRow2(r) {
|
|
7261
|
+
const key = import_picocolors27.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
|
|
7262
|
+
const steps = r.subtaskTotal ? import_picocolors27.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
|
|
7263
|
+
const gate = r.approval_state === "awaiting" ? import_picocolors27.default.yellow(" awaiting approval") : "";
|
|
7264
|
+
return `${key} ${statusTag2(r.status)} ${r.title}${steps}${gate}`;
|
|
7265
|
+
}
|
|
7266
|
+
function formatSubtask(r, index) {
|
|
7267
|
+
const n = import_picocolors27.default.dim(String(index).padStart(2, "0"));
|
|
7268
|
+
const role = r.role ? import_picocolors27.default.dim(` [${r.role}]`) : "";
|
|
7269
|
+
const scope = r.scope_paths?.length ? import_picocolors27.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
|
|
7270
|
+
return `${n} ${statusTag2(r.status)} ${r.title}${role}${scope}`;
|
|
7271
|
+
}
|
|
7272
|
+
function printTask(row) {
|
|
7273
|
+
line(`${import_picocolors27.default.bold(row.title)} ${import_picocolors27.default.dim(row.key ?? row.id)}`);
|
|
7274
|
+
line(`${statusTag2(row.status)} approval: ${row.approval_state}`);
|
|
7275
|
+
if (row.summary) line(`
|
|
7276
|
+
${row.summary}`);
|
|
7277
|
+
if (row.targets?.length) {
|
|
7278
|
+
line(
|
|
7279
|
+
`
|
|
7280
|
+
touches: ${row.targets.map((t) => t.appName ?? t.ref ?? t.kind).join(", ")}`
|
|
7281
|
+
);
|
|
7282
|
+
}
|
|
7283
|
+
if (row.subtasks?.length) {
|
|
7284
|
+
line(`
|
|
7285
|
+
${import_picocolors27.default.bold("steps")}`);
|
|
7286
|
+
row.subtasks.forEach((s, i) => line(formatSubtask(s, i + 1)));
|
|
7287
|
+
} else {
|
|
7288
|
+
line(import_picocolors27.default.dim("\nNo steps yet."));
|
|
7289
|
+
}
|
|
7290
|
+
}
|
|
7291
|
+
function statusTag2(status) {
|
|
7292
|
+
switch (status) {
|
|
7293
|
+
case "ready":
|
|
7294
|
+
return import_picocolors27.default.green("[ready]");
|
|
7295
|
+
case "working":
|
|
7296
|
+
return import_picocolors27.default.blue("[working]");
|
|
7297
|
+
case "checking":
|
|
7298
|
+
return import_picocolors27.default.cyan("[checking]");
|
|
7299
|
+
case "accepted":
|
|
7300
|
+
return import_picocolors27.default.green("[accepted]");
|
|
7301
|
+
case "archived":
|
|
7302
|
+
return import_picocolors27.default.dim("[archived]");
|
|
7303
|
+
default:
|
|
7304
|
+
return import_picocolors27.default.dim("[todo]");
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
|
|
6694
7308
|
// src/commands/decision.ts
|
|
6695
|
-
var
|
|
7309
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
6696
7310
|
function registerDecision(program3) {
|
|
6697
7311
|
const decision = program3.command("decision").description("Read and record the project's architecture decisions");
|
|
6698
7312
|
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 +7319,11 @@ function registerDecision(program3) {
|
|
|
6705
7319
|
rows = applyLimit(rows, opts.limit);
|
|
6706
7320
|
ok(rows, () => {
|
|
6707
7321
|
if (!rows.length) {
|
|
6708
|
-
line(
|
|
7322
|
+
line(import_picocolors28.default.dim("No decisions recorded yet."));
|
|
6709
7323
|
return;
|
|
6710
7324
|
}
|
|
6711
7325
|
for (const r of rows) {
|
|
6712
|
-
line(`${
|
|
7326
|
+
line(`${import_picocolors28.default.dim(r.id)} ${import_picocolors28.default.dim(shortDate(r.createdAt))} ${r.title}`);
|
|
6713
7327
|
line(` ${truncate(r.decision, 100)}`);
|
|
6714
7328
|
}
|
|
6715
7329
|
});
|
|
@@ -6723,16 +7337,16 @@ function registerDecision(program3) {
|
|
|
6723
7337
|
`/v1/projects/${projectId}/architecture-decisions/${args[0]}`
|
|
6724
7338
|
);
|
|
6725
7339
|
ok(row, () => {
|
|
6726
|
-
line(`${
|
|
6727
|
-
line(
|
|
7340
|
+
line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.id)}`);
|
|
7341
|
+
line(import_picocolors28.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
|
|
6728
7342
|
line(`
|
|
6729
|
-
${
|
|
7343
|
+
${import_picocolors28.default.bold("Context")}
|
|
6730
7344
|
${row.context}`);
|
|
6731
7345
|
line(`
|
|
6732
|
-
${
|
|
7346
|
+
${import_picocolors28.default.bold("Decision")}
|
|
6733
7347
|
${row.decision}`);
|
|
6734
7348
|
if (row.consequences) line(`
|
|
6735
|
-
${
|
|
7349
|
+
${import_picocolors28.default.bold("Consequences")}
|
|
6736
7350
|
${row.consequences}`);
|
|
6737
7351
|
});
|
|
6738
7352
|
})
|
|
@@ -6761,7 +7375,7 @@ ${row.consequences}`);
|
|
|
6761
7375
|
refId: row?.id,
|
|
6762
7376
|
output: { decision: row }
|
|
6763
7377
|
});
|
|
6764
|
-
ok(row, () => line(`Recorded decision ${
|
|
7378
|
+
ok(row, () => line(`Recorded decision ${import_picocolors28.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6765
7379
|
})
|
|
6766
7380
|
);
|
|
6767
7381
|
const requirement = program3.command("requirement").description("Read and record the project's requirements");
|
|
@@ -6773,11 +7387,11 @@ ${row.consequences}`);
|
|
|
6773
7387
|
rows = applyLimit(rows, opts.limit);
|
|
6774
7388
|
ok(rows, () => {
|
|
6775
7389
|
if (!rows.length) {
|
|
6776
|
-
line(
|
|
7390
|
+
line(import_picocolors28.default.dim("No requirements recorded yet."));
|
|
6777
7391
|
return;
|
|
6778
7392
|
}
|
|
6779
7393
|
for (const r of rows) {
|
|
6780
|
-
line(`${
|
|
7394
|
+
line(`${import_picocolors28.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
|
|
6781
7395
|
}
|
|
6782
7396
|
});
|
|
6783
7397
|
})
|
|
@@ -6790,8 +7404,8 @@ ${row.consequences}`);
|
|
|
6790
7404
|
`/v1/projects/${projectId}/requirements/${args[0]}`
|
|
6791
7405
|
);
|
|
6792
7406
|
ok(row, () => {
|
|
6793
|
-
line(`${
|
|
6794
|
-
line(
|
|
7407
|
+
line(`${import_picocolors28.default.bold(row.title)} ${import_picocolors28.default.dim(row.id)}`);
|
|
7408
|
+
line(import_picocolors28.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
|
|
6795
7409
|
line(`
|
|
6796
7410
|
${row.body}`);
|
|
6797
7411
|
});
|
|
@@ -6817,7 +7431,7 @@ ${row.body}`);
|
|
|
6817
7431
|
refId: row?.id,
|
|
6818
7432
|
output: { requirement: row }
|
|
6819
7433
|
});
|
|
6820
|
-
ok(row, () => line(`Recorded requirement ${
|
|
7434
|
+
ok(row, () => line(`Recorded requirement ${import_picocolors28.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6821
7435
|
})
|
|
6822
7436
|
);
|
|
6823
7437
|
requirement.command("update <id>").description(
|
|
@@ -6846,7 +7460,7 @@ ${row.body}`);
|
|
|
6846
7460
|
code: "bad_request"
|
|
6847
7461
|
});
|
|
6848
7462
|
}
|
|
6849
|
-
ok(row, () => line(`Updated requirement ${
|
|
7463
|
+
ok(row, () => line(`Updated requirement ${import_picocolors28.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
|
|
6850
7464
|
})
|
|
6851
7465
|
);
|
|
6852
7466
|
}
|
|
@@ -6869,7 +7483,7 @@ function shortDate(iso) {
|
|
|
6869
7483
|
}
|
|
6870
7484
|
|
|
6871
7485
|
// src/commands/doc.ts
|
|
6872
|
-
var
|
|
7486
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
6873
7487
|
function registerDoc(program3) {
|
|
6874
7488
|
const doc = program3.command("doc").description("Read and write project documents");
|
|
6875
7489
|
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 +7494,13 @@ function registerDoc(program3) {
|
|
|
6880
7494
|
}) ?? [];
|
|
6881
7495
|
ok(rows, () => {
|
|
6882
7496
|
if (!rows.length) {
|
|
6883
|
-
line(
|
|
7497
|
+
line(import_picocolors29.default.dim("No documents yet."));
|
|
6884
7498
|
return;
|
|
6885
7499
|
}
|
|
6886
7500
|
for (const r of rows) {
|
|
6887
|
-
const link = r.workItemId ?
|
|
6888
|
-
const file = r.filePath ?
|
|
6889
|
-
line(`${
|
|
7501
|
+
const link = r.workItemId ? import_picocolors29.default.dim(` \u21B3 ${r.workItemId}`) : "";
|
|
7502
|
+
const file = r.filePath ? import_picocolors29.default.dim(` ${r.filePath}`) : "";
|
|
7503
|
+
line(`${import_picocolors29.default.dim(r.id)} ${r.title}${link}${file}`);
|
|
6890
7504
|
}
|
|
6891
7505
|
});
|
|
6892
7506
|
})
|
|
@@ -6900,17 +7514,17 @@ function registerDoc(program3) {
|
|
|
6900
7514
|
);
|
|
6901
7515
|
if (opts.markdown) {
|
|
6902
7516
|
ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
|
|
6903
|
-
line(`${
|
|
7517
|
+
line(`${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.id)}`);
|
|
6904
7518
|
line(
|
|
6905
|
-
row.filePath ? `Read it at ${
|
|
7519
|
+
row.filePath ? `Read it at ${import_picocolors29.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors29.default.dim("This document has no markdown mirror on disk yet.")
|
|
6906
7520
|
);
|
|
6907
7521
|
});
|
|
6908
7522
|
return;
|
|
6909
7523
|
}
|
|
6910
7524
|
ok(row, () => {
|
|
6911
|
-
line(`${
|
|
6912
|
-
if (row.workItemId) line(
|
|
6913
|
-
if (row.filePath) line(
|
|
7525
|
+
line(`${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.id)}`);
|
|
7526
|
+
if (row.workItemId) line(import_picocolors29.default.dim(`linked to work item ${row.workItemId}`));
|
|
7527
|
+
if (row.filePath) line(import_picocolors29.default.dim(`markdown mirror: ${row.filePath}`));
|
|
6914
7528
|
line("");
|
|
6915
7529
|
line(row.contentJson);
|
|
6916
7530
|
});
|
|
@@ -6939,7 +7553,7 @@ function registerDoc(program3) {
|
|
|
6939
7553
|
refId: row?.id,
|
|
6940
7554
|
output: { document: row }
|
|
6941
7555
|
});
|
|
6942
|
-
ok(row, () => line(`Created document ${
|
|
7556
|
+
ok(row, () => line(`Created document ${import_picocolors29.default.bold(row?.id ?? "")} \u2014 ${title}`));
|
|
6943
7557
|
})
|
|
6944
7558
|
);
|
|
6945
7559
|
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 +7580,13 @@ function registerDoc(program3) {
|
|
|
6966
7580
|
code: "bad_request"
|
|
6967
7581
|
});
|
|
6968
7582
|
}
|
|
6969
|
-
ok(row, () => line(`Updated document ${
|
|
7583
|
+
ok(row, () => line(`Updated document ${import_picocolors29.default.bold(row.id)} \u2014 ${row.title}`));
|
|
6970
7584
|
})
|
|
6971
7585
|
);
|
|
6972
7586
|
}
|
|
6973
7587
|
|
|
6974
7588
|
// src/commands/design.ts
|
|
6975
|
-
var
|
|
7589
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
6976
7590
|
function registerDesign(program3) {
|
|
6977
7591
|
const design = program3.command("design").description("Read the project's brand (colours, fonts, logo)");
|
|
6978
7592
|
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 +7600,11 @@ function registerDesign(program3) {
|
|
|
6986
7600
|
if (opts.raw) {
|
|
6987
7601
|
ok(files, () => {
|
|
6988
7602
|
if (!files.length) {
|
|
6989
|
-
line(
|
|
7603
|
+
line(import_picocolors30.default.dim("No brand set for this project."));
|
|
6990
7604
|
return;
|
|
6991
7605
|
}
|
|
6992
7606
|
for (const f of files) {
|
|
6993
|
-
line(
|
|
7607
|
+
line(import_picocolors30.default.bold(f.path));
|
|
6994
7608
|
line(f.contents);
|
|
6995
7609
|
line("");
|
|
6996
7610
|
}
|
|
@@ -7007,21 +7621,21 @@ function registerDesign(program3) {
|
|
|
7007
7621
|
} : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
|
|
7008
7622
|
ok(summary, () => {
|
|
7009
7623
|
if (!tokens) {
|
|
7010
|
-
line(
|
|
7624
|
+
line(import_picocolors30.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
|
|
7011
7625
|
return;
|
|
7012
7626
|
}
|
|
7013
7627
|
for (const [name, value] of Object.entries(tokens.brand)) {
|
|
7014
|
-
line(`${
|
|
7628
|
+
line(`${import_picocolors30.default.dim(name.padEnd(12))} ${value}`);
|
|
7015
7629
|
}
|
|
7016
7630
|
for (const [name, value] of Object.entries(tokens.color)) {
|
|
7017
|
-
line(`${
|
|
7631
|
+
line(`${import_picocolors30.default.dim(`color.${name}`.padEnd(12))} ${value}`);
|
|
7018
7632
|
}
|
|
7019
7633
|
for (const [name, value] of Object.entries(tokens.font)) {
|
|
7020
|
-
line(`${
|
|
7634
|
+
line(`${import_picocolors30.default.dim(`font.${name}`.padEnd(12))} ${value}`);
|
|
7021
7635
|
}
|
|
7022
7636
|
line("");
|
|
7023
7637
|
line(
|
|
7024
|
-
|
|
7638
|
+
import_picocolors30.default.dim(
|
|
7025
7639
|
`Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
|
|
7026
7640
|
)
|
|
7027
7641
|
);
|
|
@@ -7053,7 +7667,7 @@ function unwrap(group) {
|
|
|
7053
7667
|
|
|
7054
7668
|
// src/index.ts
|
|
7055
7669
|
var pkg = {
|
|
7056
|
-
version: true ? "0.
|
|
7670
|
+
version: true ? "0.3.0" : "0.0.0-dev"
|
|
7057
7671
|
};
|
|
7058
7672
|
var program2 = new Command();
|
|
7059
7673
|
program2.name("workser").description(
|
|
@@ -7086,6 +7700,8 @@ registerOpen(program2);
|
|
|
7086
7700
|
registerDoctor(program2);
|
|
7087
7701
|
registerAgent(program2);
|
|
7088
7702
|
registerVerify(program2);
|
|
7703
|
+
registerCheckpoint(program2);
|
|
7704
|
+
registerSync(program2);
|
|
7089
7705
|
registerWorkflow(program2);
|
|
7090
7706
|
registerApp(program2);
|
|
7091
7707
|
registerTool(program2);
|
|
@@ -7096,6 +7712,7 @@ registerImage(program2);
|
|
|
7096
7712
|
registerAsk(program2);
|
|
7097
7713
|
registerSearch(program2);
|
|
7098
7714
|
registerBoard(program2);
|
|
7715
|
+
registerTask(program2);
|
|
7099
7716
|
registerDecision(program2);
|
|
7100
7717
|
registerDoc(program2);
|
|
7101
7718
|
registerDesign(program2);
|