@islamihab/kds 0.1.3 → 0.2.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 +761 -66
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -14587,7 +14587,7 @@ import { join } from "path";
14587
14587
  // package.json
14588
14588
  var package_default = {
14589
14589
  name: "cli",
14590
- version: "0.1.3",
14590
+ version: "0.2.0",
14591
14591
  private: true,
14592
14592
  type: "module",
14593
14593
  bin: {
@@ -14626,7 +14626,7 @@ var DEFAULT_SERVER_URL = "https://dashboard.kaidev.studio";
14626
14626
  var DISCOVERY_PATH = "/api/discovery";
14627
14627
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
14628
14628
  var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
14629
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/values/base64.js
14629
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/values/base64.js
14630
14630
  var lookup = [];
14631
14631
  var revLookup = [];
14632
14632
  var Arr = Uint8Array;
@@ -14710,7 +14710,7 @@ function fromByteArray(uint8) {
14710
14710
  return parts.join("");
14711
14711
  }
14712
14712
 
14713
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/common/index.js
14713
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/common/index.js
14714
14714
  function parseArgs2(args) {
14715
14715
  if (args === undefined) {
14716
14716
  return {};
@@ -14746,13 +14746,33 @@ function isSimpleObject(value) {
14746
14746
  return isObject2 && isSimple;
14747
14747
  }
14748
14748
 
14749
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/values/value.js
14749
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/values/value.js
14750
14750
  var LITTLE_ENDIAN = true;
14751
14751
  var MIN_INT64 = BigInt("-9223372036854775808");
14752
14752
  var MAX_INT64 = BigInt("9223372036854775807");
14753
14753
  var ZERO = BigInt("0");
14754
14754
  var EIGHT = BigInt("8");
14755
14755
  var TWOFIFTYSIX = BigInt("256");
14756
+ var COMMIT_TS_UNRESOLVED = "This commit timestamp is unresolved: its value is assigned when the mutation commits. Read the document after the mutation completes to get its value.";
14757
+
14758
+ class CommitTsPlaceholder {
14759
+ [Symbol.toPrimitive](hint) {
14760
+ if (hint === "string") {
14761
+ return this.toString();
14762
+ }
14763
+ throw new Error(COMMIT_TS_UNRESOLVED);
14764
+ }
14765
+ valueOf() {
14766
+ throw new Error(COMMIT_TS_UNRESOLVED);
14767
+ }
14768
+ toJSON() {
14769
+ throw new Error(COMMIT_TS_UNRESOLVED);
14770
+ }
14771
+ toString() {
14772
+ return "[unresolved commit timestamp]";
14773
+ }
14774
+ }
14775
+ var commitTsPlaceholder = new CommitTsPlaceholder;
14756
14776
  function isSpecial(n) {
14757
14777
  return Number.isNaN(n) || !Number.isFinite(n) || Object.is(n, -0);
14758
14778
  }
@@ -14869,6 +14889,12 @@ function jsonToConvex(value) {
14869
14889
  }
14870
14890
  return float;
14871
14891
  }
14892
+ if (key === "$commitTs") {
14893
+ if (value.$commitTs !== null) {
14894
+ throw new Error(`Malformed $commitTs field on ${value}`);
14895
+ }
14896
+ return commitTsPlaceholder;
14897
+ }
14872
14898
  if (key === "$set") {
14873
14899
  throw new Error(`Received a Set which is no longer supported as a Convex type.`);
14874
14900
  }
@@ -14937,6 +14963,9 @@ function convexToJsonInternal(value, originalValue, context, includeTopLevelUnde
14937
14963
  if (value instanceof ArrayBuffer) {
14938
14964
  return { $bytes: fromByteArray(new Uint8Array(value)) };
14939
14965
  }
14966
+ if (value instanceof CommitTsPlaceholder) {
14967
+ return { $commitTs: null };
14968
+ }
14940
14969
  if (Array.isArray(value)) {
14941
14970
  return value.map((value2, i2) => convexToJsonInternal(value2, originalValue, context + `[${i2}]`, false));
14942
14971
  }
@@ -14985,10 +15014,10 @@ function convexOrUndefinedToJsonInternal(value, originalValue, context) {
14985
15014
  function convexToJson(value) {
14986
15015
  return convexToJsonInternal(value, value, "", false);
14987
15016
  }
14988
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/server/functionName.js
15017
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/server/functionName.js
14989
15018
  var functionName = Symbol.for("functionName");
14990
15019
 
14991
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/server/components/paths.js
15020
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/server/components/paths.js
14992
15021
  var toReferencePath = Symbol.for("toReferencePath");
14993
15022
  function extractReferencePath(reference) {
14994
15023
  return reference[toReferencePath] ?? null;
@@ -15016,7 +15045,7 @@ function getFunctionAddress(functionReference) {
15016
15045
  return functionAddress;
15017
15046
  }
15018
15047
 
15019
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/server/api.js
15048
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/server/api.js
15020
15049
  function getFunctionName(functionReference) {
15021
15050
  const address = getFunctionAddress(functionReference);
15022
15051
  if (address.name === undefined) {
@@ -15063,10 +15092,10 @@ function createApi(pathParts = []) {
15063
15092
  return new Proxy({}, handler);
15064
15093
  }
15065
15094
  var anyApi = createApi();
15066
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/index.js
15067
- var version2 = "1.42.1";
15095
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/index.js
15096
+ var version2 = "1.43.0";
15068
15097
 
15069
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/values/errors.js
15098
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/values/errors.js
15070
15099
  var __defProp2 = Object.defineProperty;
15071
15100
  var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15072
15101
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -15084,7 +15113,7 @@ class ConvexError extends (_b = Error, _a3 = IDENTIFYING_FIELD, _b) {
15084
15113
  }
15085
15114
  }
15086
15115
 
15087
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/server/components/index.js
15116
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/server/components/index.js
15088
15117
  function createChildComponents(root, pathParts) {
15089
15118
  const handler = {
15090
15119
  get(_, prop) {
@@ -15114,13 +15143,36 @@ var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
15114
15143
  var PAGE_MODES = ["themed", "raw"];
15115
15144
  var PAGE_VISIBILITIES = ["public", "private"];
15116
15145
  var MAX_PAGE_HTML_BYTES = 4000000;
15146
+ var ISSUE_STATUSES = ["backlog", "todo", "in_progress", "done", "canceled"];
15147
+ var ISSUE_PRIORITIES = ["urgent", "high", "medium", "low", "none"];
15148
+ var ISSUE_DISPOSITIONS = [
15149
+ "needs_triage",
15150
+ "needs_info",
15151
+ "ready_for_agent",
15152
+ "ready_for_human",
15153
+ "wontfix"
15154
+ ];
15155
+ var ISSUE_CREATE_DISPOSITIONS = [
15156
+ "needs_triage",
15157
+ "ready_for_agent",
15158
+ "ready_for_human"
15159
+ ];
15160
+ var ISSUE_ESTIMATES = [1, 2, 3, 5, 8];
15161
+ var ISSUE_TERMINAL_STATUSES = ["done", "canceled"];
15162
+ var PROJECT_STATUSES = ["planned", "in_progress", "paused", "completed", "canceled"];
15163
+ var PROJECT_HEALTHS = ["on_track", "at_risk", "off_track"];
15164
+ var ISSUE_IDENTIFIER_PREFIX = "KAI";
15165
+ var ISSUE_LIST_PAGE_SIZE = 50;
15166
+ var ISSUE_DUE_DATE_MODES = ["overdue", "due_today", "due_soon", "no_due_date"];
15167
+ var issueIsOpen = (status) => !ISSUE_TERMINAL_STATUSES.some((terminal) => terminal === status);
15168
+ var MAX_PROJECT_REPO_LENGTH = 200;
15117
15169
 
15118
15170
  // src/commands/auth/login.ts
15119
15171
  import open from "open";
15120
- // ../../node_modules/.bun/@convex-dev+better-auth@0.12.1+7592867d216cc73d/node_modules/@convex-dev/better-auth/dist/version.js
15172
+ // ../../node_modules/.bun/@convex-dev+better-auth@0.12.1+2116c7faeda40f52/node_modules/@convex-dev/better-auth/dist/version.js
15121
15173
  var VERSION = "0.12.1";
15122
15174
 
15123
- // ../../node_modules/.bun/@convex-dev+better-auth@0.12.1+7592867d216cc73d/node_modules/@convex-dev/better-auth/dist/plugins/convex/client.js
15175
+ // ../../node_modules/.bun/@convex-dev+better-auth@0.12.1+2116c7faeda40f52/node_modules/@convex-dev/better-auth/dist/plugins/convex/client.js
15124
15176
  var convexClient = () => {
15125
15177
  return {
15126
15178
  id: "convex",
@@ -15312,7 +15364,7 @@ var BetterAuthError = class extends Error {
15312
15364
  }
15313
15365
  };
15314
15366
 
15315
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/utils/url.mjs
15367
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/utils/url.mjs
15316
15368
  var SLASH_CHAR_CODE = 47;
15317
15369
  function trimTrailingSlashes(value) {
15318
15370
  let end = value.length;
@@ -15402,13 +15454,13 @@ function getOrigin(url2) {
15402
15454
  return null;
15403
15455
  }
15404
15456
  }
15405
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/package.mjs
15457
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/package.mjs
15406
15458
  var version3 = "1.6.23";
15407
15459
 
15408
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/version.mjs
15460
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/version.mjs
15409
15461
  var PACKAGE_VERSION = version3;
15410
15462
 
15411
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/broadcast-channel.mjs
15463
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/broadcast-channel.mjs
15412
15464
  var kBroadcastChannel = Symbol.for("better-auth:broadcast-channel");
15413
15465
  var now = () => Math.floor(Date.now() / 1000);
15414
15466
  var WindowBroadcastChannel = class {
@@ -15642,7 +15694,7 @@ var onMount = ($store, initialize) => {
15642
15694
  };
15643
15695
  });
15644
15696
  };
15645
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/equality.mjs
15697
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/equality.mjs
15646
15698
  function isPlainObject2(value) {
15647
15699
  if (typeof value !== "object" || value === null)
15648
15700
  return false;
@@ -15679,7 +15731,7 @@ function withEquality(store, isEqual) {
15679
15731
  });
15680
15732
  }
15681
15733
 
15682
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/focus-manager.mjs
15734
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/focus-manager.mjs
15683
15735
  var kFocusManager = Symbol.for("better-auth:focus-manager");
15684
15736
  var WindowFocusManager = class {
15685
15737
  listeners = /* @__PURE__ */ new Set;
@@ -15711,7 +15763,7 @@ function getGlobalFocusManager() {
15711
15763
  return globalThis[kFocusManager];
15712
15764
  }
15713
15765
 
15714
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/online-manager.mjs
15766
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/online-manager.mjs
15715
15767
  var kOnlineManager = Symbol.for("better-auth:online-manager");
15716
15768
  var WindowOnlineManager = class {
15717
15769
  listeners = /* @__PURE__ */ new Set;
@@ -15745,7 +15797,7 @@ function getGlobalOnlineManager() {
15745
15797
  return globalThis[kOnlineManager];
15746
15798
  }
15747
15799
 
15748
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/parser.mjs
15800
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/parser.mjs
15749
15801
  var PROTO_POLLUTION_PATTERNS = {
15750
15802
  proto: /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/,
15751
15803
  constructor: /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/,
@@ -15823,7 +15875,7 @@ function parseJSON(value, options = { strict: true }) {
15823
15875
  return betterJSONParse(value, options);
15824
15876
  }
15825
15877
 
15826
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/session-refresh.mjs
15878
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/session-refresh.mjs
15827
15879
  var now2 = () => Math.floor(Date.now() / 1000);
15828
15880
  var FOCUS_REFETCH_RATE_LIMIT_SECONDS = 5;
15829
15881
  function createSessionRefreshManager(opts) {
@@ -15971,7 +16023,7 @@ function isSafeUrlScheme(value) {
15971
16023
  return !DANGEROUS_URL_SCHEMES.includes(parsed.protocol);
15972
16024
  }
15973
16025
 
15974
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/fetch-plugins.mjs
16026
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/fetch-plugins.mjs
15975
16027
  var redirectPlugin = {
15976
16028
  id: "redirect",
15977
16029
  name: "Redirect",
@@ -15987,7 +16039,7 @@ var redirectPlugin = {
15987
16039
  } }
15988
16040
  };
15989
16041
 
15990
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/session-atom.mjs
16042
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/session-atom.mjs
15991
16043
  var isServer = () => typeof window === "undefined";
15992
16044
  function normalizeSessionResponse(res) {
15993
16045
  if (typeof res === "object" && res !== null && "data" in res && "error" in res)
@@ -16828,7 +16880,7 @@ var betterFetch = async (url2, options) => {
16828
16880
  };
16829
16881
  };
16830
16882
 
16831
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/config.mjs
16883
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/config.mjs
16832
16884
  var resolvePublicAuthUrl = (basePath) => {
16833
16885
  if (typeof process === "undefined")
16834
16886
  return;
@@ -16938,7 +16990,7 @@ var getClientConfig = (options, loadEnv) => {
16938
16990
  };
16939
16991
  };
16940
16992
 
16941
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/utils/is-atom.mjs
16993
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/utils/is-atom.mjs
16942
16994
  function isAtom(value) {
16943
16995
  return typeof value === "object" && value !== null && "get" in value && typeof value.get === "function" && "lc" in value && typeof value.lc === "number";
16944
16996
  }
@@ -16956,7 +17008,7 @@ function toKebabCase(input) {
16956
17008
  return splitWords(input).map((word) => word.toLowerCase()).join("-");
16957
17009
  }
16958
17010
 
16959
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/proxy.mjs
17011
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/proxy.mjs
16960
17012
  function getMethod2(path, knownPathMethods, args) {
16961
17013
  const method = knownPathMethods[path];
16962
17014
  const { fetchOptions, query: _query, ...body } = args || {};
@@ -17038,7 +17090,7 @@ function createDynamicPathProxy(routes, client3, knownPathMethods, atoms, atomLi
17038
17090
  return createProxy();
17039
17091
  }
17040
17092
 
17041
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/client/vanilla.mjs
17093
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/client/vanilla.mjs
17042
17094
  function createAuthClient(options) {
17043
17095
  const { pluginPathMethods, pluginsActions, pluginsAtoms, $fetch, atomListeners, $store } = getClientConfig(options);
17044
17096
  const resolvedHooks = {};
@@ -17051,7 +17103,7 @@ function createAuthClient(options) {
17051
17103
  $store
17052
17104
  }, $fetch, pluginPathMethods, pluginsAtoms, atomListeners);
17053
17105
  }
17054
- // ../../node_modules/.bun/better-auth@1.6.23+7367520ea0616224/node_modules/better-auth/dist/plugins/device-authorization/client.mjs
17106
+ // ../../node_modules/.bun/better-auth@1.6.23+2c138958c46c0bcd/node_modules/better-auth/dist/plugins/device-authorization/client.mjs
17055
17107
  var deviceAuthorizationClient = () => {
17056
17108
  return {
17057
17109
  id: "device-authorization",
@@ -17073,6 +17125,12 @@ var normalizeUrl = (url2, errorMessage) => {
17073
17125
  throw new Error(errorMessage ?? "URL must be a valid URL");
17074
17126
  return new URL(parsed.data).origin;
17075
17127
  };
17128
+ var localToday = () => {
17129
+ const now3 = new Date;
17130
+ const month = String(now3.getMonth() + 1).padStart(2, "0");
17131
+ const day = String(now3.getDate()).padStart(2, "0");
17132
+ return `${now3.getFullYear()}-${month}-${day}`;
17133
+ };
17076
17134
 
17077
17135
  // src/lib/zod.ts
17078
17136
  var configSchema = exports_external.object({ sessionToken: exports_external.string().optional(), convexUrl: exports_external.url(), convexSiteUrl: exports_external.url() });
@@ -17133,7 +17191,7 @@ var revokeCliSession = async (config2) => {
17133
17191
  throw new Error("The auth server did not confirm sign out");
17134
17192
  await saveConfig({ ...config2, sessionToken: undefined });
17135
17193
  };
17136
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/browser/logging.js
17194
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/browser/logging.js
17137
17195
  var __defProp4 = Object.defineProperty;
17138
17196
  var __defNormalProp3 = (obj, key, value) => (key in obj) ? __defProp4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17139
17197
  var __publicField2 = (obj, key, value) => __defNormalProp3(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -17239,7 +17297,7 @@ function logForFunction(logger2, type, source, udfPath, message) {
17239
17297
  }
17240
17298
  }
17241
17299
 
17242
- // ../../node_modules/.bun/convex@1.42.1+e14d3f224186685e/node_modules/convex/dist/esm/browser/http_client.js
17300
+ // ../../node_modules/.bun/convex@1.43.0+e14d3f224186685e/node_modules/convex/dist/esm/browser/http_client.js
17243
17301
  var __defProp5 = Object.defineProperty;
17244
17302
  var __defNormalProp4 = (obj, key, value) => (key in obj) ? __defProp5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17245
17303
  var __publicField3 = (obj, key, value) => __defNormalProp4(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -17756,23 +17814,6 @@ var auth = group({
17756
17814
  commands: [login, logout, status]
17757
17815
  });
17758
17816
 
17759
- // src/lib/group.ts
17760
- import { basename } from "path";
17761
- var git = async (...args) => {
17762
- const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
17763
- const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
17764
- return exitCode === 0 ? output.trim() || null : null;
17765
- };
17766
- var repoNameFromRemote = (remote) => remote.replace(/\/+$/, "").replace(/\.git$/, "").split(/[/:]/).pop() || undefined;
17767
- var detectRepoGroup = async () => {
17768
- const remote = await git("remote", "get-url", "origin");
17769
- if (remote)
17770
- return repoNameFromRemote(remote);
17771
- const root = await git("rev-parse", "--show-toplevel");
17772
- return root ? basename(root) : undefined;
17773
- };
17774
- var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
17775
-
17776
17817
  // src/lib/input.ts
17777
17818
  var assertSize = (size) => {
17778
17819
  if (size > MAX_PAGE_HTML_BYTES)
@@ -17788,6 +17829,27 @@ var readBody = async (path) => {
17788
17829
  assertSize(file2.size);
17789
17830
  return await file2.text();
17790
17831
  };
17832
+ var readTextOption = async (value) => value === "-" ? await Bun.stdin.text() : value;
17833
+ var readIssueRef = (value) => {
17834
+ const trimmed = value.trim();
17835
+ if (/^\d+$/.test(trimmed))
17836
+ return `${ISSUE_IDENTIFIER_PREFIX}-${trimmed}`;
17837
+ try {
17838
+ const url2 = new URL(trimmed);
17839
+ return url2.pathname.match(/\/issues\/([^/]+)\/?$/)?.[1] ?? trimmed;
17840
+ } catch {
17841
+ return trimmed;
17842
+ }
17843
+ };
17844
+ var readProjectRef = (value) => {
17845
+ const trimmed = value.trim();
17846
+ try {
17847
+ const url2 = new URL(trimmed);
17848
+ return url2.pathname.match(/\/projects\/([^/]+)\/?$/)?.[1] ?? trimmed;
17849
+ } catch {
17850
+ return trimmed;
17851
+ }
17852
+ };
17791
17853
  var readPageId = (value) => {
17792
17854
  const trimmed = value.trim();
17793
17855
  try {
@@ -17799,8 +17861,437 @@ var readPageId = (value) => {
17799
17861
  }
17800
17862
  };
17801
17863
 
17802
- // src/commands/pages/create.ts
17864
+ // ../../packages/backend/convex/lib/projectRepo.ts
17865
+ var DEFAULT_REPO_HOST = "github.com";
17866
+ var REPO_FORMAT_ERROR = "Enter a repository like github.com/owner/name.";
17867
+ var HOST_PATTERN = /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/;
17868
+ var SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
17869
+ var normalizeProjectRepo = (value) => {
17870
+ const trimmed = value?.trim();
17871
+ if (!trimmed)
17872
+ return;
17873
+ if (trimmed.length > MAX_PROJECT_REPO_LENGTH) {
17874
+ throw new ConvexError(`Repository must be ${MAX_PROJECT_REPO_LENGTH} characters or fewer.`);
17875
+ }
17876
+ const path = trimmed.toLowerCase().replace(/^(https?|ssh|git):\/\//, "").replace(/^[^/@:]+@/, "").replace(":", "/").replace(/\/+$/, "").replace(/\.git$/, "");
17877
+ const segments = path.split("/");
17878
+ const [host, owner, name] = segments.length === 2 ? [DEFAULT_REPO_HOST, ...segments] : segments.length === 3 ? segments : [];
17879
+ if (!host || !owner || !name)
17880
+ throw new ConvexError(REPO_FORMAT_ERROR);
17881
+ if (!HOST_PATTERN.test(host) || !SEGMENT_PATTERN.test(owner) || !SEGMENT_PATTERN.test(name)) {
17882
+ throw new ConvexError(REPO_FORMAT_ERROR);
17883
+ }
17884
+ const key = `${host}/${owner}/${name}`;
17885
+ if (key.length > MAX_PROJECT_REPO_LENGTH) {
17886
+ throw new ConvexError(`Repository must be ${MAX_PROJECT_REPO_LENGTH} characters or fewer.`);
17887
+ }
17888
+ return key;
17889
+ };
17890
+ var tryNormalizeProjectRepo = (value) => {
17891
+ try {
17892
+ return normalizeProjectRepo(value);
17893
+ } catch {
17894
+ return;
17895
+ }
17896
+ };
17897
+
17898
+ // src/lib/repo.ts
17899
+ var currentRepoKey = async () => {
17900
+ const proc = Bun.spawn(["git", "remote", "get-url", "origin"], { stdout: "pipe", stderr: "ignore" });
17901
+ const url2 = await new Response(proc.stdout).text();
17902
+ return await proc.exited === 0 ? tryNormalizeProjectRepo(url2) : undefined;
17903
+ };
17904
+
17905
+ // src/lib/resolve.ts
17906
+ var resolveIssue = async (client3, ref) => {
17907
+ const issue2 = await client3.query(api2.issues.get, { identifier: readIssueRef(ref) });
17908
+ if (!issue2)
17909
+ throw new Error(`No issue matches ${ref}.`);
17910
+ return issue2;
17911
+ };
17912
+ var resolveProject = async (client3, ref) => {
17913
+ const project = await client3.query(api2.projects.get, { id: readProjectRef(ref), today: localToday() });
17914
+ if (!project)
17915
+ throw new Error(`No project matches ${ref}.`);
17916
+ return project;
17917
+ };
17918
+ var repoProject = async (client3) => {
17919
+ const repo = await currentRepoKey();
17920
+ if (!repo)
17921
+ throw new Error("No repository here: not a git checkout with an origin remote.");
17922
+ const project = await client3.query(api2.projects.findByRepo, { repo });
17923
+ if (!project)
17924
+ throw new Error(`No project is connected to ${repo}. Connect one on the project in the dashboard.`);
17925
+ return project;
17926
+ };
17927
+ var resolveMilestone = async (client3, projectId, name) => {
17928
+ const milestones = await client3.query(api2.milestones.listByProject, { projectId, today: localToday() });
17929
+ const wanted = name.trim().toLowerCase();
17930
+ const milestone = milestones.find((candidate) => candidate.name.toLowerCase() === wanted);
17931
+ if (!milestone) {
17932
+ const names = milestones.map((candidate) => candidate.name).join(", ");
17933
+ throw new Error(names ? `No milestone named "${name}". The project has: ${names}.` : "The project has no milestones.");
17934
+ }
17935
+ return milestone;
17936
+ };
17937
+
17938
+ // src/commands/issues/comment.ts
17939
+ var comment = command({
17940
+ name: "comment",
17941
+ description: "Comment on an issue",
17942
+ positionals: {
17943
+ id: exports_external.string().describe("Issue identifier, number, or URL"),
17944
+ body: exports_external.string().describe("Comment markdown, or - for stdin")
17945
+ },
17946
+ run: async ({ positionals: { id, body } }) => {
17947
+ const client3 = await backendClient();
17948
+ const issue2 = await resolveIssue(client3, id);
17949
+ await client3.mutation(api2.issueComments.create, { issueId: issue2._id, bodyMarkdown: await readTextOption(body) });
17950
+ console.log(`Commented on ${issue2.identifier}.`);
17951
+ }
17952
+ });
17953
+
17954
+ // src/commands/issues/create.ts
17803
17955
  var create = command({
17956
+ name: "create",
17957
+ description: "Create an issue and print its identifier",
17958
+ positionals: {
17959
+ title: exports_external.string().describe("Issue title")
17960
+ },
17961
+ options: {
17962
+ description: exports_external.string().optional().describe("Markdown description, or - for stdin").meta({ short: "d" }),
17963
+ status: exports_external.enum(ISSUE_STATUSES).optional().describe("Starting status (default backlog)").meta({ short: "s" }),
17964
+ priority: exports_external.enum(ISSUE_PRIORITIES).optional().describe("Priority (default none)").meta({ short: "p" }),
17965
+ estimate: exports_external.coerce.number().pipe(exports_external.literal([...ISSUE_ESTIMATES])).optional().describe(`Points (${ISSUE_ESTIMATES.join("|")})`),
17966
+ due: exports_external.iso.date().optional().describe("Due date (YYYY-MM-DD)"),
17967
+ project: exports_external.string().optional().describe("Project to create the issue in (id or URL)"),
17968
+ here: exports_external.boolean().default(false).describe("Create in the checkout's connected project"),
17969
+ milestone: exports_external.string().optional().describe("Milestone name (needs --project or --here)"),
17970
+ disposition: exports_external.enum(ISSUE_CREATE_DISPOSITIONS).optional().describe("Route immediately (default needs_triage)")
17971
+ },
17972
+ run: async ({ positionals: { title }, options }) => {
17973
+ if (options.project && options.here)
17974
+ throw new Error("Pass --project or --here, not both.");
17975
+ const client3 = await backendClient();
17976
+ const project = options.project ? await resolveProject(client3, options.project) : options.here ? await repoProject(client3) : undefined;
17977
+ if (options.milestone && !project)
17978
+ throw new Error("A milestone needs --project or --here.");
17979
+ const milestone = project && options.milestone ? await resolveMilestone(client3, project._id, options.milestone) : undefined;
17980
+ const { identifier } = await client3.mutation(api2.issues.create, {
17981
+ title,
17982
+ descriptionMarkdown: options.description === undefined ? undefined : await readTextOption(options.description),
17983
+ status: options.status,
17984
+ priority: options.priority,
17985
+ estimate: options.estimate,
17986
+ dueDate: options.due,
17987
+ projectId: project?._id,
17988
+ milestoneId: milestone?._id,
17989
+ disposition: options.disposition
17990
+ });
17991
+ console.log(identifier);
17992
+ }
17993
+ });
17994
+
17995
+ // src/commands/issues/get.ts
17996
+ var activityLine = (detail) => {
17997
+ const label = detail.kind.replaceAll("_", " ");
17998
+ switch (detail.kind) {
17999
+ case "created":
18000
+ case "description_changed":
18001
+ return label;
18002
+ case "title_changed":
18003
+ case "status_changed":
18004
+ case "priority_changed":
18005
+ case "disposition_changed":
18006
+ case "estimate_changed":
18007
+ case "due_date_changed":
18008
+ case "project_changed":
18009
+ case "milestone_changed":
18010
+ case "parent_changed":
18011
+ return `${label}: ${detail.from ?? "none"} \u2192 ${detail.to ?? "none"}`;
18012
+ case "label_added":
18013
+ case "label_removed":
18014
+ case "attachment_added":
18015
+ case "attachment_removed":
18016
+ return `${label}: ${detail.name}`;
18017
+ case "child_added":
18018
+ case "child_removed":
18019
+ return `${label}: ${detail.identifier}`;
18020
+ case "relation_added":
18021
+ case "relation_removed":
18022
+ return `${label}: ${detail.relation.replaceAll("_", " ")} ${detail.identifier}`;
18023
+ }
18024
+ };
18025
+ var get = command({
18026
+ name: "get",
18027
+ description: "Show an issue: properties, description, and its feed",
18028
+ positionals: {
18029
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18030
+ },
18031
+ options: {
18032
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18033
+ },
18034
+ run: async ({ positionals: { id }, options: { json: json2 } }) => {
18035
+ const client3 = await backendClient();
18036
+ const issue2 = await resolveIssue(client3, id);
18037
+ const feed = await client3.query(api2.issues.feed, { id: issue2._id });
18038
+ if (json2)
18039
+ return console.log(JSON.stringify({ ...issue2, feed }, null, 2));
18040
+ const project = issue2.projectId ? await client3.query(api2.projects.get, { id: issue2.projectId, today: localToday() }) : null;
18041
+ const milestone = issue2.milestoneId ? await client3.query(api2.milestones.get, { id: issue2.milestoneId, today: localToday() }) : null;
18042
+ console.log(`${issue2.identifier} ${issue2.title}`);
18043
+ console.log(`Status: ${issue2.status} \xB7 Priority: ${issue2.priority} \xB7 Disposition: ${issue2.disposition}`);
18044
+ if (issue2.estimate !== undefined)
18045
+ console.log(`Estimate: ${issue2.estimate}`);
18046
+ if (issue2.dueDate)
18047
+ console.log(`Due: ${issue2.dueDate}`);
18048
+ if (project)
18049
+ console.log(`Project: ${project.name}${milestone ? ` \xB7 Milestone: ${milestone.name}` : ""}`);
18050
+ if (issue2.parent)
18051
+ console.log(`Parent: ${issue2.parent.identifier} ${issue2.parent.title}`);
18052
+ if (issue2.labels.length > 0)
18053
+ console.log(`Labels: ${issue2.labels.map((label) => label.name).join(", ")}`);
18054
+ if (issue2.children.total > 0)
18055
+ console.log(`Sub-issues: ${issue2.children.done}/${issue2.children.total} done`);
18056
+ if (issue2.descriptionMarkdown)
18057
+ console.log(`
18058
+ ${issue2.descriptionMarkdown}`);
18059
+ if (feed.events.length > 0)
18060
+ console.log(`
18061
+ Feed:`);
18062
+ if (feed.truncated)
18063
+ console.log("(older events truncated)");
18064
+ for (const event of feed.events) {
18065
+ const at = new Date(event.at).toISOString().slice(0, 16).replace("T", " ");
18066
+ if (event.type === "activity") {
18067
+ console.log(`${at} ${activityLine(event.detail)}`);
18068
+ } else {
18069
+ console.log(`${at} comment${event.editedAt ? " (edited)" : ""}:`);
18070
+ for (const line of event.bodyMarkdown.split(`
18071
+ `))
18072
+ console.log(` ${line}`);
18073
+ }
18074
+ }
18075
+ }
18076
+ });
18077
+
18078
+ // src/lib/output.ts
18079
+ var printTable = (rows) => {
18080
+ if (rows.length === 0)
18081
+ return;
18082
+ const columnCount = Math.max(...rows.map((row) => row.length));
18083
+ const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
18084
+ for (const row of rows) {
18085
+ console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
18086
+ }
18087
+ };
18088
+
18089
+ // src/commands/issues/list.ts
18090
+ var list = command({
18091
+ name: "list",
18092
+ description: "List open issues, most recently updated first",
18093
+ options: {
18094
+ all: exports_external.boolean().default(false).describe("Include done and canceled issues"),
18095
+ status: exports_external.enum(ISSUE_STATUSES).optional().describe("Only this status").meta({ short: "s" }),
18096
+ priority: exports_external.enum(ISSUE_PRIORITIES).optional().describe("Only this priority").meta({ short: "p" }),
18097
+ disposition: exports_external.enum(ISSUE_DISPOSITIONS).optional().describe("Only this disposition").meta({ short: "d" }),
18098
+ project: exports_external.string().optional().describe("Only this project (id or URL)"),
18099
+ here: exports_external.boolean().default(false).describe("Only the checkout's connected project"),
18100
+ milestone: exports_external.string().optional().describe("Only this milestone (name; needs --project or --here)"),
18101
+ due: exports_external.enum(ISSUE_DUE_DATE_MODES).optional().describe("Only this due-date state"),
18102
+ search: exports_external.string().optional().describe("Free-text search (results come in relevance order)"),
18103
+ limit: exports_external.coerce.number().int().positive().default(ISSUE_LIST_PAGE_SIZE).describe("Most issues to print"),
18104
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18105
+ },
18106
+ run: async ({ options }) => {
18107
+ if (options.project && options.here)
18108
+ throw new Error("Pass --project or --here, not both.");
18109
+ const client3 = await backendClient();
18110
+ const project = options.project ? await resolveProject(client3, options.project) : options.here ? await repoProject(client3) : undefined;
18111
+ if (options.milestone && !project)
18112
+ throw new Error("A milestone filter needs --project or --here.");
18113
+ const milestone = project && options.milestone ? await resolveMilestone(client3, project._id, options.milestone) : undefined;
18114
+ const result = await client3.query(api2.issueViews.query, {
18115
+ source: {
18116
+ type: "custom",
18117
+ query: {
18118
+ layout: "list",
18119
+ groupBy: "none",
18120
+ orderBy: "updated_at",
18121
+ orderDirection: "desc",
18122
+ filters: {
18123
+ query: options.search,
18124
+ statuses: options.status ? [options.status] : options.all ? undefined : ISSUE_STATUSES.filter(issueIsOpen),
18125
+ priorities: options.priority ? [options.priority] : undefined,
18126
+ dispositions: options.disposition ? [options.disposition] : undefined,
18127
+ projectIds: project ? [project._id] : undefined,
18128
+ milestoneIds: milestone ? [milestone._id] : undefined,
18129
+ dueDate: options.due
18130
+ }
18131
+ }
18132
+ },
18133
+ paginationOpts: { numItems: options.limit, cursor: null },
18134
+ today: localToday()
18135
+ });
18136
+ if (options.json)
18137
+ return console.log(JSON.stringify(result.page, null, 2));
18138
+ if (result.page.length === 0)
18139
+ return console.log("No issues match.");
18140
+ printTable([
18141
+ ["ID", "TITLE", "STATUS", "PRIORITY", "DISPOSITION", "DUE", "UPDATED"],
18142
+ ...result.page.map((issue2) => [
18143
+ issue2.identifier,
18144
+ issue2.title,
18145
+ issue2.status,
18146
+ issue2.priority,
18147
+ issue2.disposition,
18148
+ issue2.dueDate ?? "-",
18149
+ new Date(issue2.updatedAt).toISOString().slice(0, 10)
18150
+ ])
18151
+ ]);
18152
+ if (!result.isDone)
18153
+ console.log(`
18154
+ More issues match; raise --limit past ${options.limit}.`);
18155
+ }
18156
+ });
18157
+
18158
+ // src/commands/issues/remove.ts
18159
+ var remove = command({
18160
+ name: "delete",
18161
+ description: "Delete an issue permanently (sub-issues survive as top-level issues)",
18162
+ positionals: {
18163
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18164
+ },
18165
+ run: async ({ positionals: { id } }) => {
18166
+ const client3 = await backendClient();
18167
+ const issue2 = await resolveIssue(client3, id);
18168
+ await client3.mutation(api2.issues.remove, { id: issue2._id });
18169
+ console.log(`Deleted ${issue2.identifier}.`);
18170
+ }
18171
+ });
18172
+
18173
+ // src/commands/issues/route.ts
18174
+ var route = command({
18175
+ name: "route",
18176
+ description: "Route an issue to a disposition (wontfix also cancels it)",
18177
+ positionals: {
18178
+ id: exports_external.string().describe("Issue identifier, number, or URL"),
18179
+ disposition: exports_external.enum(ISSUE_DISPOSITIONS).describe("Where the issue goes next")
18180
+ },
18181
+ options: {
18182
+ comment: exports_external.string().optional().describe("Why, as a comment (required for needs_info and wontfix), or - for stdin").meta({ short: "c" })
18183
+ },
18184
+ run: async ({ positionals: { id, disposition }, options: { comment: comment2 } }) => {
18185
+ const client3 = await backendClient();
18186
+ const issue2 = await resolveIssue(client3, id);
18187
+ await client3.mutation(api2.issues.route, {
18188
+ id: issue2._id,
18189
+ disposition,
18190
+ comment: comment2 === undefined ? undefined : await readTextOption(comment2)
18191
+ });
18192
+ console.log(`Routed ${issue2.identifier} to ${disposition}.`);
18193
+ }
18194
+ });
18195
+
18196
+ // src/commands/issues/set.ts
18197
+ var set2 = command({
18198
+ name: "set",
18199
+ description: "Set an issue's status, priority, estimate, due date, project, milestone, or parent",
18200
+ positionals: {
18201
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18202
+ },
18203
+ options: {
18204
+ status: exports_external.enum(ISSUE_STATUSES).optional().describe("New status").meta({ short: "s" }),
18205
+ priority: exports_external.enum(ISSUE_PRIORITIES).optional().describe("New priority").meta({ short: "p" }),
18206
+ estimate: exports_external.coerce.number().pipe(exports_external.literal([...ISSUE_ESTIMATES])).nullable().optional().describe(`Points (${ISSUE_ESTIMATES.join("|")}), or --no-estimate to clear`).meta({ negatable: true }),
18207
+ due: exports_external.iso.date().nullable().optional().describe("Due date (YYYY-MM-DD), or --no-due to clear").meta({ negatable: true }),
18208
+ project: exports_external.string().nullable().optional().describe("Move to this project (id or URL), or --no-project to make the issue projectless").meta({ negatable: true }),
18209
+ milestone: exports_external.string().nullable().optional().describe("Move to this milestone of the issue's project (name), or --no-milestone to clear").meta({ negatable: true }),
18210
+ parent: exports_external.string().nullable().optional().describe("Make this a sub-issue of another issue, or --no-parent to promote it").meta({ negatable: true })
18211
+ },
18212
+ run: async ({ positionals: { id }, options }) => {
18213
+ const { status: status2, priority, estimate, due, project, milestone, parent } = options;
18214
+ if (Object.values(options).every((value) => value === undefined))
18215
+ throw new Error("Nothing to set. Pass --status, --priority, --estimate, --due, --project, --milestone, or --parent.");
18216
+ const client3 = await backendClient();
18217
+ const issue2 = await resolveIssue(client3, id);
18218
+ const projectId = project === undefined ? issue2.projectId : project === null ? undefined : (await resolveProject(client3, project))._id;
18219
+ let milestoneId;
18220
+ if (milestone === undefined) {
18221
+ milestoneId = projectId === issue2.projectId ? issue2.milestoneId : undefined;
18222
+ } else if (milestone !== null) {
18223
+ if (projectId === undefined)
18224
+ throw new Error("A milestone needs a project. Pass --project too.");
18225
+ milestoneId = (await resolveMilestone(client3, projectId, milestone))._id;
18226
+ }
18227
+ const parentId = parent == null ? undefined : (await resolveIssue(client3, parent))._id;
18228
+ if (parent !== undefined)
18229
+ await client3.mutation(api2.issues.setParent, { id: issue2._id, parentId });
18230
+ if (status2 !== undefined)
18231
+ await client3.mutation(api2.issues.setStatus, { id: issue2._id, status: status2 });
18232
+ if (priority !== undefined)
18233
+ await client3.mutation(api2.issues.setPriority, { id: issue2._id, priority });
18234
+ if (estimate !== undefined)
18235
+ await client3.mutation(api2.issues.setEstimate, { id: issue2._id, estimate: estimate ?? undefined });
18236
+ if (due !== undefined)
18237
+ await client3.mutation(api2.issues.setDueDate, { id: issue2._id, dueDate: due ?? undefined });
18238
+ if (project !== undefined || milestone !== undefined)
18239
+ await client3.mutation(api2.issues.setMembership, { id: issue2._id, projectId, milestoneId });
18240
+ console.log(`Updated ${issue2.identifier}.`);
18241
+ }
18242
+ });
18243
+
18244
+ // src/commands/issues/update.ts
18245
+ var update = command({
18246
+ name: "update",
18247
+ description: "Rewrite an issue's title or description",
18248
+ positionals: {
18249
+ id: exports_external.string().describe("Issue identifier, number, or URL")
18250
+ },
18251
+ options: {
18252
+ title: exports_external.string().optional().describe("New title").meta({ short: "t" }),
18253
+ description: exports_external.string().optional().describe("New markdown description, or - for stdin").meta({ short: "d" })
18254
+ },
18255
+ run: async ({ positionals: { id }, options: { title, description } }) => {
18256
+ if (title === undefined && description === undefined)
18257
+ throw new Error("Nothing to update. Pass --title or --description.");
18258
+ const client3 = await backendClient();
18259
+ const issue2 = await resolveIssue(client3, id);
18260
+ await client3.mutation(api2.issues.update, {
18261
+ id: issue2._id,
18262
+ title,
18263
+ descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
18264
+ });
18265
+ console.log(`Updated ${issue2.identifier}.`);
18266
+ }
18267
+ });
18268
+
18269
+ // src/commands/issues/index.ts
18270
+ var issues = group({
18271
+ name: "issues",
18272
+ description: "Track and triage issues",
18273
+ commands: [create, list, get, update, set2, route, comment, remove]
18274
+ });
18275
+
18276
+ // src/lib/group.ts
18277
+ import { basename } from "path";
18278
+ var git = async (...args) => {
18279
+ const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
18280
+ const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
18281
+ return exitCode === 0 ? output.trim() || null : null;
18282
+ };
18283
+ var repoNameFromRemote = (remote) => remote.replace(/\/+$/, "").replace(/\.git$/, "").split(/[/:]/).pop() || undefined;
18284
+ var detectRepoGroup = async () => {
18285
+ const remote = await git("remote", "get-url", "origin");
18286
+ if (remote)
18287
+ return repoNameFromRemote(remote);
18288
+ const root = await git("rev-parse", "--show-toplevel");
18289
+ return root ? basename(root) : undefined;
18290
+ };
18291
+ var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
18292
+
18293
+ // src/commands/pages/create.ts
18294
+ var create2 = command({
17804
18295
  name: "create",
17805
18296
  description: "Publish a page and print its URL",
17806
18297
  positionals: {
@@ -17829,7 +18320,7 @@ var create = command({
17829
18320
  });
17830
18321
 
17831
18322
  // src/commands/pages/get.ts
17832
- var get = command({
18323
+ var get2 = command({
17833
18324
  name: "get",
17834
18325
  description: "Print a page's HTML",
17835
18326
  positionals: {
@@ -17851,19 +18342,8 @@ var get = command({
17851
18342
  // ../../packages/backend/convex/lib/format.ts
17852
18343
  var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
17853
18344
 
17854
- // src/lib/output.ts
17855
- var printTable = (rows) => {
17856
- if (rows.length === 0)
17857
- return;
17858
- const columnCount = Math.max(...rows.map((row) => row.length));
17859
- const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
17860
- for (const row of rows) {
17861
- console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
17862
- }
17863
- };
17864
-
17865
18345
  // src/commands/pages/list.ts
17866
- var list = command({
18346
+ var list2 = command({
17867
18347
  name: "list",
17868
18348
  description: "List your published pages",
17869
18349
  options: {
@@ -17893,7 +18373,7 @@ var list = command({
17893
18373
  });
17894
18374
 
17895
18375
  // src/commands/pages/remove.ts
17896
- var remove = command({
18376
+ var remove2 = command({
17897
18377
  name: "delete",
17898
18378
  description: "Delete a page",
17899
18379
  positionals: {
@@ -17920,7 +18400,7 @@ var revert = command({
17920
18400
  });
17921
18401
 
17922
18402
  // src/commands/pages/update.ts
17923
- var update = command({
18403
+ var update2 = command({
17924
18404
  name: "update",
17925
18405
  description: "Replace a page's HTML, title, group, mode, or visibility",
17926
18406
  positionals: {
@@ -17981,7 +18461,222 @@ var versions2 = command({
17981
18461
  var pages = group({
17982
18462
  name: "pages",
17983
18463
  description: "Publish HTML documents to the web",
17984
- commands: [create, list, get, update, versions2, revert, remove]
18464
+ commands: [create2, list2, get2, update2, versions2, revert, remove2]
18465
+ });
18466
+
18467
+ // src/commands/project.ts
18468
+ var project = command({
18469
+ name: "project",
18470
+ description: "Show the project connected to this checkout's repository",
18471
+ options: {
18472
+ repo: exports_external.string().optional().describe("Resolve this repository instead of the checkout's origin remote"),
18473
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18474
+ },
18475
+ run: async ({ options: { repo: repoOption, json: json2 } }) => {
18476
+ const repo = repoOption ?? await currentRepoKey();
18477
+ if (!repo) {
18478
+ throw new Error("No repository here: not a git checkout with an origin remote. Pass --repo <owner/name>.");
18479
+ }
18480
+ const found = await (await backendClient()).query(api2.projects.findByRepo, { repo });
18481
+ if (!found)
18482
+ throw new Error(`No project is connected to ${repo}. Connect one on the project in the dashboard.`);
18483
+ if (json2)
18484
+ return console.log(JSON.stringify(found, null, 2));
18485
+ console.log(found.name);
18486
+ if (found.summary)
18487
+ console.log(found.summary);
18488
+ console.log(`Repo: ${found.repo}`);
18489
+ console.log(`Status: ${found.status}${found.health ? ` \xB7 Health: ${found.health}` : ""}`);
18490
+ console.log(`Progress: ${found.progress.done}/${found.progress.total} issues done`);
18491
+ if (found.targetDate)
18492
+ console.log(`Target date: ${found.targetDate}`);
18493
+ }
18494
+ });
18495
+
18496
+ // src/commands/projects/create.ts
18497
+ var create3 = command({
18498
+ name: "create",
18499
+ description: "Create a project and print its id",
18500
+ positionals: {
18501
+ name: exports_external.string().describe("Project name")
18502
+ },
18503
+ options: {
18504
+ summary: exports_external.string().optional().describe("One-line summary").meta({ short: "s" }),
18505
+ description: exports_external.string().optional().describe("Markdown description, or - for stdin").meta({ short: "d" }),
18506
+ status: exports_external.enum(PROJECT_STATUSES).optional().describe("Starting status (default planned)"),
18507
+ health: exports_external.enum(PROJECT_HEALTHS).optional().describe("Health"),
18508
+ target: exports_external.iso.date().optional().describe("Target date (YYYY-MM-DD)"),
18509
+ repo: exports_external.string().optional().describe("Repository to connect (owner/name or any remote URL)")
18510
+ },
18511
+ run: async ({ positionals: { name }, options }) => {
18512
+ const projectId = await (await backendClient()).mutation(api2.projects.create, {
18513
+ name,
18514
+ summary: options.summary,
18515
+ descriptionMarkdown: options.description === undefined ? undefined : await readTextOption(options.description),
18516
+ status: options.status,
18517
+ health: options.health,
18518
+ targetDate: options.target,
18519
+ repo: options.repo
18520
+ });
18521
+ console.log(projectId);
18522
+ }
18523
+ });
18524
+
18525
+ // src/commands/projects/get.ts
18526
+ var get3 = command({
18527
+ name: "get",
18528
+ description: "Show a project: properties, description, and milestones",
18529
+ positionals: {
18530
+ id: exports_external.string().describe("Project id or URL")
18531
+ },
18532
+ options: {
18533
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18534
+ },
18535
+ run: async ({ positionals: { id }, options: { json: json2 } }) => {
18536
+ const client3 = await backendClient();
18537
+ const project2 = await resolveProject(client3, id);
18538
+ const milestones = await client3.query(api2.milestones.listByProject, {
18539
+ projectId: project2._id,
18540
+ today: localToday()
18541
+ });
18542
+ if (json2)
18543
+ return console.log(JSON.stringify({ ...project2, milestones }, null, 2));
18544
+ console.log(project2.name);
18545
+ if (project2.summary)
18546
+ console.log(project2.summary);
18547
+ if (project2.repo)
18548
+ console.log(`Repo: ${project2.repo}`);
18549
+ console.log(`Status: ${project2.status}${project2.health ? ` \xB7 Health: ${project2.health}` : ""}`);
18550
+ console.log(`Progress: ${project2.progress.done}/${project2.progress.total} issues done`);
18551
+ if (project2.targetDate)
18552
+ console.log(`Target date: ${project2.targetDate}${project2.isOverdue ? " (overdue)" : ""}`);
18553
+ if (project2.descriptionMarkdown)
18554
+ console.log(`
18555
+ ${project2.descriptionMarkdown}`);
18556
+ if (milestones.length > 0) {
18557
+ console.log(`
18558
+ Milestones:`);
18559
+ for (const milestone of milestones) {
18560
+ const target = milestone.targetDate ? ` \xB7 target ${milestone.targetDate}${milestone.isOverdue ? " (overdue)" : ""}` : "";
18561
+ console.log(` ${milestone.name} \u2014 ${milestone.progress.done}/${milestone.progress.total} done${target}`);
18562
+ }
18563
+ }
18564
+ }
18565
+ });
18566
+
18567
+ // src/commands/projects/list.ts
18568
+ var list3 = command({
18569
+ name: "list",
18570
+ description: "List your projects, most recently updated first",
18571
+ options: {
18572
+ limit: exports_external.coerce.number().int().positive().default(50).describe("Most projects to print"),
18573
+ json: exports_external.boolean().default(false).describe("Print as JSON")
18574
+ },
18575
+ run: async ({ options: { limit, json: json2 } }) => {
18576
+ const result = await (await backendClient()).query(api2.projects.listPaginated, {
18577
+ paginationOpts: { numItems: limit, cursor: null },
18578
+ today: localToday()
18579
+ });
18580
+ if (json2)
18581
+ return console.log(JSON.stringify(result.page, null, 2));
18582
+ if (result.page.length === 0)
18583
+ return console.log("No projects yet.");
18584
+ printTable([
18585
+ ["NAME", "STATUS", "HEALTH", "PROGRESS", "TARGET", "REPO", "ID"],
18586
+ ...result.page.map((project2) => [
18587
+ project2.name,
18588
+ project2.status,
18589
+ project2.health ?? "-",
18590
+ `${project2.progress.done}/${project2.progress.total}`,
18591
+ project2.targetDate ? `${project2.targetDate}${project2.isOverdue ? " (overdue)" : ""}` : "-",
18592
+ project2.repo ?? "-",
18593
+ project2._id
18594
+ ])
18595
+ ]);
18596
+ if (!result.isDone)
18597
+ console.log(`
18598
+ More projects exist; raise --limit past ${limit}.`);
18599
+ }
18600
+ });
18601
+
18602
+ // src/commands/projects/remove.ts
18603
+ var remove3 = command({
18604
+ name: "delete",
18605
+ description: "Delete a project and its milestones (its issues survive, projectless)",
18606
+ positionals: {
18607
+ id: exports_external.string().describe("Project id or URL")
18608
+ },
18609
+ run: async ({ positionals: { id } }) => {
18610
+ const client3 = await backendClient();
18611
+ const project2 = await resolveProject(client3, id);
18612
+ await client3.mutation(api2.projects.remove, { id: project2._id });
18613
+ console.log(`Deleted ${project2.name}.`);
18614
+ }
18615
+ });
18616
+
18617
+ // src/commands/projects/set.ts
18618
+ var set3 = command({
18619
+ name: "set",
18620
+ description: "Set a project's status, health, target date, or repository",
18621
+ positionals: {
18622
+ id: exports_external.string().describe("Project id or URL")
18623
+ },
18624
+ options: {
18625
+ status: exports_external.enum(PROJECT_STATUSES).optional().describe("New status").meta({ short: "s" }),
18626
+ health: exports_external.enum(PROJECT_HEALTHS).nullable().optional().describe("New health, or --no-health to clear").meta({ negatable: true }),
18627
+ target: exports_external.iso.date().nullable().optional().describe("Target date (YYYY-MM-DD), or --no-target to clear").meta({ negatable: true }),
18628
+ repo: exports_external.string().nullable().optional().describe("Repository to connect (owner/name or any remote URL), or --no-repo to disconnect").meta({ negatable: true })
18629
+ },
18630
+ run: async ({ positionals: { id }, options }) => {
18631
+ const { status: status2, health, target, repo } = options;
18632
+ if (Object.values(options).every((value) => value === undefined))
18633
+ throw new Error("Nothing to set. Pass --status, --health, --target, or --repo.");
18634
+ const client3 = await backendClient();
18635
+ const project2 = await resolveProject(client3, id);
18636
+ if (repo !== undefined)
18637
+ await client3.mutation(api2.projects.setRepo, { id: project2._id, repo: repo ?? undefined });
18638
+ if (status2 !== undefined)
18639
+ await client3.mutation(api2.projects.setStatus, { id: project2._id, status: status2 });
18640
+ if (health !== undefined)
18641
+ await client3.mutation(api2.projects.setHealth, { id: project2._id, health: health ?? undefined });
18642
+ if (target !== undefined)
18643
+ await client3.mutation(api2.projects.setTargetDate, { id: project2._id, targetDate: target ?? undefined });
18644
+ console.log(`Updated ${project2.name}.`);
18645
+ }
18646
+ });
18647
+
18648
+ // src/commands/projects/update.ts
18649
+ var update3 = command({
18650
+ name: "update",
18651
+ description: "Rewrite a project's name, summary, or description",
18652
+ positionals: {
18653
+ id: exports_external.string().describe("Project id or URL")
18654
+ },
18655
+ options: {
18656
+ name: exports_external.string().optional().describe("New name").meta({ short: "n" }),
18657
+ summary: exports_external.string().optional().describe("New one-line summary").meta({ short: "s" }),
18658
+ description: exports_external.string().optional().describe("New markdown description, or - for stdin").meta({ short: "d" })
18659
+ },
18660
+ run: async ({ positionals: { id }, options: { name, summary, description } }) => {
18661
+ if (name === undefined && summary === undefined && description === undefined)
18662
+ throw new Error("Nothing to update. Pass --name, --summary, or --description.");
18663
+ const client3 = await backendClient();
18664
+ const project2 = await resolveProject(client3, id);
18665
+ await client3.mutation(api2.projects.update, {
18666
+ id: project2._id,
18667
+ name,
18668
+ summary,
18669
+ descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
18670
+ });
18671
+ console.log(`Updated ${name ?? project2.name}.`);
18672
+ }
18673
+ });
18674
+
18675
+ // src/commands/projects/index.ts
18676
+ var projects = group({
18677
+ name: "projects",
18678
+ description: "Manage projects",
18679
+ commands: [create3, list3, get3, update3, set3, remove3]
17985
18680
  });
17986
18681
 
17987
18682
  // src/commands/upgrade.ts
@@ -17999,7 +18694,7 @@ var upgrade = command({
17999
18694
  var rootCommand = group({
18000
18695
  name: "kds",
18001
18696
  description: "KDS CLI",
18002
- commands: [auth, pages, upgrade],
18697
+ commands: [auth, issues, pages, project, projects, upgrade],
18003
18698
  options: {
18004
18699
  version: exports_external.boolean().default(false).describe("Show the version").meta({ short: "v" })
18005
18700
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {