@workser/cli 0.6.10 → 0.6.12

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.
Files changed (2) hide show
  1. package/dist/index.js +125 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8639,7 +8639,58 @@ function collect2(value, previous) {
8639
8639
 
8640
8640
  // src/commands/task.ts
8641
8641
  var import_picocolors29 = __toESM(require_picocolors(), 1);
8642
+
8643
+ // src/subtask-attachments.ts
8644
+ var MAX_REF_NOTE = 500;
8645
+ var MAX_REFS = 20;
8646
+ var URL_SCHEME = /^https?:\/\//i;
8647
+ function labelFor(type, value) {
8648
+ if (type === "url") {
8649
+ try {
8650
+ return new URL(value).hostname.replace(/^www\./, "");
8651
+ } catch {
8652
+ return value;
8653
+ }
8654
+ }
8655
+ const parts = value.split(/[\\/]/).filter(Boolean);
8656
+ return parts[parts.length - 1] || value;
8657
+ }
8658
+ function parseRef(raw) {
8659
+ const at = raw.indexOf("::");
8660
+ const value = (at === -1 ? raw : raw.slice(0, at)).trim();
8661
+ if (!value) return null;
8662
+ const note = at === -1 ? "" : raw.slice(at + 2).trim().slice(0, MAX_REF_NOTE);
8663
+ const type = URL_SCHEME.test(value) ? "url" : "file";
8664
+ return {
8665
+ type,
8666
+ value,
8667
+ label: labelFor(type, value),
8668
+ ...note ? { note } : {}
8669
+ };
8670
+ }
8671
+ function parseRefs(raw) {
8672
+ if (!raw?.length) return [];
8673
+ const out = [];
8674
+ const seen = /* @__PURE__ */ new Set();
8675
+ for (const item of raw) {
8676
+ const parsed = parseRef(item);
8677
+ if (!parsed) continue;
8678
+ if (seen.has(parsed.value)) continue;
8679
+ seen.add(parsed.value);
8680
+ out.push(parsed);
8681
+ }
8682
+ return out.slice(0, MAX_REFS);
8683
+ }
8684
+
8685
+ // src/commands/task.ts
8642
8686
  var STATUSES2 = [
8687
+ /**
8688
+ * LISTED, NOT PLANNED. A goal's later phases get their tasks written down
8689
+ * when the plan is agreed, so the owner can read the whole shape — but those
8690
+ * rows have no subtasks and the runner must never pick them up. `todo` keeps
8691
+ * its meaning: the team may start this now.
8692
+ */
8693
+ "backlog",
8643
8694
  "todo",
8644
8695
  "working",
8645
8696
  "checking",
@@ -8647,6 +8698,8 @@ var STATUSES2 = [
8647
8698
  "accepted",
8648
8699
  "archived"
8649
8700
  ];
8701
+ var PRIORITIES2 = ["urgent", "high", "normal", "low"];
8702
+ var SIZES = ["small", "standard"];
8650
8703
  var ROLES = [
8651
8704
  "pm",
8652
8705
  "architect",
@@ -8726,10 +8779,21 @@ function registerTask(program3) {
8726
8779
  task.command("create <title>").description("Open a new root task for the project team").option("--note <text>", "context, constraints and expected outcome").option("--kind <value>", KIND_HELP).option("--label <value...>", "labels to put on the task").option("--app <id...>", "apps the task touches").option("--infra <ref...>", "shared setup it touches").option("--goal <id>", "the business goal this delivers part of").option(
8727
8780
  "--phase <name>",
8728
8781
  "which slice of that goal \u2014 must match one of its phase names"
8782
+ ).option("--priority <value>", `how urgent (${PRIORITIES2.join(" | ")})`).option(
8783
+ "--size <value>",
8784
+ `small lets the channel start it without asking; standard asks (${SIZES.join(" | ")})`
8785
+ ).option(
8786
+ "--status <value>",
8787
+ `start it somewhere other than 'todo' \u2014 use 'backlog' for a later phase's work (${STATUSES2.join(" | ")})`
8729
8788
  ).action(
8730
8789
  action(async ({ ctx, args, opts }) => {
8731
8790
  requireProject(ctx);
8732
8791
  if (opts.kind !== void 0) assertOneOf("--kind", opts.kind, KINDS2);
8792
+ if (opts.priority !== void 0)
8793
+ assertOneOf("--priority", opts.priority, PRIORITIES2);
8794
+ if (opts.size !== void 0) assertOneOf("--size", opts.size, SIZES);
8795
+ if (opts.status !== void 0)
8796
+ assertOneOf("--status", opts.status, STATUSES2);
8733
8797
  const hasChannelOrigin = Boolean(
8734
8798
  ctx.projectChannelId && ctx.projectChannelMessageId
8735
8799
  );
@@ -8743,6 +8807,11 @@ function registerTask(program3) {
8743
8807
  // a goal has to be argued for, never assumed.
8744
8808
  goalId: opts.goal,
8745
8809
  phase: opts.phase,
8810
+ priority: opts.priority,
8811
+ size: opts.size,
8812
+ // `backlog` for a later phase's work: written down so the owner can
8813
+ // read the whole plan, with nothing for the runner to pick up.
8814
+ status: opts.status,
8746
8815
  targets: [
8747
8816
  ...(opts.app ?? []).map((appId) => ({
8748
8817
  kind: "app",
@@ -8817,6 +8886,9 @@ function registerTask(program3) {
8817
8886
  "--infra <ref...>",
8818
8887
  "shared setup it touches (database | storage | auth | hosting | jobs)"
8819
8888
  ).option("--scope <path...>", "files or folders THIS subtask owns").option(
8889
+ "--ref <spec...>",
8890
+ 'what this step should READ first \u2014 a path or URL, optionally "::why it matters" (repeatable)'
8891
+ ).option(
8820
8892
  "--depends-on <id...>",
8821
8893
  "subtasks that must finish first (key or id)"
8822
8894
  ).option(
@@ -8840,6 +8912,10 @@ function registerTask(program3) {
8840
8912
  role: opts.role,
8841
8913
  category: opts.kind,
8842
8914
  scopePaths: opts.scope,
8915
+ // What to read before doing it. Parsed here rather than pushed at
8916
+ // the API so a malformed pointer is dropped where the agent can
8917
+ // still be told, not stored and drawn as an empty chip.
8918
+ attachments: parseRefs(opts.ref),
8843
8919
  dependsOn,
8844
8920
  targets: [
8845
8921
  ...(opts.app ?? []).map((appId) => ({
@@ -8898,6 +8974,9 @@ function registerTask(program3) {
8898
8974
  })
8899
8975
  );
8900
8976
  subtask.command("update <id>").description("Change a subtask \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 subtask's scope entirely").option(
8977
+ "--ref <spec...>",
8978
+ "replaces what this step should READ first \u2014 same form as `subtask add --ref`"
8979
+ ).option(
8901
8980
  "--agent <id>",
8902
8981
  `run this step on a specific CLI (${AGENTS.join(" | ")}); "default" hands it back to the role's own`
8903
8982
  ).option("--model <id>", 'the model that CLI should use; "default" clears it').option(
@@ -8925,6 +9004,10 @@ function registerTask(program3) {
8925
9004
  role: opts.role,
8926
9005
  category: opts.kind,
8927
9006
  scopePaths: opts.scope,
9007
+ // Only sent when `--ref` was actually given: the PATCH replaces
9008
+ // the whole list, so passing `[]` for an untouched flag would
9009
+ // clear a brief the manager set on a previous call.
9010
+ attachments: opts.ref ? parseRefs(opts.ref) : void 0,
8928
9011
  agentOverride: clearable(opts.agent),
8929
9012
  agentModelOverride: clearable(opts.model),
8930
9013
  agentEffortOverride: clearable(opts.effort)
@@ -9365,6 +9448,47 @@ function registerGoal(program3) {
9365
9448
  );
9366
9449
  })
9367
9450
  );
9451
+ goal.command("adopt <id>").description(
9452
+ "File tasks that already exist under a part of this plan \u2014 for work done before the plan was written"
9453
+ ).option("--task <id...>", "the tasks to file under it").option("--phase <name>", "which part of the plan they belong to").action(
9454
+ action(async ({ ctx, args, opts }) => {
9455
+ requireProject(ctx);
9456
+ const goalId = String(args[0]);
9457
+ const taskIds = opts.task ?? [];
9458
+ if (!taskIds.length) {
9459
+ throw new WorkserError(
9460
+ "Name the tasks to file, e.g. `--task WORK-12 WORK-13`."
9461
+ );
9462
+ }
9463
+ const goal2 = await api(
9464
+ ctx,
9465
+ `/v1/project-goals/${encodeURIComponent(goalId)}`
9466
+ );
9467
+ if (!goal2) throw new WorkserError(`No goal ${goalId}.`);
9468
+ const names = (goal2.phases ?? []).map(
9469
+ (p) => typeof p === "string" ? p : p.name
9470
+ );
9471
+ const phase = String(opts.phase ?? "").trim();
9472
+ if (!phase || !names.includes(phase)) {
9473
+ throw new WorkserError(
9474
+ `--phase must be one of this goal's parts: ${names.join(", ")}.`
9475
+ );
9476
+ }
9477
+ const filed = [];
9478
+ for (const taskId of taskIds) {
9479
+ await api(ctx, `/v1/project-tasks/${encodeURIComponent(taskId)}`, {
9480
+ method: "PATCH",
9481
+ body: { goalId, phase }
9482
+ });
9483
+ filed.push(taskId);
9484
+ }
9485
+ ok({ goalId, phase, filed }, () => {
9486
+ success(
9487
+ `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors30.default.bold(phase)}`
9488
+ );
9489
+ });
9490
+ })
9491
+ );
9368
9492
  goal.command("update <id>").description("Change the shape, or move the goal along").option("--title <text>", "the owner's words for what they want").option("--outcome <text>", "what 'done' means").option("--phase <name...>", "replace the ordered phase list").option("--status <value>", `one of: ${STATUSES3.join(" | ")}`).action(
9369
9493
  action(async ({ ctx, args, opts }) => {
9370
9494
  if (opts.status && !STATUSES3.includes(opts.status)) {
@@ -11066,7 +11190,7 @@ function colour(d) {
11066
11190
 
11067
11191
  // src/index.ts
11068
11192
  var pkg = {
11069
- version: true ? "0.6.10" : "0.0.0-dev"
11193
+ version: true ? "0.6.12" : "0.0.0-dev"
11070
11194
  };
11071
11195
  var program2 = new Command();
11072
11196
  program2.name("workser").description(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workser/cli",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
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",