@islamihab/kds 0.2.3 → 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 +203 -12
  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.3",
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
 
@@ -17981,7 +18043,7 @@ var runInbox = async (options) => {
17981
18043
  const inbox = await client3.query(api2.inbox.list, {});
17982
18044
  const targetLastSeen = new Map(inbox.targetLastSeen.map((state) => [state.targetId, state.lastSeenAt]));
17983
18045
  const events = options.all ? inbox.events : inbox.events.filter((event) => {
17984
- const targetId = event.type === "issue" ? event.issue.id : event.project.id;
18046
+ const targetId = event.type === "project" ? event.project.id : event.issue.id;
17985
18047
  return event.at > Math.max(inbox.lastSeenAt, targetLastSeen.get(targetId) ?? 0);
17986
18048
  });
17987
18049
  if (options.json)
@@ -18000,8 +18062,8 @@ var runInbox = async (options) => {
18000
18062
  ...events.map((event) => [
18001
18063
  new Date(event.at).toISOString().slice(0, 16).replace("T", " "),
18002
18064
  actorLabel(event.via, event.agent),
18003
- event.type === "issue" ? event.issue.identifier : event.project.name,
18004
- event.type === "issue" ? issueActivityLine(event.detail) : projectActivityLine(event.detail)
18065
+ event.type === "project" ? event.project.name : event.issue.identifier,
18066
+ event.type === "project" ? projectActivityLine(event.detail) : event.type === "comment" ? `commented: ${event.excerpt}` : issueActivityLine(event.detail)
18005
18067
  ])
18006
18068
  ]);
18007
18069
  if (inbox.truncated)
@@ -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,20 +18627,115 @@ 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
  },
18556
- run: async ({ positionals: { id } }) => {
18634
+ options: {
18635
+ ready: exports_external.boolean().default(false).describe("Also route the issue to ready_for_agent")
18636
+ },
18637
+ run: async ({ positionals: { id }, options: { ready } }) => {
18557
18638
  const client3 = await backendClient();
18558
18639
  const issue2 = await resolveIssue(client3, id);
18559
18640
  const { branch } = await client3.mutation(api2.issues.start, { id: issue2._id });
18641
+ if (ready && issue2.disposition !== "ready_for_agent") {
18642
+ await client3.mutation(api2.issues.route, { id: issue2._id, disposition: "ready_for_agent" });
18643
+ }
18560
18644
  console.log(`Started ${issue2.identifier}: in_progress, branch ${branch}`);
18645
+ if (ready) {
18646
+ console.log("Routed to ready_for_agent.");
18647
+ } else if (issue2.disposition !== "ready_for_agent" && await agentClaim()) {
18648
+ console.log(`Note: disposition is ${issue2.disposition}. Pass --ready if this hand-off was intended.`);
18649
+ }
18561
18650
  console.log(`
18562
18651
  git switch -c ${shellArg(branch)}`);
18563
18652
  }
18564
18653
  });
18565
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
+
18566
18739
  // src/commands/issues/unrelate.ts
18567
18740
  var relationEntries = (relations, kind) => {
18568
18741
  switch (kind) {
@@ -18633,7 +18806,22 @@ var update = command({
18633
18806
  var issues = group({
18634
18807
  name: "issues",
18635
18808
  description: "Track and triage issues",
18636
- 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
+ ]
18637
18825
  });
18638
18826
 
18639
18827
  // src/lib/group.ts
@@ -19068,6 +19256,9 @@ var rootCommand = group({
19068
19256
  }
19069
19257
  });
19070
19258
 
19259
+ // src/lib/errors.ts
19260
+ var errorMessage = (error51) => error51 instanceof ConvexError && typeof error51.data === "string" ? error51.data : error51.message;
19261
+
19071
19262
  // src/lib/update-check.ts
19072
19263
  var latestVersion = async () => {
19073
19264
  const cached2 = await Bun.file(CACHE_PATH).json().catch(() => null).then(cacheSchema.safeParse);
@@ -19105,7 +19296,7 @@ try {
19105
19296
  if (args[0] !== "upgrade")
19106
19297
  await maybeNotifyUpdate();
19107
19298
  } catch (error51) {
19108
- console.error(error51.message);
19299
+ console.error(errorMessage(error51));
19109
19300
  process.exitCode = 1;
19110
19301
  } finally {
19111
19302
  closePrompts();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {