@xbghc/warden 0.13.1 → 0.15.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.
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // bin/cli.ts
4
- import path10 from "path";
4
+ import path11 from "path";
5
5
  import { spawn } from "child_process";
6
6
  import { existsSync as existsSync2 } from "fs";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  // packages/server/src/index.ts
10
10
  import net from "net";
11
- import { randomUUID as randomUUID2 } from "crypto";
11
+ import { randomUUID as randomUUID3 } from "crypto";
12
12
 
13
13
  // node_modules/.pnpm/@hono+node-server@1.19.17_hono@4.13.7/node_modules/@hono/node-server/dist/index.mjs
14
14
  import { createServer as createServerHTTP } from "http";
@@ -652,8 +652,8 @@ var serve = (options, listeningListener) => {
652
652
  };
653
653
 
654
654
  // packages/server/src/app.ts
655
- import path8 from "path";
656
- import { randomUUID } from "crypto";
655
+ import path9 from "path";
656
+ import { randomUUID as randomUUID2 } from "crypto";
657
657
 
658
658
  // node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/compose.js
659
659
  var compose = (middleware, onError, onNotFound) => {
@@ -812,26 +812,26 @@ var throwNestingLimitExceeded = () => {
812
812
  };
813
813
 
814
814
  // node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/url.js
815
- var splitPath = (path11) => {
816
- const paths = path11.split("/");
815
+ var splitPath = (path12) => {
816
+ const paths = path12.split("/");
817
817
  if (paths[0] === "") {
818
818
  paths.shift();
819
819
  }
820
820
  return paths;
821
821
  };
822
822
  var splitRoutingPath = (routePath) => {
823
- const { groups, path: path11 } = extractGroupsFromPath(routePath);
824
- const paths = splitPath(path11);
823
+ const { groups, path: path12 } = extractGroupsFromPath(routePath);
824
+ const paths = splitPath(path12);
825
825
  return replaceGroupMarks(paths, groups);
826
826
  };
827
- var extractGroupsFromPath = (path11) => {
827
+ var extractGroupsFromPath = (path12) => {
828
828
  const groups = [];
829
- path11 = path11.replace(/\{[^}]+\}/g, (match2, index) => {
829
+ path12 = path12.replace(/\{[^}]+\}/g, (match2, index) => {
830
830
  const mark = `@${index}`;
831
831
  groups.push([mark, match2]);
832
832
  return mark;
833
833
  });
834
- return { groups, path: path11 };
834
+ return { groups, path: path12 };
835
835
  };
836
836
  var replaceGroupMarks = (paths, groups) => {
837
837
  for (let i = groups.length - 1; i >= 0; i--) {
@@ -888,8 +888,8 @@ var getPath = (request) => {
888
888
  const queryIndex = url.indexOf("?", i);
889
889
  const hashIndex = url.indexOf("#", i);
890
890
  const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
891
- const path11 = url.slice(start, end);
892
- return tryDecodeURI(path11.includes("%25") ? path11.replace(/%25/g, "%2525") : path11);
891
+ const path12 = url.slice(start, end);
892
+ return tryDecodeURI(path12.includes("%25") ? path12.replace(/%25/g, "%2525") : path12);
893
893
  } else if (charCode === 63 || charCode === 35) {
894
894
  break;
895
895
  }
@@ -906,11 +906,11 @@ var mergePath = (base, sub, ...rest) => {
906
906
  }
907
907
  return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
908
908
  };
909
- var checkOptionalParameter = (path11) => {
910
- if (path11.charCodeAt(path11.length - 1) !== 63 || !path11.includes(":")) {
909
+ var checkOptionalParameter = (path12) => {
910
+ if (path12.charCodeAt(path12.length - 1) !== 63 || !path12.includes(":")) {
911
911
  return null;
912
912
  }
913
- const segments = path11.split("/");
913
+ const segments = path12.split("/");
914
914
  const results = [];
915
915
  let basePath = "";
916
916
  segments.forEach((segment) => {
@@ -1052,9 +1052,9 @@ var HonoRequest = class {
1052
1052
  */
1053
1053
  path;
1054
1054
  bodyCache = {};
1055
- constructor(request, path11 = "/", matchResult = [[]]) {
1055
+ constructor(request, path12 = "/", matchResult = [[]]) {
1056
1056
  this.raw = request;
1057
- this.path = path11;
1057
+ this.path = path12;
1058
1058
  this.#matchResult = matchResult;
1059
1059
  }
1060
1060
  param(key) {
@@ -1826,8 +1826,8 @@ var Hono = class _Hono {
1826
1826
  return this;
1827
1827
  };
1828
1828
  });
1829
- this.on = (method, path11, ...handlers) => {
1830
- for (const p of [path11].flat()) {
1829
+ this.on = (method, path12, ...handlers) => {
1830
+ for (const p of [path12].flat()) {
1831
1831
  this.#path = p;
1832
1832
  for (const m of [method].flat()) {
1833
1833
  const methodName = m.toUpperCase();
@@ -1885,8 +1885,8 @@ var Hono = class _Hono {
1885
1885
  * app.route("/api", app2) // GET /api/user
1886
1886
  * ```
1887
1887
  */
1888
- route(path11, app) {
1889
- const subApp = this.basePath(path11);
1888
+ route(path12, app) {
1889
+ const subApp = this.basePath(path12);
1890
1890
  app.routes.map((r) => {
1891
1891
  let handler;
1892
1892
  if (app.errorHandler === errorHandler) {
@@ -1912,9 +1912,9 @@ var Hono = class _Hono {
1912
1912
  * const api = new Hono().basePath('/api')
1913
1913
  * ```
1914
1914
  */
1915
- basePath(path11) {
1915
+ basePath(path12) {
1916
1916
  const subApp = this.#clone();
1917
- subApp._basePath = mergePath(this._basePath, path11);
1917
+ subApp._basePath = mergePath(this._basePath, path12);
1918
1918
  return subApp;
1919
1919
  }
1920
1920
  /**
@@ -1988,7 +1988,7 @@ var Hono = class _Hono {
1988
1988
  * })
1989
1989
  * ```
1990
1990
  */
1991
- mount(path11, applicationHandler, options) {
1991
+ mount(path12, applicationHandler, options) {
1992
1992
  let replaceRequest;
1993
1993
  let optionHandler;
1994
1994
  if (options) {
@@ -2015,7 +2015,7 @@ var Hono = class _Hono {
2015
2015
  return [c.env, executionContext];
2016
2016
  };
2017
2017
  replaceRequest ||= (() => {
2018
- const mergedPath = mergePath(this._basePath, path11);
2018
+ const mergedPath = mergePath(this._basePath, path12);
2019
2019
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
2020
2020
  return (request) => {
2021
2021
  const url = new URL(request.url);
@@ -2030,18 +2030,18 @@ var Hono = class _Hono {
2030
2030
  }
2031
2031
  await next();
2032
2032
  };
2033
- this.#addRoute(METHOD_NAME_ALL, mergePath(path11, "*"), handler);
2033
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path12, "*"), handler);
2034
2034
  return this;
2035
2035
  }
2036
- #addRoute(method, path11, handler, baseRoutePath) {
2037
- path11 = mergePath(this._basePath, path11);
2036
+ #addRoute(method, path12, handler, baseRoutePath) {
2037
+ path12 = mergePath(this._basePath, path12);
2038
2038
  const r = {
2039
2039
  basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
2040
- path: path11,
2040
+ path: path12,
2041
2041
  method,
2042
2042
  handler
2043
2043
  };
2044
- this.router.add(method, path11, [handler, r]);
2044
+ this.router.add(method, path12, [handler, r]);
2045
2045
  this.routes.push(r);
2046
2046
  }
2047
2047
  #handleError(err, c) {
@@ -2054,10 +2054,10 @@ var Hono = class _Hono {
2054
2054
  if (method === "HEAD") {
2055
2055
  return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
2056
2056
  }
2057
- const path11 = this.getPath(request, { env });
2058
- const matchResult = this.router.match(method, path11);
2057
+ const path12 = this.getPath(request, { env });
2058
+ const matchResult = this.router.match(method, path12);
2059
2059
  const c = new Context(request, {
2060
- path: path11,
2060
+ path: path12,
2061
2061
  matchResult,
2062
2062
  env,
2063
2063
  executionCtx,
@@ -2160,7 +2160,7 @@ var createNullObject = () => /* @__PURE__ */ Object.create(null);
2160
2160
 
2161
2161
  // node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/matcher.js
2162
2162
  var emptyParam = [];
2163
- function match(method, path11) {
2163
+ function match(method, path12) {
2164
2164
  const matchers = this.buildAllMatchers();
2165
2165
  const match2 = ((method2, path22) => {
2166
2166
  const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
@@ -2176,7 +2176,7 @@ function match(method, path11) {
2176
2176
  return [matcher[1][index], match3];
2177
2177
  });
2178
2178
  this.match = match2;
2179
- return match2(method, path11);
2179
+ return match2(method, path12);
2180
2180
  }
2181
2181
 
2182
2182
  // node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/node.js
@@ -2293,14 +2293,14 @@ var Trie = class {
2293
2293
  #index = 0;
2294
2294
  // dynamic path -> [handler index, param assoc]; static paths are not registered
2295
2295
  paths = createNullObject();
2296
- insert(path11, isStatic) {
2296
+ insert(path12, isStatic) {
2297
2297
  if (isStatic) {
2298
- this.#root.insert(path11.split(""), 0, [], this.#context, true);
2298
+ this.#root.insert(path12.split(""), 0, [], this.#context, true);
2299
2299
  return;
2300
2300
  }
2301
2301
  const paramAssoc = [];
2302
2302
  const groups = [];
2303
- let markedPath = path11;
2303
+ let markedPath = path12;
2304
2304
  for (let i = 0; ; ) {
2305
2305
  let replaced = false;
2306
2306
  markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
@@ -2325,7 +2325,7 @@ var Trie = class {
2325
2325
  }
2326
2326
  }
2327
2327
  this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
2328
- this.paths[path11] = [this.#index++, paramAssoc];
2328
+ this.paths[path12] = [this.#index++, paramAssoc];
2329
2329
  }
2330
2330
  buildRegExp() {
2331
2331
  let regexp = this.#root.buildRegExpStr();
@@ -2352,17 +2352,17 @@ var Trie = class {
2352
2352
 
2353
2353
  // node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/router.js
2354
2354
  var wildcardRegExpCache = createNullObject();
2355
- function buildWildcardRegExp(path11) {
2356
- return wildcardRegExpCache[path11] ??= new RegExp(
2357
- `^${path11.replace(
2355
+ function buildWildcardRegExp(path12) {
2356
+ return wildcardRegExpCache[path12] ??= new RegExp(
2357
+ `^${path12.replace(
2358
2358
  /\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g,
2359
2359
  (match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ONLY_WILDCARD_REG_EXP_STR : `/:${LABEL_REG_EXP_STR}`
2360
2360
  )}$`
2361
2361
  );
2362
2362
  }
2363
- function findMiddleware(middleware, path11) {
2363
+ function findMiddleware(middleware, path12) {
2364
2364
  for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
2365
- if (buildWildcardRegExp(k).test(path11)) {
2365
+ if (buildWildcardRegExp(k).test(path12)) {
2366
2366
  return [...middleware[k]];
2367
2367
  }
2368
2368
  }
@@ -2378,14 +2378,14 @@ var RegExpRouter = class {
2378
2378
  this.#routes = { [METHOD_NAME_ALL]: createNullObject() };
2379
2379
  this.#tries = { [METHOD_NAME_ALL]: new Trie() };
2380
2380
  }
2381
- #insertPath(method, path11) {
2381
+ #insertPath(method, path12) {
2382
2382
  try {
2383
- this.#tries[method].insert(path11, !/\*|\/:/.test(path11));
2383
+ this.#tries[method].insert(path12, !/\*|\/:/.test(path12));
2384
2384
  } catch (e) {
2385
- throw e === PATH_ERROR ? new UnsupportedPathError(path11) : e;
2385
+ throw e === PATH_ERROR ? new UnsupportedPathError(path12) : e;
2386
2386
  }
2387
2387
  }
2388
- add(method, path11, handler) {
2388
+ add(method, path12, handler) {
2389
2389
  const middleware = this.#middleware;
2390
2390
  const routes = this.#routes;
2391
2391
  if (!middleware) {
@@ -2401,28 +2401,28 @@ var RegExpRouter = class {
2401
2401
  }
2402
2402
  }
2403
2403
  }
2404
- if (path11 === "/*") {
2405
- path11 = "*";
2404
+ if (path12 === "/*") {
2405
+ path12 = "*";
2406
2406
  }
2407
2407
  const methods = method === METHOD_NAME_ALL ? Object.keys(middleware) : [method];
2408
- if (/\*$/.test(path11)) {
2409
- const re = buildWildcardRegExp(path11);
2408
+ if (/\*$/.test(path12)) {
2409
+ const re = buildWildcardRegExp(path12);
2410
2410
  for (const m of methods) {
2411
- if (!middleware[m][path11]) {
2412
- this.#insertPath(m, path11);
2413
- middleware[m][path11] = findMiddleware(middleware[m], path11) || findMiddleware(middleware[METHOD_NAME_ALL], path11) || [];
2411
+ if (!middleware[m][path12]) {
2412
+ this.#insertPath(m, path12);
2413
+ middleware[m][path12] = findMiddleware(middleware[m], path12) || findMiddleware(middleware[METHOD_NAME_ALL], path12) || [];
2414
2414
  }
2415
2415
  }
2416
2416
  for (const handlerMap of [middleware, routes]) {
2417
2417
  for (const m of methods) {
2418
2418
  for (const p in handlerMap[m]) {
2419
- re.test(p) && handlerMap[m][p].push([handler, path11]);
2419
+ re.test(p) && handlerMap[m][p].push([handler, path12]);
2420
2420
  }
2421
2421
  }
2422
2422
  }
2423
2423
  return;
2424
2424
  }
2425
- const paths = checkOptionalParameter(path11) || [path11];
2425
+ const paths = checkOptionalParameter(path12) || [path12];
2426
2426
  for (const path22 of paths) {
2427
2427
  for (const m of methods) {
2428
2428
  if (!routes[m][path22]) {
@@ -2451,11 +2451,11 @@ var RegExpRouter = class {
2451
2451
  const handlerData = [];
2452
2452
  const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
2453
2453
  for (const r of [middleware, routes]) {
2454
- for (const path11 in r) {
2455
- const handlers = r[path11];
2456
- const pathData = trie.paths[path11];
2454
+ for (const path12 in r) {
2455
+ const handlers = r[path12];
2456
+ const pathData = trie.paths[path12];
2457
2457
  if (!pathData) {
2458
- staticMap[path11] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
2458
+ staticMap[path12] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
2459
2459
  continue;
2460
2460
  }
2461
2461
  handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [
@@ -2479,13 +2479,13 @@ var SmartRouter = class {
2479
2479
  constructor(init) {
2480
2480
  this.#routers = init.routers;
2481
2481
  }
2482
- add(method, path11, handler) {
2482
+ add(method, path12, handler) {
2483
2483
  if (!this.#routes) {
2484
2484
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
2485
2485
  }
2486
- this.#routes.push([method, path11, handler]);
2486
+ this.#routes.push([method, path12, handler]);
2487
2487
  }
2488
- match(method, path11) {
2488
+ match(method, path12) {
2489
2489
  if (!this.#routes) {
2490
2490
  throw new Error("Fatal error");
2491
2491
  }
@@ -2500,7 +2500,7 @@ var SmartRouter = class {
2500
2500
  for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
2501
2501
  router.add(...routes[i2]);
2502
2502
  }
2503
- res = router.match(method, path11);
2503
+ res = router.match(method, path12);
2504
2504
  } catch (e) {
2505
2505
  if (e instanceof UnsupportedPathError) {
2506
2506
  continue;
@@ -2535,9 +2535,9 @@ var Node2 = class _Node2 {
2535
2535
  #patterns = [];
2536
2536
  #pattern;
2537
2537
  #params = emptyParams;
2538
- insert(method, path11, handler) {
2538
+ insert(method, path12, handler) {
2539
2539
  let curNode = this;
2540
- const parts = splitRoutingPath(path11);
2540
+ const parts = splitRoutingPath(path12);
2541
2541
  const possibleKeys = /* @__PURE__ */ new Set();
2542
2542
  let i = 0;
2543
2543
  for (const p of parts) {
@@ -2577,12 +2577,12 @@ var Node2 = class _Node2 {
2577
2577
  }
2578
2578
  }
2579
2579
  }
2580
- search(method, path11) {
2580
+ search(method, path12) {
2581
2581
  const handlerSets = [];
2582
2582
  this.#params = emptyParams;
2583
2583
  const curNode = this;
2584
2584
  let curNodes = [curNode];
2585
- const parts = splitPath(path11);
2585
+ const parts = splitPath(path12);
2586
2586
  const curNodesQueue = [];
2587
2587
  const len = parts.length;
2588
2588
  let partOffsets = null;
@@ -2624,13 +2624,13 @@ var Node2 = class _Node2 {
2624
2624
  if (matcher !== true) {
2625
2625
  if (!partOffsets) {
2626
2626
  partOffsets = [];
2627
- let offset = path11[0] === "/" ? 1 : 0;
2627
+ let offset = path12[0] === "/" ? 1 : 0;
2628
2628
  for (let p = 0; p < len; p++) {
2629
2629
  partOffsets[p] = offset;
2630
2630
  offset += parts[p].length + 1;
2631
2631
  }
2632
2632
  }
2633
- const restPathString = path11.slice(partOffsets[i]);
2633
+ const restPathString = path12.slice(partOffsets[i]);
2634
2634
  const m = matcher.exec(restPathString);
2635
2635
  if (m) {
2636
2636
  params[name] = m[0];
@@ -2690,13 +2690,13 @@ var Node2 = class _Node2 {
2690
2690
  var TrieRouter = class {
2691
2691
  name = "TrieRouter";
2692
2692
  #node = new Node2();
2693
- add(method, path11, handler) {
2694
- for (const result of checkOptionalParameter(path11) || [path11]) {
2693
+ add(method, path12, handler) {
2694
+ for (const result of checkOptionalParameter(path12) || [path12]) {
2695
2695
  this.#node.insert(method, result, handler);
2696
2696
  }
2697
2697
  }
2698
- match(method, path11) {
2699
- return this.#node.search(method, path11);
2698
+ match(method, path12) {
2699
+ return this.#node.search(method, path12);
2700
2700
  }
2701
2701
  };
2702
2702
 
@@ -2902,6 +2902,11 @@ function parseSuffix(rest) {
2902
2902
  if (!isValidRef(ref)) throw new TargetKeyError(`invalid base ref: ${ref}`);
2903
2903
  return { kind: "base", ref };
2904
2904
  }
2905
+ if (rest.startsWith("checkpoint:")) {
2906
+ const n = rest.slice("checkpoint:".length);
2907
+ if (!/^[1-9][0-9]{0,8}$/.test(n)) throw new TargetKeyError(`invalid checkpoint: ${n}`);
2908
+ return { kind: "checkpoint", id: Number(n) };
2909
+ }
2905
2910
  if (rest.startsWith("range:")) {
2906
2911
  const spec = rest.slice("range:".length);
2907
2912
  const idx = spec.indexOf("..");
@@ -2917,7 +2922,7 @@ function parseTargetKey(key) {
2917
2922
  if (typeof key !== "string" || !key) throw new TargetKeyError("empty target key");
2918
2923
  if (key.startsWith("worktree:")) {
2919
2924
  const rest = key.slice("worktree:".length);
2920
- const m = /^(.+):(working|staged|all|commit:.+|range:.+|base:.+)$/.exec(rest);
2925
+ const m = /^(.+):(working|staged|all|commit:.+|range:.+|base:.+|checkpoint:[0-9]+)$/.exec(rest);
2921
2926
  if (!m?.[1] || !m[2]) throw new TargetKeyError(`invalid worktree target key: ${key}`);
2922
2927
  const worktree = m[1];
2923
2928
  if (!worktree.startsWith("/")) throw new TargetKeyError(`worktree path must be absolute: ${worktree}`);
@@ -2942,6 +2947,9 @@ function formatTargetKey(t) {
2942
2947
  case "base":
2943
2948
  suffix = `base:${t.ref}`;
2944
2949
  break;
2950
+ case "checkpoint":
2951
+ suffix = `checkpoint:${t.id}`;
2952
+ break;
2945
2953
  }
2946
2954
  return t.worktree ? `worktree:${t.worktree}:${suffix}` : suffix;
2947
2955
  }
@@ -3059,7 +3067,8 @@ var ALLOWED_SUBCOMMANDS = /* @__PURE__ */ new Set([
3059
3067
  "merge-base",
3060
3068
  "rev-list",
3061
3069
  "for-each-ref",
3062
- "check-ref-format"
3070
+ "check-ref-format",
3071
+ "grep"
3063
3072
  ]);
3064
3073
  var FORBIDDEN_OPTION_PREFIXES = ["--output", "--ext-diff", "--textconv", "-c", "--config-env", "--exec-path", "--git-dir", "--work-tree"];
3065
3074
  var DEFAULT_GIT_TIMEOUT_MS = 3e4;
@@ -3074,6 +3083,14 @@ var GitError = class extends HttpError {
3074
3083
  exitCode;
3075
3084
  stderr;
3076
3085
  };
3086
+ function snapshotVars(snapshot) {
3087
+ if (!snapshot) return {};
3088
+ return {
3089
+ GIT_OBJECT_DIRECTORY: snapshot.objects,
3090
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: snapshot.alternates,
3091
+ ...snapshot.index ? { GIT_INDEX_FILE: snapshot.index } : {}
3092
+ };
3093
+ }
3077
3094
  function assertAllowedGitArgs(args) {
3078
3095
  const sub = args[0];
3079
3096
  if (!sub || !ALLOWED_SUBCOMMANDS.has(sub)) {
@@ -3082,6 +3099,15 @@ function assertAllowedGitArgs(args) {
3082
3099
  if (sub === "worktree" && args[1] !== "list") {
3083
3100
  throw new GitError(`git worktree ${args[1] ?? ""} is not allowed`, null, "", 400, "git_subcommand_forbidden");
3084
3101
  }
3102
+ if (sub === "grep") {
3103
+ for (const arg of args.slice(1)) {
3104
+ if (arg === "--") break;
3105
+ const name = arg.split("=")[0];
3106
+ if (arg.startsWith("-O") || name.length > 3 && "--open-files-in-pager".startsWith(name)) {
3107
+ throw new GitError(`git option not allowed: ${arg}`, null, "", 400, "git_option_forbidden");
3108
+ }
3109
+ }
3110
+ }
3085
3111
  for (const arg of args.slice(1)) {
3086
3112
  if (arg.includes("\0")) throw new GitError("NUL byte in git argument", null, "", 400, "git_bad_argument");
3087
3113
  for (const prefix of FORBIDDEN_OPTION_PREFIXES) {
@@ -3110,7 +3136,8 @@ async function runGit(args, opts) {
3110
3136
  LC_ALL: "C",
3111
3137
  GIT_PAGER: "cat",
3112
3138
  PAGER: "cat",
3113
- GIT_TERMINAL_PROMPT: "0"
3139
+ GIT_TERMINAL_PROMPT: "0",
3140
+ ...snapshotVars(opts.snapshot)
3114
3141
  },
3115
3142
  windowsHide: true
3116
3143
  },
@@ -3202,6 +3229,14 @@ async function applyOnce(cwd, patch, opts) {
3202
3229
  });
3203
3230
  }
3204
3231
  async function runGitWrite(args, opts) {
3232
+ return execWrite(args, opts, {});
3233
+ }
3234
+ async function runGitSnapshot(args, opts) {
3235
+ const allowed = args[0] === "add" && args[1] === "--all" && args.slice(2).every((a) => a === "--intent-to-add") || args[0] === "write-tree" && args[1] === "--missing-ok" && args.length === 2;
3236
+ if (!allowed) throw new GitError(`not a snapshot command: git ${args.join(" ")}`, null, "", 400, "git_subcommand_forbidden");
3237
+ return execWrite(args, opts, { GIT_OBJECT_DIRECTORY: opts.objects, GIT_INDEX_FILE: opts.index });
3238
+ }
3239
+ function execWrite(args, opts, extraEnv) {
3205
3240
  for (const arg of args) if (arg.includes("\0")) throw new GitError("NUL byte in git argument", null, "", 400, "git_bad_argument");
3206
3241
  return new Promise((resolve, reject) => {
3207
3242
  execFile(
@@ -3212,7 +3247,7 @@ async function runGitWrite(args, opts) {
3212
3247
  timeout: opts.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS,
3213
3248
  maxBuffer: 16 * 1024 * 1024,
3214
3249
  encoding: "utf8",
3215
- env: { ...process.env, LC_ALL: "C", GIT_PAGER: "cat", PAGER: "cat", GIT_TERMINAL_PROMPT: "0" },
3250
+ env: { ...process.env, LC_ALL: "C", GIT_PAGER: "cat", PAGER: "cat", GIT_TERMINAL_PROMPT: "0", ...extraEnv },
3216
3251
  windowsHide: true
3217
3252
  },
3218
3253
  (error, stdout, stderr) => {
@@ -3364,9 +3399,86 @@ async function getRepoInfo(ctx, defaultTarget) {
3364
3399
  }
3365
3400
 
3366
3401
  // packages/server/src/targets.ts
3367
- import path2 from "path";
3402
+ import path3 from "path";
3368
3403
  import { readFile } from "fs/promises";
3369
3404
 
3405
+ // packages/server/src/checkpoints.ts
3406
+ import path2 from "path";
3407
+ import { randomUUID } from "crypto";
3408
+ import { copyFile, mkdir, rm, stat, utimes } from "fs/promises";
3409
+ function checkpointStore(stateFile) {
3410
+ return path2.join(path2.dirname(stateFile), "checkpoints");
3411
+ }
3412
+ async function repoObjects(cwd) {
3413
+ const r = await runGit(["rev-parse", "--git-path", "objects"], { cwd });
3414
+ return path2.resolve(cwd, r.stdout.trim());
3415
+ }
3416
+ async function repoIndex(cwd) {
3417
+ const r = await runGit(["rev-parse", "--git-path", "index"], { cwd });
3418
+ return path2.resolve(cwd, r.stdout.trim());
3419
+ }
3420
+ async function checkpointObjects(cwd, store) {
3421
+ return { objects: path2.join(store, "objects"), alternates: await repoObjects(cwd) };
3422
+ }
3423
+ async function withIndexCopy(cwd, store, fn) {
3424
+ const tmp = path2.join(store, "tmp");
3425
+ await mkdir(path2.join(store, "objects"), { recursive: true });
3426
+ await mkdir(tmp, { recursive: true });
3427
+ const [objects, index] = await Promise.all([checkpointObjects(cwd, store), repoIndex(cwd)]);
3428
+ const copy = path2.join(tmp, `index-${randomUUID()}`);
3429
+ try {
3430
+ const st = await stat(index).catch(() => void 0);
3431
+ if (st) {
3432
+ await copyFile(index, copy);
3433
+ const earlier = Math.floor(st.mtimeMs / 1e3) - 1;
3434
+ await utimes(copy, earlier, earlier);
3435
+ }
3436
+ return await fn({ ...objects, index: copy });
3437
+ } finally {
3438
+ await rm(copy, { force: true });
3439
+ await rm(`${copy}.lock`, { force: true });
3440
+ }
3441
+ }
3442
+ async function snapshotWorktree(cwd, store) {
3443
+ return withIndexCopy(cwd, store, async (snapshot) => {
3444
+ await runGitSnapshot(["add", "--all"], { cwd, ...snapshot });
3445
+ const r = await runGitSnapshot(["write-tree", "--missing-ok"], { cwd, ...snapshot });
3446
+ return r.stdout.trim();
3447
+ });
3448
+ }
3449
+ async function withCheckpointIndex(cwd, store, fn) {
3450
+ return withIndexCopy(cwd, store, async (snapshot) => {
3451
+ await runGitSnapshot(["add", "--all", "--intent-to-add"], { cwd, ...snapshot });
3452
+ return fn(snapshot);
3453
+ });
3454
+ }
3455
+ var MAX_CHECKPOINTS = 20;
3456
+ var sameWorktree = (c, worktree) => c.worktree === worktree;
3457
+ function checkpointsOf(state, worktree) {
3458
+ return state.checkpoints.filter((c) => sameWorktree(c, worktree));
3459
+ }
3460
+ function findCheckpoint(state, worktree, id) {
3461
+ return state.checkpoints.find((c) => sameWorktree(c, worktree) && c.id === id);
3462
+ }
3463
+ function checkpointKey(c) {
3464
+ return formatTargetKey({ kind: "checkpoint", id: c.id, ...c.worktree ? { worktree: c.worktree } : {} });
3465
+ }
3466
+ function forgetCheckpoint(state, c) {
3467
+ state.checkpoints = state.checkpoints.filter((x) => x !== c);
3468
+ const key = checkpointKey(c);
3469
+ const gone = new Set((state.targets[key]?.comments ?? []).map((x) => x.id));
3470
+ delete state.targets[key];
3471
+ if (gone.size) for (const issue of state.issues) issue.commentIds = issue.commentIds.filter((id) => !gone.has(id));
3472
+ }
3473
+ function addCheckpoint(state, worktree, tree, head) {
3474
+ const mine = checkpointsOf(state, worktree);
3475
+ const id = mine.reduce((n, c) => Math.max(n, c.id), 0) + 1;
3476
+ const checkpoint = { id, ...worktree ? { worktree } : {}, tree, head, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
3477
+ state.checkpoints.push(checkpoint);
3478
+ for (const old of mine.slice(0, Math.max(0, mine.length + 1 - MAX_CHECKPOINTS))) forgetCheckpoint(state, old);
3479
+ return checkpoint;
3480
+ }
3481
+
3370
3482
  // packages/server/src/hash.ts
3371
3483
  import { createHash } from "crypto";
3372
3484
  function sha1(input) {
@@ -3587,7 +3699,52 @@ function parseUnifiedDiff(text) {
3587
3699
  return files;
3588
3700
  }
3589
3701
 
3702
+ // packages/server/src/debug.ts
3703
+ var LEADER = String.raw`(?:\/\/+|\/\*+|#+|--|<!--|;+|%+)\s*`;
3704
+ var BLOCK_START = new RegExp(`${LEADER}(?:debug|develblock):start\\b`, "i");
3705
+ var BLOCK_END = new RegExp(`${LEADER}(?:debug|develblock):end\\b`, "i");
3706
+ var LINE_TAG = new RegExp(`${LEADER}[@!]?(?:no-?commit|do not commit)\\b`, "i");
3707
+ var DEBUG_MARKER_STRINGS = ["debug:start", "develblock:start", "nocommit", "no-commit", "do not commit"];
3708
+ function debugLineNumbers(text) {
3709
+ const out = /* @__PURE__ */ new Set();
3710
+ const lines = text.split("\n");
3711
+ let depth = 0;
3712
+ lines.forEach((line, i) => {
3713
+ const no = i + 1;
3714
+ if (BLOCK_START.test(line)) depth++;
3715
+ if (depth > 0 || LINE_TAG.test(line)) out.add(no);
3716
+ if (depth > 0 && BLOCK_END.test(line)) depth--;
3717
+ });
3718
+ return out;
3719
+ }
3720
+ function markDebugLines(diff, newSide, oldSide) {
3721
+ let added = 0;
3722
+ let deleted = 0;
3723
+ for (const hunk of diff.hunks) {
3724
+ for (const line of hunk.lines) {
3725
+ const debug = line.type === "del" ? !!oldSide?.has(line.oldLineNo) : !!newSide?.has(line.newLineNo);
3726
+ if (!debug) continue;
3727
+ line.debug = true;
3728
+ if (line.type === "add") added++;
3729
+ else if (line.type === "del") deleted++;
3730
+ }
3731
+ }
3732
+ if (added) diff.debugAdditions = added;
3733
+ if (deleted) diff.debugDeletions = deleted;
3734
+ }
3735
+
3590
3736
  // packages/server/src/targets.ts
3737
+ function checkpointOf(ctx) {
3738
+ if (!ctx.checkpoint) throw new Error(`checkpoint of ${ctx.key} was not looked up`);
3739
+ return ctx.checkpoint;
3740
+ }
3741
+ async function objectsFor(ctx) {
3742
+ return ctx.target.kind === "checkpoint" ? { snapshot: await checkpointObjects(ctx.cwd, checkpointOf(ctx).store) } : {};
3743
+ }
3744
+ async function runDiff(ctx, args) {
3745
+ if (ctx.target.kind !== "checkpoint") return (await runGit(args, { cwd: ctx.cwd })).stdout;
3746
+ return withCheckpointIndex(ctx.cwd, checkpointOf(ctx).store, async (snapshot) => (await runGit(args, { cwd: ctx.cwd, snapshot })).stdout);
3747
+ }
3591
3748
  var DIFF_BASE_ARGS = ["diff", "--no-color", "--no-ext-diff", "-U3", "-M", "--find-renames"];
3592
3749
  function resolveTargetContext(repo, worktrees, key) {
3593
3750
  let target;
@@ -3641,6 +3798,8 @@ async function diffArgs(ctx) {
3641
3798
  }
3642
3799
  case "base":
3643
3800
  return [...DIFF_BASE_ARGS, await baseSha(ctx, t.ref)];
3801
+ case "checkpoint":
3802
+ return [...DIFF_BASE_ARGS, checkpointOf(ctx).tree];
3644
3803
  }
3645
3804
  }
3646
3805
  function includesUntracked(t) {
@@ -3682,8 +3841,7 @@ async function mapLimit(items, limit, fn) {
3682
3841
  }
3683
3842
  async function listTargetDiffs(ctx) {
3684
3843
  const args = await diffArgs(ctx);
3685
- const r = await runGit(args, { cwd: ctx.cwd });
3686
- const files = parseUnifiedDiff(r.stdout);
3844
+ const files = parseUnifiedDiff(await runDiff(ctx, args));
3687
3845
  if (includesUntracked(ctx.target)) {
3688
3846
  const untracked = await listUntracked(ctx.cwd);
3689
3847
  const extra = await mapLimit(untracked, 8, (f) => untrackedDiff(ctx.cwd, f).catch(() => void 0));
@@ -3705,8 +3863,7 @@ async function getFileDiff(ctx, filePath, hints = {}) {
3705
3863
  const args = await diffArgs(ctx);
3706
3864
  const pathspec = [literal(filePath)];
3707
3865
  if (hints.oldPath && hints.oldPath !== filePath) pathspec.push(literal(hints.oldPath));
3708
- const r = await runGit([...args, "--", ...pathspec], { cwd: ctx.cwd });
3709
- const files = parseUnifiedDiff(r.stdout);
3866
+ const files = parseUnifiedDiff(await runDiff(ctx, [...args, "--", ...pathspec]));
3710
3867
  return files.find((f) => f.path === filePath) ?? files[0];
3711
3868
  }
3712
3869
  async function refForSide(ctx, side) {
@@ -3722,9 +3879,12 @@ async function refForSide(ctx, side) {
3722
3879
  if (side === "new") return t.sha;
3723
3880
  return await revParse(ctx.cwd, `${t.sha}^`) ?? EMPTY_TREE_SHA;
3724
3881
  case "range":
3725
- return side === "new" ? t.head : t.base;
3882
+ if (side === "new") return t.head;
3883
+ return await mergeBase(ctx.cwd, t.base, t.head) ?? t.base;
3726
3884
  case "base":
3727
3885
  return side === "new" ? void 0 : baseSha(ctx, t.ref);
3886
+ case "checkpoint":
3887
+ return side === "new" ? void 0 : checkpointOf(ctx).tree;
3728
3888
  }
3729
3889
  }
3730
3890
  async function getFullFile(ctx, filePath, side) {
@@ -3732,20 +3892,64 @@ async function getFullFile(ctx, filePath, side) {
3732
3892
  const ref = await refForSide(ctx, side);
3733
3893
  if (ref === void 0) {
3734
3894
  try {
3735
- const buf = await readFile(path2.join(ctx.cwd, filePath));
3895
+ const buf = await readFile(path3.join(ctx.cwd, filePath));
3736
3896
  return buf.toString("utf8");
3737
3897
  } catch {
3738
3898
  return null;
3739
3899
  }
3740
3900
  }
3741
3901
  try {
3742
- const r = await runGit(["show", `${ref}:${filePath}`], { cwd: ctx.cwd });
3902
+ const r = await runGit(["show", `${ref}:${filePath}`], { cwd: ctx.cwd, ...await objectsFor(ctx) });
3743
3903
  return r.stdout;
3744
3904
  } catch (e) {
3745
3905
  if (e instanceof HttpError && e.code === "git_failed") return null;
3746
3906
  throw e;
3747
3907
  }
3748
3908
  }
3909
+ var GREP_CHUNK = 500;
3910
+ async function pathsWithMarkers(ctx, side, paths) {
3911
+ const found = /* @__PURE__ */ new Set();
3912
+ if (paths.length === 0) return found;
3913
+ const ref = await refForSide(ctx, side);
3914
+ const args = ["grep", "-l", "-z", "-I", "-i", "-F", ...DEBUG_MARKER_STRINGS.flatMap((m) => ["-e", m])];
3915
+ if (ref === void 0) args.push("--untracked");
3916
+ else if (ref === ":0") args.push("--cached");
3917
+ else args.push(ref);
3918
+ const prefix = ref === void 0 || ref === ":0" ? "" : `${ref}:`;
3919
+ const objects = await objectsFor(ctx);
3920
+ for (let i = 0; i < paths.length; i += GREP_CHUNK) {
3921
+ const chunk = paths.slice(i, i + GREP_CHUNK).map(literal);
3922
+ const r = await runGit([...args, "--", ...chunk], { cwd: ctx.cwd, okCodes: [0, 1], ...objects });
3923
+ for (const p of r.stdout.split("\0")) if (p) found.add(p.startsWith(prefix) ? p.slice(prefix.length) : p);
3924
+ }
3925
+ return found;
3926
+ }
3927
+ async function annotateDebug(ctx, files) {
3928
+ const text = files.filter((f) => !f.binary && f.hunks.length > 0);
3929
+ const oldPathOf = (f) => f.oldPath ?? f.path;
3930
+ const none = () => /* @__PURE__ */ new Set();
3931
+ const [onNew, onOld] = await Promise.all([
3932
+ pathsWithMarkers(
3933
+ ctx,
3934
+ "new",
3935
+ text.filter((f) => f.status !== "deleted").map((f) => f.path)
3936
+ ).catch(none),
3937
+ pathsWithMarkers(ctx, "old", text.filter((f) => f.status !== "added").map(oldPathOf)).catch(none)
3938
+ ]);
3939
+ const read = async (p, side, marked) => {
3940
+ if (!marked.has(p)) return void 0;
3941
+ const content = await getFullFile(ctx, p, side).catch(() => null);
3942
+ return content === null ? void 0 : debugLineNumbers(content);
3943
+ };
3944
+ await mapLimit(
3945
+ text.filter((f) => onNew.has(f.path) || onOld.has(oldPathOf(f))),
3946
+ 8,
3947
+ async (f) => {
3948
+ const [n, o] = await Promise.all([read(f.path, "new", onNew), read(oldPathOf(f), "old", onOld)]);
3949
+ markDebugLines(f, n, o);
3950
+ }
3951
+ );
3952
+ }
3749
3953
 
3750
3954
  // packages/server/src/anchor.ts
3751
3955
  function sideLines(diff, side) {
@@ -3895,9 +4099,9 @@ function cutHunk(hunk, picked, mode) {
3895
4099
  });
3896
4100
  return out;
3897
4101
  }
3898
- function pickedLines(diff, selection) {
4102
+ function pickedLines(diff, selection, skipDebug) {
3899
4103
  const picked = /* @__PURE__ */ new Map();
3900
- const changed = (h) => h.lines.map((l, i) => l.type === "context" ? -1 : i).filter((i) => i >= 0);
4104
+ const changed = (h) => h.lines.map((l, i) => l.type === "context" || skipDebug && l.debug ? -1 : i).filter((i) => i >= 0);
3901
4105
  if (selection === void 0) {
3902
4106
  for (const [i, h] of diff.hunks.entries()) picked.set(i, new Set(changed(h)));
3903
4107
  return picked;
@@ -3921,10 +4125,11 @@ function pickedLines(diff, selection) {
3921
4125
  }
3922
4126
  return picked;
3923
4127
  }
3924
- function buildStagePatch(diff, mode, selection) {
4128
+ function buildStagePatch(diff, mode, selection, opts = {}) {
3925
4129
  if (diff.binary) throw badRequest("binary files cannot be staged from here; use git add", "not_stageable");
3926
4130
  const whole = selection === void 0;
3927
- const picked = pickedLines(diff, selection);
4131
+ const skipDebug = !!opts.skipDebug && mode === "stage";
4132
+ const picked = pickedLines(diff, selection, skipDebug);
3928
4133
  let lines = 0;
3929
4134
  for (const set of picked.values()) lines += set.size;
3930
4135
  const changedTotal = diff.hunks.reduce((n, h) => n + h.lines.filter((l) => l.type !== "context").length, 0);
@@ -3957,7 +4162,11 @@ function buildStagePatch(diff, mode, selection) {
3957
4162
  }
3958
4163
  if (lines === 0) {
3959
4164
  const headerOnly = whole && (modeChange || rename2 || (diff.status === "added" || diff.status === "deleted") && diff.hunks.length === 0);
3960
- if (!headerOnly) throw badRequest("no changed lines selected", "empty_selection");
4165
+ if (!headerOnly) {
4166
+ if (skipDebug && [...pickedLines(diff, selection, false).values()].some((set) => set.size > 0))
4167
+ throw badRequest("every changed line picked is debug code; pick those lines one by one to stage them anyway", "debug_only");
4168
+ throw badRequest("no changed lines selected", "empty_selection");
4169
+ }
3961
4170
  return { patch: header.join("\n") + "\n", lines: 0 };
3962
4171
  }
3963
4172
  let delta = 0;
@@ -3983,15 +4192,15 @@ function buildStagePatch(diff, mode, selection) {
3983
4192
  }
3984
4193
 
3985
4194
  // packages/server/src/worktrees.ts
3986
- import path3 from "path";
3987
- import { readdir, realpath as realpath2, stat } from "fs/promises";
4195
+ import path4 from "path";
4196
+ import { readdir, realpath as realpath2, stat as stat2 } from "fs/promises";
3988
4197
  function slotPath(ctx, slot) {
3989
- return path3.join(path3.dirname(ctx.commonRoot), `${path3.basename(ctx.commonRoot)}-${slot}`);
4198
+ return path4.join(path4.dirname(ctx.commonRoot), `${path4.basename(ctx.commonRoot)}-${slot}`);
3990
4199
  }
3991
4200
  function slotOf(ctx, p) {
3992
- if (path3.dirname(p) !== path3.dirname(ctx.commonRoot)) return void 0;
3993
- const prefix = `${path3.basename(ctx.commonRoot)}-`;
3994
- const name = path3.basename(p);
4201
+ if (path4.dirname(p) !== path4.dirname(ctx.commonRoot)) return void 0;
4202
+ const prefix = `${path4.basename(ctx.commonRoot)}-`;
4203
+ const name = path4.basename(p);
3995
4204
  const n = name.startsWith(prefix) ? name.slice(prefix.length) : "";
3996
4205
  return /^[1-9]\d*$/.test(n) ? Number(n) : void 0;
3997
4206
  }
@@ -4076,7 +4285,7 @@ async function assertBranchName(ctx, name) {
4076
4285
  }
4077
4286
  }
4078
4287
  async function dirUsable(p) {
4079
- const st = await stat(p).catch((e) => {
4288
+ const st = await stat2(p).catch((e) => {
4080
4289
  if (e.code === "ENOENT") return void 0;
4081
4290
  throw e;
4082
4291
  });
@@ -4173,7 +4382,7 @@ async function dropBranch(ctx, wt, asked) {
4173
4382
  }
4174
4383
  async function releaseWorktree(ctx, req) {
4175
4384
  const p = req.path.trim();
4176
- const wt = (await listWorktreesAll(ctx)).find((w) => w.path === p || p && w.path === path3.resolve(p));
4385
+ const wt = (await listWorktreesAll(ctx)).find((w) => w.path === p || p && w.path === path4.resolve(p));
4177
4386
  if (!wt) throw badRequest(`unknown worktree: ${p}`, "unknown_worktree");
4178
4387
  if (wt.isMain) throw badRequest("the main worktree cannot be released", "main_worktree");
4179
4388
  if (wt.bare || slotOf(ctx, wt.path) === void 0) throw badRequest(`${wt.path} is not a slot; remove it instead`, "not_a_slot");
@@ -4189,7 +4398,7 @@ async function releaseWorktree(ctx, req) {
4189
4398
  }
4190
4399
  async function removeWorktree(ctx, req) {
4191
4400
  const p = req.path.trim();
4192
- const wt = (await listWorktreesAll(ctx)).find((w) => w.path === p || p && w.path === path3.resolve(p));
4401
+ const wt = (await listWorktreesAll(ctx)).find((w) => w.path === p || p && w.path === path4.resolve(p));
4193
4402
  if (!wt) throw badRequest(`unknown worktree: ${p}`, "unknown_worktree");
4194
4403
  if (wt.isMain) throw badRequest("the main worktree cannot be removed", "main_worktree");
4195
4404
  const args = ["worktree", "remove"];
@@ -4211,24 +4420,24 @@ async function removeWorktree(ctx, req) {
4211
4420
 
4212
4421
  // packages/server/src/state.ts
4213
4422
  import os from "os";
4214
- import path4 from "path";
4215
- import { mkdir, open, readFile as readFile2, rename, stat as stat2, unlink, writeFile } from "fs/promises";
4423
+ import path5 from "path";
4424
+ import { mkdir as mkdir2, open, readFile as readFile2, rename, stat as stat3, unlink, writeFile } from "fs/promises";
4216
4425
  function dataDir() {
4217
4426
  const xdg = process.env.XDG_DATA_HOME;
4218
- const base = xdg?.trim() ? xdg : path4.join(os.homedir(), ".local", "share");
4219
- return path4.join(base, "warden");
4427
+ const base = xdg?.trim() ? xdg : path5.join(os.homedir(), ".local", "share");
4428
+ return path5.join(base, "warden");
4220
4429
  }
4221
4430
  function repoHash(repoRoot) {
4222
4431
  return sha1(repoRoot).slice(0, 12);
4223
4432
  }
4224
4433
  function stateFilePath(repoRoot, baseDir = dataDir()) {
4225
- return path4.join(baseDir, repoHash(repoRoot), "state.json");
4434
+ return path5.join(baseDir, repoHash(repoRoot), "state.json");
4226
4435
  }
4227
4436
  function defaultPrefs() {
4228
- return { viewMode: "unified", nvimSocketByRoot: {}, autoRefresh: true, railOpen: false };
4437
+ return { viewMode: "unified", nvimSocketByRoot: {}, autoRefresh: true, railOpen: false, ignoreDebug: true };
4229
4438
  }
4230
4439
  function defaultState(repoRoot) {
4231
- return { schemaVersion: 1, repoRoot, targets: {}, issues: [], todos: [], prefs: defaultPrefs() };
4440
+ return { schemaVersion: 1, repoRoot, targets: {}, issues: [], todos: [], checkpoints: [], prefs: defaultPrefs() };
4232
4441
  }
4233
4442
  function ensureTarget(state, key) {
4234
4443
  let t = state.targets[key];
@@ -4239,6 +4448,10 @@ function ensureTarget(state, key) {
4239
4448
  return t;
4240
4449
  }
4241
4450
  var usable = (v) => !!v && typeof v === "object" && typeof v.id === "string";
4451
+ var usableCheckpoint = (v) => {
4452
+ const c = v;
4453
+ return !!c && typeof c === "object" && Number.isInteger(c.id) && typeof c.tree === "string" && (c.worktree === void 0 || typeof c.worktree === "string");
4454
+ };
4242
4455
  var usableComment = (v) => usable(v) && typeof v.anchor === "object" && !!v.anchor;
4243
4456
  function normalise(raw2, repoRoot) {
4244
4457
  const base = defaultState(repoRoot);
@@ -4261,12 +4474,14 @@ function normalise(raw2, repoRoot) {
4261
4474
  targets,
4262
4475
  issues: Array.isArray(r.issues) ? r.issues.filter(usable) : [],
4263
4476
  todos: Array.isArray(r.todos) ? r.todos.filter(usable) : [],
4477
+ checkpoints: Array.isArray(r.checkpoints) ? r.checkpoints.filter(usableCheckpoint) : [],
4264
4478
  prefs: {
4265
4479
  ...defaultPrefs(),
4266
4480
  ...r.prefs ?? {},
4267
4481
  nvimSocketByRoot: r.prefs?.nvimSocketByRoot ?? {},
4268
4482
  autoRefresh: typeof r.prefs?.autoRefresh === "boolean" ? r.prefs.autoRefresh : true,
4269
- railOpen: typeof r.prefs?.railOpen === "boolean" ? r.prefs.railOpen : false
4483
+ railOpen: typeof r.prefs?.railOpen === "boolean" ? r.prefs.railOpen : false,
4484
+ ignoreDebug: typeof r.prefs?.ignoreDebug === "boolean" ? r.prefs.ignoreDebug : true
4270
4485
  }
4271
4486
  };
4272
4487
  }
@@ -4318,7 +4533,7 @@ var StateStore = class {
4318
4533
  /** Apply a mutation under lock. The callback receives the freshest on-disk state. */
4319
4534
  update(fn) {
4320
4535
  const run2 = async () => {
4321
- await mkdir(path4.dirname(this.file), { recursive: true });
4536
+ await mkdir2(path5.dirname(this.file), { recursive: true });
4322
4537
  const release = await this.acquireLock();
4323
4538
  try {
4324
4539
  const state = await this.load();
@@ -4356,7 +4571,7 @@ var StateStore = class {
4356
4571
  const err = e;
4357
4572
  if (err.code !== "EEXIST") throw e;
4358
4573
  try {
4359
- const st = await stat2(lock);
4574
+ const st = await stat3(lock);
4360
4575
  if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
4361
4576
  await unlink(lock).catch(() => void 0);
4362
4577
  continue;
@@ -4381,6 +4596,7 @@ function forgetWorktreeTargets(state, worktreePath) {
4381
4596
  delete state.targets[key];
4382
4597
  }
4383
4598
  if (gone.size) for (const issue of state.issues) issue.commentIds = issue.commentIds.filter((id) => !gone.has(id));
4599
+ state.checkpoints = state.checkpoints.filter((c) => c.worktree !== worktreePath);
4384
4600
  }
4385
4601
 
4386
4602
  // packages/server/src/export.ts
@@ -4457,9 +4673,9 @@ function formatIssueExport({ repoRoot, issue, comments }) {
4457
4673
 
4458
4674
  // packages/server/src/nvim.ts
4459
4675
  import os2 from "os";
4460
- import path5 from "path";
4676
+ import path6 from "path";
4461
4677
  import { execFile as execFile2 } from "child_process";
4462
- import { readdir as readdir2, stat as stat3 } from "fs/promises";
4678
+ import { readdir as readdir2, stat as stat4 } from "fs/promises";
4463
4679
  var QUERY_TIMEOUT_MS = 500;
4464
4680
  var CACHE_TTL_MS = 1e4;
4465
4681
  function execNvim(args, timeoutMs) {
@@ -4488,14 +4704,14 @@ function socketDirs(env = process.env) {
4488
4704
  }
4489
4705
  }
4490
4706
  const tmp = env.TMPDIR || "/tmp";
4491
- if (user) dirs.push(path5.join(tmp, `nvim.${user}`));
4492
- if (env.NVIM_LISTEN_ADDRESS) dirs.push(path5.dirname(env.NVIM_LISTEN_ADDRESS));
4707
+ if (user) dirs.push(path6.join(tmp, `nvim.${user}`));
4708
+ if (env.NVIM_LISTEN_ADDRESS) dirs.push(path6.dirname(env.NVIM_LISTEN_ADDRESS));
4493
4709
  return [...new Set(dirs)];
4494
4710
  }
4495
4711
  var SOCKET_NAME = /^nvim\.(\d+)\.\d+$/;
4496
4712
  async function isSocket(p) {
4497
4713
  try {
4498
- return (await stat3(p)).isSocket();
4714
+ return (await stat4(p)).isSocket();
4499
4715
  } catch {
4500
4716
  return false;
4501
4717
  }
@@ -4510,7 +4726,7 @@ async function findSockets(dirs = socketDirs()) {
4510
4726
  return;
4511
4727
  }
4512
4728
  for (const ent of entries) {
4513
- const full = path5.join(dir, ent.name);
4729
+ const full = path6.join(dir, ent.name);
4514
4730
  const m = SOCKET_NAME.exec(ent.name);
4515
4731
  if (m && (ent.isSocket() || await isSocket(full))) {
4516
4732
  found.set(full, Number(m[1]));
@@ -4597,9 +4813,9 @@ var TmuxService = class {
4597
4813
  const sessions = [];
4598
4814
  for (const line of output.trimEnd().split("\n")) {
4599
4815
  const [id, name, ...parts] = line.split(" ");
4600
- const path11 = parts.join(" ");
4601
- if (!id || !/^\$\d+$/.test(id) || !name || !path11) continue;
4602
- if (await realpath3(path11).catch(() => null) === root) sessions.push({ id, name, path: path11 });
4816
+ const path12 = parts.join(" ");
4817
+ if (!id || !/^\$\d+$/.test(id) || !name || !path12) continue;
4818
+ if (await realpath3(path12).catch(() => null) === root) sessions.push({ id, name, path: path12 });
4603
4819
  }
4604
4820
  return sessions;
4605
4821
  }
@@ -4613,8 +4829,8 @@ var TmuxService = class {
4613
4829
  };
4614
4830
 
4615
4831
  // packages/server/src/watcher.ts
4616
- import path6 from "path";
4617
- import { stat as stat4 } from "fs/promises";
4832
+ import path7 from "path";
4833
+ import { stat as stat5 } from "fs/promises";
4618
4834
  var DEFAULT_POLL_INTERVAL_MS = 1500;
4619
4835
  function statusPaths(out) {
4620
4836
  const parts = out.split("\0");
@@ -4710,7 +4926,7 @@ var RepoWatcher = class {
4710
4926
  const stats = await Promise.all(
4711
4927
  statusPaths(statusRes.stdout).map(async (rel) => {
4712
4928
  try {
4713
- const st = await stat4(path6.join(this.root, rel));
4929
+ const st = await stat5(path7.join(this.root, rel));
4714
4930
  return `${rel} ${st.mtimeMs} ${st.size}`;
4715
4931
  } catch {
4716
4932
  return `${rel} -`;
@@ -4722,8 +4938,8 @@ var RepoWatcher = class {
4722
4938
  };
4723
4939
 
4724
4940
  // packages/server/src/static.ts
4725
- import path7 from "path";
4726
- import { readFile as readFile3, stat as stat5 } from "fs/promises";
4941
+ import path8 from "path";
4942
+ import { readFile as readFile3, stat as stat6 } from "fs/promises";
4727
4943
  var MIME = {
4728
4944
  ".html": "text/html; charset=utf-8",
4729
4945
  ".js": "text/javascript; charset=utf-8",
@@ -4740,20 +4956,20 @@ var MIME = {
4740
4956
  ".txt": "text/plain; charset=utf-8"
4741
4957
  };
4742
4958
  async function serveStaticFile(c, webDir) {
4743
- const root = path7.resolve(webDir);
4959
+ const root = path8.resolve(webDir);
4744
4960
  let rel;
4745
4961
  try {
4746
4962
  rel = decodeURIComponent(c.req.path);
4747
4963
  } catch {
4748
4964
  return c.text("Bad Request", 400);
4749
4965
  }
4750
- let target = path7.resolve(root, "." + rel);
4751
- if (target !== root && !target.startsWith(root + path7.sep)) return c.text("Forbidden", 403);
4966
+ let target = path8.resolve(root, "." + rel);
4967
+ if (target !== root && !target.startsWith(root + path8.sep)) return c.text("Forbidden", 403);
4752
4968
  try {
4753
- const st = await stat5(target);
4754
- if (st.isDirectory()) target = path7.join(target, "index.html");
4969
+ const st = await stat6(target);
4970
+ if (st.isDirectory()) target = path8.join(target, "index.html");
4755
4971
  } catch {
4756
- target = path7.join(root, "index.html");
4972
+ target = path8.join(root, "index.html");
4757
4973
  }
4758
4974
  let body;
4759
4975
  try {
@@ -4761,7 +4977,7 @@ async function serveStaticFile(c, webDir) {
4761
4977
  } catch {
4762
4978
  return c.text("Not Found", 404);
4763
4979
  }
4764
- const ext = path7.extname(target).toLowerCase();
4980
+ const ext = path8.extname(target).toLowerCase();
4765
4981
  const isAsset = rel.startsWith("/assets/");
4766
4982
  return new Response(new Uint8Array(body), {
4767
4983
  status: 200,
@@ -4797,7 +5013,8 @@ function createApp(opts) {
4797
5013
  worktreeCache = { at: Date.now(), list };
4798
5014
  return list;
4799
5015
  };
4800
- const targetCtx = async (key) => {
5016
+ const checkpoints = checkpointStore(store.file);
5017
+ const worktreeCtx = async (key) => {
4801
5018
  let list = await worktrees();
4802
5019
  try {
4803
5020
  return resolveTargetContext(repo, list, key);
@@ -4809,6 +5026,13 @@ function createApp(opts) {
4809
5026
  throw e;
4810
5027
  }
4811
5028
  };
5029
+ const targetCtx = async (key) => {
5030
+ const ctx = await worktreeCtx(key);
5031
+ if (ctx.target.kind !== "checkpoint") return ctx;
5032
+ const cp = findCheckpoint(await store.load(), ctx.target.worktree, ctx.target.id);
5033
+ if (!cp) throw notFound(`no checkpoint #${ctx.target.id} in this worktree`, "unknown_checkpoint");
5034
+ return { ...ctx, checkpoint: { tree: cp.tree, store: checkpoints } };
5035
+ };
4812
5036
  const fileDiffWithHints = async (ctx, filePath, explicit) => {
4813
5037
  const cached = listings.get(ctx.key);
4814
5038
  const hint = cached && Date.now() - cached.at < LISTING_HINT_TTL_MS ? cached.files.find((f) => f.path === filePath) : void 0;
@@ -4845,7 +5069,7 @@ function createApp(opts) {
4845
5069
  worktreeCache = { at: Date.now(), list: info.worktrees };
4846
5070
  const last = state.prefs.lastTarget;
4847
5071
  const target = last ? tryParseTargetKey(last) : void 0;
4848
- const usable2 = !!target && (!target.worktree || info.worktrees.some((w) => w.path === target.worktree));
5072
+ const usable2 = !!target && (!target.worktree || info.worktrees.some((w) => w.path === target.worktree)) && (target.kind !== "checkpoint" || !!findCheckpoint(state, target.worktree, target.id));
4849
5073
  if (last && usable2) info.defaultTarget = last;
4850
5074
  else if (last) {
4851
5075
  await store.update((s) => {
@@ -4863,6 +5087,7 @@ function createApp(opts) {
4863
5087
  if (typeof body.lastTarget === "string") s.prefs.lastTarget = body.lastTarget;
4864
5088
  if (typeof body.autoRefresh === "boolean") s.prefs.autoRefresh = body.autoRefresh;
4865
5089
  if (typeof body.railOpen === "boolean") s.prefs.railOpen = body.railOpen;
5090
+ if (typeof body.ignoreDebug === "boolean") s.prefs.ignoreDebug = body.ignoreDebug;
4866
5091
  if (body.nvimSocketByRoot && typeof body.nvimSocketByRoot === "object") {
4867
5092
  s.prefs.nvimSocketByRoot = { ...s.prefs.nvimSocketByRoot, ...body.nvimSocketByRoot };
4868
5093
  }
@@ -4874,6 +5099,7 @@ function createApp(opts) {
4874
5099
  const key = c.req.param("key");
4875
5100
  const ctx = await targetCtx(key);
4876
5101
  const diffs = await listTargetDiffs(ctx);
5102
+ await annotateDebug(ctx, diffs);
4877
5103
  listings.set(key, { at: Date.now(), files: diffs });
4878
5104
  const notices = changedNotices.get(key) ?? /* @__PURE__ */ new Set();
4879
5105
  changedNotices.set(key, notices);
@@ -4914,6 +5140,7 @@ function createApp(opts) {
4914
5140
  const untracked = c.req.query("untracked") === "1" ? true : void 0;
4915
5141
  const diff = await fileDiffWithHints(ctx, filePath, { oldPath, untracked });
4916
5142
  if (!diff) throw notFound(`no diff for ${filePath} in ${key}`, "no_diff");
5143
+ await annotateDebug(ctx, [diff]);
4917
5144
  return c.json(diff);
4918
5145
  });
4919
5146
  api.get("/targets/:key/file/full", async (c) => {
@@ -4941,6 +5168,33 @@ function createApp(opts) {
4941
5168
  changedNotices.get(key)?.delete(body.path);
4942
5169
  return c.json({ viewed });
4943
5170
  });
5171
+ api.get("/targets/:key/checkpoints", async (c) => {
5172
+ const ctx = await worktreeCtx(c.req.param("key"));
5173
+ const res = { checkpoints: checkpointsOf(await store.load(), ctx.target.worktree) };
5174
+ return c.json(res);
5175
+ });
5176
+ api.post("/targets/:key/checkpoints", async (c) => {
5177
+ const ctx = await worktreeCtx(c.req.param("key"));
5178
+ const tree = await snapshotWorktree(ctx.cwd, checkpoints);
5179
+ const head = await revParse(ctx.cwd, "HEAD") ?? "";
5180
+ const res = await store.update((s) => {
5181
+ const newest = checkpointsOf(s, ctx.target.worktree).at(-1);
5182
+ const unchanged = newest?.tree === tree;
5183
+ const checkpoint = unchanged ? newest : addCheckpoint(s, ctx.target.worktree, tree, head);
5184
+ return { checkpoint, unchanged, targetKey: checkpointKey(checkpoint) };
5185
+ });
5186
+ return c.json(res, res.unchanged ? 200 : 201);
5187
+ });
5188
+ api.delete("/targets/:key/checkpoints/:id", async (c) => {
5189
+ const ctx = await worktreeCtx(c.req.param("key"));
5190
+ const id = Number(c.req.param("id"));
5191
+ await store.update((s) => {
5192
+ const cp = Number.isInteger(id) ? findCheckpoint(s, ctx.target.worktree, id) : void 0;
5193
+ if (!cp) throw notFound(`no checkpoint #${c.req.param("id")} in this worktree`, "unknown_checkpoint");
5194
+ forgetCheckpoint(s, cp);
5195
+ });
5196
+ return c.json({ ok: true });
5197
+ });
4944
5198
  api.post("/targets/:key/stage", async (c) => {
4945
5199
  const key = c.req.param("key");
4946
5200
  const ctx = await targetCtx(key);
@@ -4950,11 +5204,13 @@ function createApp(opts) {
4950
5204
  if (!body.path || typeof body.path !== "string") throw badRequest("missing path");
4951
5205
  if (!body.contentHash || typeof body.contentHash !== "string") throw badRequest("contentHash is required");
4952
5206
  if (body.hunks !== void 0 && !Array.isArray(body.hunks)) throw badRequest("hunks must be an array", "bad_selection");
5207
+ if (body.skipDebug !== void 0 && typeof body.skipDebug !== "boolean") throw badRequest("skipDebug must be a boolean", "bad_selection");
4953
5208
  const diff = await fileDiffWithHints(ctx, body.path);
4954
5209
  if (!diff) throw badRequest(`file ${body.path} is not part of ${key}`, "no_diff");
4955
5210
  if (diff.contentHash !== body.contentHash)
4956
5211
  throw new HttpError(409, "the diff changed since it was loaded; refresh and pick the lines again", "diff_changed");
4957
- const { patch, lines } = buildStagePatch(diff, mode, body.hunks);
5212
+ if (body.skipDebug) await annotateDebug(ctx, [diff]);
5213
+ const { patch, lines } = buildStagePatch(diff, mode, body.hunks, { skipDebug: body.skipDebug });
4958
5214
  await applyToIndex(ctx.cwd, patch, { reverse: mode === "unstage" });
4959
5215
  void watchers.get(ctx.cwd)?.poll();
4960
5216
  const res = { ok: true, lines };
@@ -4980,7 +5236,7 @@ function createApp(opts) {
4980
5236
  if (!anchored) throw badRequest("selection is not fully visible in the diff on that side (must be inside one hunk)", "bad_selection");
4981
5237
  const now = (/* @__PURE__ */ new Date()).toISOString();
4982
5238
  const comment = {
4983
- id: randomUUID(),
5239
+ id: randomUUID2(),
4984
5240
  targetKey: key,
4985
5241
  filePath: body.filePath,
4986
5242
  side: body.side,
@@ -5179,7 +5435,7 @@ function createApp(opts) {
5179
5435
  if (!body.title?.trim()) throw badRequest("title is required");
5180
5436
  const now = (/* @__PURE__ */ new Date()).toISOString();
5181
5437
  const issue = {
5182
- id: randomUUID(),
5438
+ id: randomUUID2(),
5183
5439
  title: body.title.trim(),
5184
5440
  body: typeof body.body === "string" ? body.body : "",
5185
5441
  status: body.status === "closed" ? "closed" : "open",
@@ -5273,7 +5529,7 @@ function createApp(opts) {
5273
5529
  const branch = body.branch?.trim() || await currentBranch(await knownRoot(body.root));
5274
5530
  const now = (/* @__PURE__ */ new Date()).toISOString();
5275
5531
  const todo = {
5276
- id: randomUUID(),
5532
+ id: randomUUID2(),
5277
5533
  branch,
5278
5534
  title: body.title.trim(),
5279
5535
  body: typeof body.body === "string" ? body.body : "",
@@ -5475,7 +5731,7 @@ function createApp(opts) {
5475
5731
  api.post("/nvim/open", async (c) => {
5476
5732
  const body = await c.req.json();
5477
5733
  if (!body.socket || !body.absPath) throw badRequest("socket and absPath are required");
5478
- if (!path8.isAbsolute(body.absPath)) throw badRequest("absPath must be absolute");
5734
+ if (!path9.isAbsolute(body.absPath)) throw badRequest("absPath must be absolute");
5479
5735
  const scan = await nvim.scan();
5480
5736
  if (!scan.instances.some((i) => i.socket === body.socket)) {
5481
5737
  const fresh = await nvim.scan(true);
@@ -5535,14 +5791,14 @@ function reachableFromWindows(port, token, timeoutMs = PROBE_TIMEOUT_MS) {
5535
5791
  }
5536
5792
 
5537
5793
  // packages/server/src/update.ts
5538
- import path9 from "path";
5539
- import { mkdir as mkdir2, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
5794
+ import path10 from "path";
5795
+ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
5540
5796
  var PACKAGE = "@xbghc/warden";
5541
5797
  var LATEST_URL = "https://registry.npmjs.org/@xbghc%2fwarden/latest";
5542
5798
  var FETCH_TIMEOUT_MS = 3e3;
5543
5799
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
5544
5800
  function updateCacheFile(baseDir = dataDir()) {
5545
- return path9.join(baseDir, "update-check.json");
5801
+ return path10.join(baseDir, "update-check.json");
5546
5802
  }
5547
5803
  function updateCheckEnabled(env = process.env) {
5548
5804
  return !env.WARDEN_NO_UPDATE_CHECK && !env.NO_UPDATE_NOTIFIER && !env.CI;
@@ -5576,7 +5832,7 @@ async function readCache(file, now) {
5576
5832
  }
5577
5833
  async function writeCache(file, cache) {
5578
5834
  try {
5579
- await mkdir2(path9.dirname(file), { recursive: true });
5835
+ await mkdir3(path10.dirname(file), { recursive: true });
5580
5836
  await writeFile2(file, JSON.stringify(cache));
5581
5837
  } catch {
5582
5838
  }
@@ -5621,7 +5877,7 @@ async function startServer(opts) {
5621
5877
  const repo = await resolveRepo(opts.repoPath);
5622
5878
  const stateFile = opts.stateFile ?? stateFilePath(repo.commonRoot);
5623
5879
  const store = new StateStore(stateFile, repo.commonRoot);
5624
- const instanceToken = randomUUID2();
5880
+ const instanceToken = randomUUID3();
5625
5881
  const app = createApp({ repo, store, nvim: new NvimService(), webDir: opts.webDir, instanceToken, update: opts.update });
5626
5882
  const listen = (p) => new Promise((resolve, reject) => {
5627
5883
  const s = serve({ fetch: app.fetch, hostname: HOST, port: p }, () => resolve(s));
@@ -5665,7 +5921,7 @@ async function startServer(opts) {
5665
5921
  }
5666
5922
 
5667
5923
  // bin/cli.ts
5668
- var VERSION = true ? "0.13.1" : "dev";
5924
+ var VERSION = true ? "0.15.0" : "dev";
5669
5925
  function parseArgs(argv) {
5670
5926
  const args = { repoPath: process.cwd(), open: true, updateCheck: true, help: false, version: false };
5671
5927
  for (let i = 0; i < argv.length; i++) {
@@ -5684,7 +5940,7 @@ function parseArgs(argv) {
5684
5940
  } else if (a === "--help" || a === "-h") args.help = true;
5685
5941
  else if (a === "--version" || a === "-v") args.version = true;
5686
5942
  else if (a.startsWith("-")) throw new Error(`unknown option: ${a}`);
5687
- else args.repoPath = path10.resolve(a);
5943
+ else args.repoPath = path11.resolve(a);
5688
5944
  }
5689
5945
  return args;
5690
5946
  }
@@ -5729,16 +5985,16 @@ async function openBrowser(url) {
5729
5985
  return false;
5730
5986
  }
5731
5987
  function resolveWebDir() {
5732
- const here = path10.dirname(fileURLToPath(import.meta.url));
5988
+ const here = path11.dirname(fileURLToPath(import.meta.url));
5733
5989
  const candidates = [
5734
5990
  process.env.WARDEN_WEB_DIR,
5735
- path10.join(here, "web"),
5991
+ path11.join(here, "web"),
5736
5992
  // dist/cli.js -> dist/web
5737
- path10.join(here, "..", "dist", "web")
5993
+ path11.join(here, "..", "dist", "web")
5738
5994
  // bin/cli.ts (tsx dev) -> dist/web
5739
5995
  ].filter((p) => !!p);
5740
5996
  for (const dir of candidates) {
5741
- if (existsSync2(path10.join(dir, "index.html"))) return dir;
5997
+ if (existsSync2(path11.join(dir, "index.html"))) return dir;
5742
5998
  }
5743
5999
  return void 0;
5744
6000
  }
@@ -5763,7 +6019,7 @@ async function main() {
5763
6019
  if (!webDir) {
5764
6020
  console.error("warning: built web assets not found (dist/web). Only the /api endpoints will be served.");
5765
6021
  }
5766
- const viaNpx = fileURLToPath(import.meta.url).includes(`${path10.sep}_npx${path10.sep}`);
6022
+ const viaNpx = fileURLToPath(import.meta.url).includes(`${path11.sep}_npx${path11.sep}`);
5767
6023
  const checking = args.updateCheck && VERSION !== "dev" && updateCheckEnabled();
5768
6024
  const update = checking ? checkForUpdate({ current: VERSION, npx: viaNpx }) : Promise.resolve(null);
5769
6025
  let server;