@islamihab/kds 0.2.4 → 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.
Files changed (2) hide show
  1. package/index.js +188 -8
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -14597,7 +14597,7 @@ import { join } from "path";
14597
14597
  // package.json
14598
14598
  var package_default = {
14599
14599
  name: "cli",
14600
- version: "0.2.4",
14600
+ version: "0.3.0",
14601
14601
  private: true,
14602
14602
  type: "module",
14603
14603
  bin: {
@@ -15153,7 +15153,17 @@ var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
15153
15153
  var PAGE_MODES = ["themed", "raw"];
15154
15154
  var PAGE_VISIBILITIES = ["public", "private"];
15155
15155
  var MAX_PAGE_HTML_BYTES = 4000000;
15156
- var ISSUE_STATUSES = ["backlog", "todo", "in_progress", "done", "canceled"];
15156
+ var ISSUE_STATUSES = [
15157
+ "backlog",
15158
+ "todo",
15159
+ "in_progress",
15160
+ "ready_for_review",
15161
+ "in_review",
15162
+ "changes_requested",
15163
+ "approved",
15164
+ "done",
15165
+ "canceled"
15166
+ ];
15157
15167
  var ISSUE_PRIORITIES = ["urgent", "high", "medium", "low", "none"];
15158
15168
  var ISSUE_DISPOSITIONS = [
15159
15169
  "needs_triage",
@@ -15169,6 +15179,15 @@ var ISSUE_CREATE_DISPOSITIONS = [
15169
15179
  ];
15170
15180
  var ISSUE_ESTIMATES = [1, 2, 3, 5, 8];
15171
15181
  var ISSUE_TERMINAL_STATUSES = ["done", "canceled"];
15182
+ var ISSUE_OPEN_STATUSES = [
15183
+ "backlog",
15184
+ "todo",
15185
+ "in_progress",
15186
+ "ready_for_review",
15187
+ "in_review",
15188
+ "changes_requested",
15189
+ "approved"
15190
+ ];
15172
15191
  var ISSUE_RELATION_KINDS = ["blocks", "blocked_by", "duplicate_of", "duplicated_by", "relates_to"];
15173
15192
  var PROJECT_STATUSES = ["planned", "in_progress", "paused", "completed", "canceled"];
15174
15193
  var PROJECT_HEALTHS = ["on_track", "at_risk", "off_track"];
@@ -15176,6 +15195,49 @@ var ISSUE_IDENTIFIER_PREFIX = "KAI";
15176
15195
  var ISSUE_LIST_PAGE_SIZE = 50;
15177
15196
  var MAX_ISSUE_ATTACHMENT_BYTES = 1e7;
15178
15197
  var ISSUE_DUE_DATE_MODES = ["overdue", "due_today", "due_soon", "no_due_date"];
15198
+ var BUILT_IN_ISSUE_VIEWS = {
15199
+ all: {
15200
+ layout: "list",
15201
+ groupBy: "status",
15202
+ orderBy: "updated_at",
15203
+ orderDirection: "desc",
15204
+ filters: {}
15205
+ },
15206
+ active: {
15207
+ layout: "list",
15208
+ groupBy: "status",
15209
+ orderBy: "updated_at",
15210
+ orderDirection: "desc",
15211
+ filters: { statuses: [...ISSUE_OPEN_STATUSES] }
15212
+ },
15213
+ agent_queue: {
15214
+ layout: "list",
15215
+ groupBy: "status",
15216
+ orderBy: "updated_at",
15217
+ orderDirection: "desc",
15218
+ filters: {
15219
+ statuses: [...ISSUE_OPEN_STATUSES],
15220
+ dispositions: ["ready_for_agent"]
15221
+ }
15222
+ },
15223
+ triage: {
15224
+ layout: "list",
15225
+ groupBy: "disposition",
15226
+ orderBy: "updated_at",
15227
+ orderDirection: "desc",
15228
+ filters: {
15229
+ statuses: [...ISSUE_OPEN_STATUSES],
15230
+ dispositions: ["needs_triage", "needs_info"]
15231
+ }
15232
+ },
15233
+ dispatch: {
15234
+ layout: "board",
15235
+ groupBy: "none",
15236
+ orderBy: "updated_at",
15237
+ orderDirection: "desc",
15238
+ filters: { statuses: [...ISSUE_OPEN_STATUSES, "done"] }
15239
+ }
15240
+ };
15179
15241
  var issueIsOpen = (status) => !ISSUE_TERMINAL_STATUSES.some((terminal) => terminal === status);
15180
15242
  var MAX_PROJECT_REPO_LENGTH = 200;
15181
15243
 
@@ -18259,6 +18321,7 @@ var create = command({
18259
18321
  project: exports_external.string().optional().describe("Project to create the issue in (id or URL)"),
18260
18322
  here: exports_external.boolean().default(false).describe("Create in the checkout's connected project"),
18261
18323
  milestone: exports_external.string().optional().describe("Milestone name (needs --project or --here)"),
18324
+ parent: exports_external.string().optional().describe("Create as a sub-issue of this issue (identifier, number, or URL)"),
18262
18325
  disposition: exports_external.enum(ISSUE_CREATE_DISPOSITIONS).optional().describe("Route immediately (default needs_triage)")
18263
18326
  },
18264
18327
  run: async ({ positionals: { title }, options }) => {
@@ -18269,6 +18332,7 @@ var create = command({
18269
18332
  if (options.milestone && !project)
18270
18333
  throw new Error("A milestone needs --project or --here.");
18271
18334
  const milestone = project && options.milestone ? await resolveMilestone(client3, project._id, options.milestone) : undefined;
18335
+ const parent = options.parent ? await resolveIssue(client3, options.parent) : undefined;
18272
18336
  const { identifier } = await client3.mutation(api2.issues.create, {
18273
18337
  title,
18274
18338
  descriptionMarkdown: options.description === undefined ? undefined : await readTextOption(options.description),
@@ -18278,12 +18342,24 @@ var create = command({
18278
18342
  dueDate: options.due,
18279
18343
  projectId: project?._id,
18280
18344
  milestoneId: milestone?._id,
18345
+ parentId: parent?._id,
18281
18346
  disposition: options.disposition
18282
18347
  });
18283
18348
  console.log(identifier);
18284
18349
  }
18285
18350
  });
18286
18351
 
18352
+ // src/lib/tasks.ts
18353
+ var printTasks = (tasks) => {
18354
+ if (tasks.length === 0)
18355
+ return;
18356
+ const width = String(tasks.length).length;
18357
+ console.log(`Tasks: ${tasks.filter((task2) => task2.isDone).length}/${tasks.length}`);
18358
+ for (const [index, task2] of tasks.entries()) {
18359
+ console.log(` ${String(index + 1).padStart(width)} [${task2.isDone ? "x" : " "}] ${task2.title}`);
18360
+ }
18361
+ };
18362
+
18287
18363
  // src/commands/issues/get.ts
18288
18364
  var get = command({
18289
18365
  name: "get",
@@ -18297,13 +18373,14 @@ var get = command({
18297
18373
  run: async ({ positionals: { id }, options: { json: json2 } }) => {
18298
18374
  const client3 = await backendClient();
18299
18375
  const issue2 = await resolveIssue(client3, id);
18300
- const [feed, relations, attachments] = await Promise.all([
18376
+ const [feed, relations, attachments, tasks] = await Promise.all([
18301
18377
  client3.query(api2.issues.feed, { id: issue2._id }),
18302
18378
  client3.query(api2.issues.relations, { id: issue2._id }),
18303
- client3.query(api2.issueAttachments.list, { issueId: issue2._id })
18379
+ client3.query(api2.issueAttachments.list, { issueId: issue2._id }),
18380
+ client3.query(api2.issueTasks.list, { issueId: issue2._id })
18304
18381
  ]);
18305
18382
  if (json2)
18306
- return console.log(JSON.stringify({ ...issue2, relations, attachments, feed }, null, 2));
18383
+ return console.log(JSON.stringify({ ...issue2, relations, attachments, tasks, feed }, null, 2));
18307
18384
  const project = issue2.projectId ? await client3.query(api2.projects.get, { id: issue2.projectId, today: localToday() }) : null;
18308
18385
  const milestone = issue2.milestoneId ? await client3.query(api2.milestones.get, { id: issue2.milestoneId, today: localToday() }) : null;
18309
18386
  console.log(`${issue2.identifier} ${issue2.title}`);
@@ -18341,6 +18418,7 @@ var get = command({
18341
18418
  console.log(` ${attachment.name} \u2014 ${attachment.url ?? "unavailable"}`);
18342
18419
  }
18343
18420
  }
18421
+ printTasks(tasks);
18344
18422
  if (issue2.descriptionMarkdown)
18345
18423
  console.log(`
18346
18424
  ${issue2.descriptionMarkdown}`);
@@ -18549,7 +18627,7 @@ var set2 = command({
18549
18627
  var shellArg = (value) => /^[\w./-]+$/.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
18550
18628
  var start = command({
18551
18629
  name: "start",
18552
- description: "Claim an issue: move it to in_progress and record the branch to work under",
18630
+ description: "Claim implementation work: move unstarted or changes_requested work to in_progress with its branch",
18553
18631
  positionals: {
18554
18632
  id: exports_external.string().describe("Issue identifier, number, or URL")
18555
18633
  },
@@ -18574,6 +18652,90 @@ var start = command({
18574
18652
  }
18575
18653
  });
18576
18654
 
18655
+ // src/commands/issues/start-review.ts
18656
+ var startReview = command({
18657
+ name: "start-review",
18658
+ description: "Claim review work: move a ready_for_review issue to in_review",
18659
+ positionals: {
18660
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18661
+ },
18662
+ run: async ({ positionals: { id } }) => {
18663
+ const client3 = await backendClient();
18664
+ const issue2 = await resolveIssue(client3, id);
18665
+ await client3.mutation(api2.issues.startReview, { id: issue2._id });
18666
+ console.log(`Reviewing ${issue2.identifier}: in_review`);
18667
+ }
18668
+ });
18669
+
18670
+ // src/commands/issues/submit.ts
18671
+ var submit = command({
18672
+ name: "submit",
18673
+ description: "Hand an issue off for review: move in_progress or changes_requested work to ready_for_review",
18674
+ positionals: {
18675
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18676
+ },
18677
+ run: async ({ positionals: { id } }) => {
18678
+ const client3 = await backendClient();
18679
+ const issue2 = await resolveIssue(client3, id);
18680
+ await client3.mutation(api2.issues.submit, { id: issue2._id });
18681
+ console.log(`Submitted ${issue2.identifier}: ready_for_review`);
18682
+ }
18683
+ });
18684
+
18685
+ // src/commands/issues/tasks.ts
18686
+ var position = exports_external.array(exports_external.coerce.number().int().positive());
18687
+ var tasks = command({
18688
+ name: "tasks",
18689
+ description: "Show an issue's checklist, or add, tick, promote, and drop its tasks",
18690
+ positionals: {
18691
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18692
+ },
18693
+ options: {
18694
+ add: exports_external.array(exports_external.string()).optional().describe("Add a task (repeatable)").meta({ short: "a" }),
18695
+ check: position.optional().describe("Tick a task by its number (repeatable)"),
18696
+ uncheck: position.optional().describe("Untick a task by its number (repeatable)"),
18697
+ convert: position.optional().describe("Promote a task into its own issue (repeatable)"),
18698
+ remove: position.optional().describe("Drop a task by its number (repeatable)"),
18699
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18700
+ },
18701
+ run: async ({ positionals: { id }, options: { add, check: check2, uncheck, convert, remove: remove2, json: json2 } }) => {
18702
+ const client3 = await backendClient();
18703
+ const issue2 = await resolveIssue(client3, id);
18704
+ const before = await client3.query(api2.issueTasks.list, { issueId: issue2._id });
18705
+ const at = (numbered) => (numbered ?? []).map((number4) => {
18706
+ const task2 = before[number4 - 1];
18707
+ if (!task2)
18708
+ throw new Error(`${issue2.identifier} has no task ${number4}.`);
18709
+ return task2;
18710
+ });
18711
+ const ticked = at(check2);
18712
+ const unticked = at(uncheck);
18713
+ const promoted = at(convert);
18714
+ const dropped = at(remove2);
18715
+ for (const title of add ?? [])
18716
+ await client3.mutation(api2.issueTasks.create, { issueId: issue2._id, title });
18717
+ for (const task2 of ticked)
18718
+ await client3.mutation(api2.issueTasks.update, { id: task2._id, isDone: true });
18719
+ for (const task2 of unticked)
18720
+ await client3.mutation(api2.issueTasks.update, { id: task2._id, isDone: false });
18721
+ for (const task2 of promoted) {
18722
+ const created = await client3.mutation(api2.issueTasks.convert, { id: task2._id });
18723
+ if (!json2) {
18724
+ console.log(`${created.identifier} ${created.nested ? "created as a sub-issue" : "created and related"}: ${task2.title}`);
18725
+ }
18726
+ }
18727
+ for (const task2 of dropped)
18728
+ await client3.mutation(api2.issueTasks.remove, { id: task2._id });
18729
+ const wrote = [add, check2, uncheck, convert, remove2].some((values) => values !== undefined);
18730
+ const after = wrote ? await client3.query(api2.issueTasks.list, { issueId: issue2._id }) : before;
18731
+ if (json2)
18732
+ return console.log(JSON.stringify(after, null, 2));
18733
+ if (after.length === 0)
18734
+ return console.log(`${issue2.identifier} has no tasks.`);
18735
+ printTasks(after);
18736
+ }
18737
+ });
18738
+
18577
18739
  // src/commands/issues/unrelate.ts
18578
18740
  var relationEntries = (relations, kind) => {
18579
18741
  switch (kind) {
@@ -18644,7 +18806,22 @@ var update = command({
18644
18806
  var issues = group({
18645
18807
  name: "issues",
18646
18808
  description: "Track and triage issues",
18647
- commands: [create, list, get, update, set2, route, start, relate, unrelate, comment, remove]
18809
+ commands: [
18810
+ create,
18811
+ list,
18812
+ get,
18813
+ update,
18814
+ set2,
18815
+ route,
18816
+ start,
18817
+ submit,
18818
+ startReview,
18819
+ tasks,
18820
+ relate,
18821
+ unrelate,
18822
+ comment,
18823
+ remove
18824
+ ]
18648
18825
  });
18649
18826
 
18650
18827
  // src/lib/group.ts
@@ -19079,6 +19256,9 @@ var rootCommand = group({
19079
19256
  }
19080
19257
  });
19081
19258
 
19259
+ // src/lib/errors.ts
19260
+ var errorMessage = (error51) => error51 instanceof ConvexError && typeof error51.data === "string" ? error51.data : error51.message;
19261
+
19082
19262
  // src/lib/update-check.ts
19083
19263
  var latestVersion = async () => {
19084
19264
  const cached2 = await Bun.file(CACHE_PATH).json().catch(() => null).then(cacheSchema.safeParse);
@@ -19116,7 +19296,7 @@ try {
19116
19296
  if (args[0] !== "upgrade")
19117
19297
  await maybeNotifyUpdate();
19118
19298
  } catch (error51) {
19119
- console.error(error51.message);
19299
+ console.error(errorMessage(error51));
19120
19300
  process.exitCode = 1;
19121
19301
  } finally {
19122
19302
  closePrompts();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {