@islamihab/kds 0.4.0 → 0.6.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 +143 -11
  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.4.0",
14600
+ version: "0.6.0",
14601
14601
  private: true,
14602
14602
  type: "module",
14603
14603
  bin: {
@@ -15188,6 +15188,14 @@ var ISSUE_OPEN_STATUSES = [
15188
15188
  "changes_requested",
15189
15189
  "approved"
15190
15190
  ];
15191
+ var REVIEW_FINDING_CATEGORIES = [
15192
+ "functional_correctness",
15193
+ "maintainability",
15194
+ "security",
15195
+ "performance"
15196
+ ];
15197
+ var REVIEW_FINDING_SEVERITIES = ["critical", "major", "minor", "trivial"];
15198
+ var MAX_REVIEW_FINDINGS = 50;
15191
15199
  var ISSUE_LABEL_COLORS = [
15192
15200
  "gray",
15193
15201
  "red",
@@ -18005,6 +18013,19 @@ var logout = command({
18005
18013
  }
18006
18014
  });
18007
18015
 
18016
+ // src/commands/auth/reviewer-token.ts
18017
+ var reviewerToken = command({
18018
+ name: "reviewer-token",
18019
+ description: "Print a short-lived GitHub token for the reviewer App identity",
18020
+ options: {
18021
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18022
+ },
18023
+ run: async ({ options: { json: json2 } }) => {
18024
+ const minted = await (await backendClient()).action(api2.githubReviewer.mintToken, {});
18025
+ console.log(json2 ? JSON.stringify(minted, null, 2) : minted.token);
18026
+ }
18027
+ });
18028
+
18008
18029
  // src/lib/activity.ts
18009
18030
  var SOURCE_LABELS = { github: "GitHub", agent: "Agent" };
18010
18031
  var actorLabel = (via, agent) => {
@@ -18091,7 +18112,7 @@ var status = command({
18091
18112
  var auth = group({
18092
18113
  name: "auth",
18093
18114
  description: "Manage backend authentication",
18094
- commands: [login, logout, status]
18115
+ commands: [login, logout, status, reviewerToken]
18095
18116
  });
18096
18117
 
18097
18118
  // src/lib/output.ts
@@ -18146,9 +18167,10 @@ var runInbox = async (options) => {
18146
18167
  };
18147
18168
 
18148
18169
  // src/lib/input.ts
18149
- var assertSize = (size) => {
18150
- if (size > MAX_PAGE_HTML_BYTES)
18151
- throw new Error(`Page is too large (${size} bytes; max ${MAX_PAGE_HTML_BYTES}).`);
18170
+ var assertSize = (size, subject = "Page") => {
18171
+ if (size > MAX_PAGE_HTML_BYTES) {
18172
+ throw new Error(`${subject} is too large (${size} bytes; max ${MAX_PAGE_HTML_BYTES}).`);
18173
+ }
18152
18174
  };
18153
18175
  var readBody = async (path) => {
18154
18176
  if (path === "-") {
@@ -18161,6 +18183,18 @@ var readBody = async (path) => {
18161
18183
  return await file2.text();
18162
18184
  };
18163
18185
  var readTextOption = async (value) => value === "-" ? await Bun.stdin.text() : value;
18186
+ var readFileOption = async (path) => {
18187
+ if (path === "-") {
18188
+ const text = await Bun.stdin.text();
18189
+ assertSize(new TextEncoder().encode(text).length, "Input");
18190
+ return text;
18191
+ }
18192
+ const file2 = Bun.file(path);
18193
+ if (!await file2.exists())
18194
+ throw new Error(`No file at ${path}.`);
18195
+ assertSize(file2.size, "Input");
18196
+ return await file2.text();
18197
+ };
18164
18198
  var readIssueRef = (value) => {
18165
18199
  const trimmed = value.trim();
18166
18200
  if (/^\d+$/.test(trimmed))
@@ -18476,7 +18510,7 @@ var get = command({
18476
18510
  ]);
18477
18511
  if (json2)
18478
18512
  return console.log(JSON.stringify({ ...issue2, relations, attachments, tasks, feed }, null, 2));
18479
- const project = issue2.projectId ? await client3.query(api2.projects.get, { id: issue2.projectId, today: localToday() }) : null;
18513
+ const project = issue2.project;
18480
18514
  const milestone = issue2.milestoneId ? await client3.query(api2.milestones.get, { id: issue2.milestoneId, today: localToday() }) : null;
18481
18515
  console.log(`${issue2.identifier} ${issue2.title}`);
18482
18516
  console.log(`Status: ${issue2.status} \xB7 Priority: ${issue2.priority} \xB7 Disposition: ${issue2.disposition}`);
@@ -18685,6 +18719,37 @@ More issues match; raise --limit past ${options.limit}.`);
18685
18719
  }
18686
18720
  });
18687
18721
 
18722
+ // src/commands/issues/mark-addressed.ts
18723
+ var parseFindingRef = (value) => {
18724
+ const match = /^(.+):([1-9]\d*)$/.exec(value);
18725
+ if (!match?.[1] || !match[2])
18726
+ throw new Error(`--finding takes path:line, got "${value}".`);
18727
+ return { path: match[1], line: Number(match[2]) };
18728
+ };
18729
+ var markAddressed = command({
18730
+ name: "mark-addressed",
18731
+ description: "Mark review findings addressed by a commit and resolve their conversations",
18732
+ positionals: {
18733
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18734
+ },
18735
+ options: {
18736
+ commit: exports_external.string().describe("The commit sha that addressed the findings").meta({ short: "c" }),
18737
+ finding: exports_external.array(exports_external.string()).optional().describe("Mark only this finding, as path:line (repeatable; default all)")
18738
+ },
18739
+ run: async ({ positionals: { id }, options: { commit, finding } }) => {
18740
+ const client3 = await backendClient();
18741
+ const issue2 = await resolveIssue(client3, id);
18742
+ const result = await client3.action(api2.githubReviewer.markAddressed, {
18743
+ id: issue2._id,
18744
+ commitSha: commit,
18745
+ findings: finding?.map(parseFindingRef)
18746
+ });
18747
+ const already = result.alreadyMarked.length ? ` (${result.alreadyMarked.length} already marked)` : "";
18748
+ console.log(`Marked ${result.marked.length} on ${result.identifier} as addressed in ${commit}${already}.`);
18749
+ console.log(result.resolved ? `Resolved ${result.resolved} conversation${result.resolved === 1 ? "" : "s"}.` : "All conversations already resolved.");
18750
+ }
18751
+ });
18752
+
18688
18753
  // src/commands/issues/relate.ts
18689
18754
  var relate = command({
18690
18755
  name: "relate",
@@ -18777,10 +18842,10 @@ var set2 = command({
18777
18842
  throw new Error("Nothing to set. Pass --status, --priority, --estimate, --due, --project, --milestone, --parent, --agent-config, or --label.");
18778
18843
  const client3 = await backendClient();
18779
18844
  const issue2 = await resolveIssue(client3, id);
18780
- const projectId = project === undefined ? issue2.projectId : project === null ? undefined : (await resolveProject(client3, project))._id;
18845
+ const projectId = project === undefined ? issue2.project?._id : project === null ? undefined : (await resolveProject(client3, project))._id;
18781
18846
  let milestoneId;
18782
18847
  if (milestone === undefined) {
18783
- milestoneId = projectId === issue2.projectId ? issue2.milestoneId : undefined;
18848
+ milestoneId = projectId === issue2.project?._id ? issue2.milestoneId : undefined;
18784
18849
  } else if (milestone !== null) {
18785
18850
  if (projectId === undefined)
18786
18851
  throw new Error("A milestone needs a project. Pass --project too.");
@@ -18846,15 +18911,16 @@ var start = command({
18846
18911
  // src/commands/issues/start-review.ts
18847
18912
  var startReview = command({
18848
18913
  name: "start-review",
18849
- description: "Claim review work: move a ready_for_review issue to in_review",
18914
+ description: "Claim review work: move a ready_for_review issue to in_review and announce the review on its pull request",
18850
18915
  positionals: {
18851
18916
  id: exports_external.string().describe("Issue identifier, number, or URL")
18852
18917
  },
18853
18918
  run: async ({ positionals: { id } }) => {
18854
18919
  const client3 = await backendClient();
18855
18920
  const issue2 = await resolveIssue(client3, id);
18856
- await client3.mutation(api2.issues.startReview, { id: issue2._id });
18857
- console.log(`Reviewing ${issue2.identifier}: in_review`);
18921
+ const started = await client3.action(api2.githubReviewer.startReview, { id: issue2._id });
18922
+ console.log(`Reviewing ${started.identifier}: in_review`);
18923
+ console.log(`Announced on ${started.prUrl}; check run open.`);
18858
18924
  }
18859
18925
  });
18860
18926
 
@@ -18873,6 +18939,70 @@ var submit = command({
18873
18939
  }
18874
18940
  });
18875
18941
 
18942
+ // src/commands/issues/submit-review.ts
18943
+ var findingsSchema = exports_external.array(exports_external.object({
18944
+ path: exports_external.string().min(1),
18945
+ line: exports_external.number().int().positive(),
18946
+ startLine: exports_external.number().int().positive().optional(),
18947
+ category: exports_external.enum(REVIEW_FINDING_CATEGORIES),
18948
+ severity: exports_external.enum(REVIEW_FINDING_SEVERITIES),
18949
+ summary: exports_external.string().min(1),
18950
+ body: exports_external.string().min(1),
18951
+ diff: exports_external.string().optional()
18952
+ })).min(1).max(MAX_REVIEW_FINDINGS);
18953
+ var readFindings = async (path) => {
18954
+ const text = await readFileOption(path);
18955
+ let json2;
18956
+ try {
18957
+ json2 = JSON.parse(text);
18958
+ } catch {
18959
+ throw new Error("The findings file is not valid JSON.");
18960
+ }
18961
+ const parsed = findingsSchema.safeParse(json2);
18962
+ if (!parsed.success)
18963
+ throw new Error(`The findings file is invalid:
18964
+ ${exports_external.prettifyError(parsed.error)}`);
18965
+ return parsed.data;
18966
+ };
18967
+ var submitReview = command({
18968
+ name: "submit-review",
18969
+ description: "Submit the review verdict on an issue's linked pull request as the reviewer App",
18970
+ positionals: {
18971
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18972
+ },
18973
+ options: {
18974
+ approve: exports_external.boolean().default(false).describe("Approve: bare APPROVE plus a clean bill"),
18975
+ "request-changes": exports_external.boolean().default(false).describe("Request changes from a findings file"),
18976
+ findings: exports_external.string().optional().describe("Findings JSON file, or - for stdin (with --request-changes)"),
18977
+ bill: exports_external.string().optional().describe("Clean-bill text, or - for stdin (with --approve)")
18978
+ },
18979
+ run: async ({ positionals: { id }, options }) => {
18980
+ if (options.approve === options["request-changes"]) {
18981
+ throw new Error("Pass exactly one of --approve or --request-changes.");
18982
+ }
18983
+ if (options.approve && options.findings !== undefined)
18984
+ throw new Error("--findings goes with --request-changes.");
18985
+ if (options["request-changes"] && options.bill !== undefined)
18986
+ throw new Error("--bill goes with --approve.");
18987
+ let verdict;
18988
+ if (options.approve) {
18989
+ if (options.bill === undefined)
18990
+ throw new Error("--approve needs the clean bill: --bill <text>, or - for stdin.");
18991
+ verdict = { kind: "approve", bill: await readTextOption(options.bill) };
18992
+ } else {
18993
+ if (options.findings === undefined) {
18994
+ throw new Error("--request-changes needs the findings: --findings <file>, or - for stdin.");
18995
+ }
18996
+ verdict = { kind: "request_changes", findings: await readFindings(options.findings) };
18997
+ }
18998
+ const client3 = await backendClient();
18999
+ const issue2 = await resolveIssue(client3, id);
19000
+ const submitted = await client3.action(api2.githubReviewer.submitReview, { id: issue2._id, verdict });
19001
+ console.log(`Submitted review on ${submitted.identifier}: ${submitted.prUrl}`);
19002
+ console.log(submitted.summary);
19003
+ }
19004
+ });
19005
+
18876
19006
  // src/commands/issues/tasks.ts
18877
19007
  var position = exports_external.array(exports_external.coerce.number().int().positive());
18878
19008
  var tasks = command({
@@ -19008,6 +19138,8 @@ var issues = group({
19008
19138
  start,
19009
19139
  submit,
19010
19140
  startReview,
19141
+ submitReview,
19142
+ markAddressed,
19011
19143
  tasks,
19012
19144
  relate,
19013
19145
  unrelate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {