@islamihab/kds 0.5.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 +126 -7
  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.5.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",
@@ -18159,9 +18167,10 @@ var runInbox = async (options) => {
18159
18167
  };
18160
18168
 
18161
18169
  // src/lib/input.ts
18162
- var assertSize = (size) => {
18163
- if (size > MAX_PAGE_HTML_BYTES)
18164
- 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
+ }
18165
18174
  };
18166
18175
  var readBody = async (path) => {
18167
18176
  if (path === "-") {
@@ -18174,6 +18183,18 @@ var readBody = async (path) => {
18174
18183
  return await file2.text();
18175
18184
  };
18176
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
+ };
18177
18198
  var readIssueRef = (value) => {
18178
18199
  const trimmed = value.trim();
18179
18200
  if (/^\d+$/.test(trimmed))
@@ -18698,6 +18719,37 @@ More issues match; raise --limit past ${options.limit}.`);
18698
18719
  }
18699
18720
  });
18700
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
+
18701
18753
  // src/commands/issues/relate.ts
18702
18754
  var relate = command({
18703
18755
  name: "relate",
@@ -18859,15 +18911,16 @@ var start = command({
18859
18911
  // src/commands/issues/start-review.ts
18860
18912
  var startReview = command({
18861
18913
  name: "start-review",
18862
- 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",
18863
18915
  positionals: {
18864
18916
  id: exports_external.string().describe("Issue identifier, number, or URL")
18865
18917
  },
18866
18918
  run: async ({ positionals: { id } }) => {
18867
18919
  const client3 = await backendClient();
18868
18920
  const issue2 = await resolveIssue(client3, id);
18869
- await client3.mutation(api2.issues.startReview, { id: issue2._id });
18870
- 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.`);
18871
18924
  }
18872
18925
  });
18873
18926
 
@@ -18886,6 +18939,70 @@ var submit = command({
18886
18939
  }
18887
18940
  });
18888
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
+
18889
19006
  // src/commands/issues/tasks.ts
18890
19007
  var position = exports_external.array(exports_external.coerce.number().int().positive());
18891
19008
  var tasks = command({
@@ -19021,6 +19138,8 @@ var issues = group({
19021
19138
  start,
19022
19139
  submit,
19023
19140
  startReview,
19141
+ submitReview,
19142
+ markAddressed,
19024
19143
  tasks,
19025
19144
  relate,
19026
19145
  unrelate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {