From b66c2832c5a6b11f2e6aa91a2461976f6b2b67d5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:39:54 +0000 Subject: [PATCH 1/5] fix(list-command): handle missing generated files in subcommand interception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getSubcommandsForRoute() uses require('../app.js') which transitively loads api-schema.ts, importing from a generated file that may not exist during development or in CI environments without a prior build step. Previously this threw an uncaught ResolveMessage error, crashing the entire command. Now it catches the error and returns an empty set, allowing the command to proceed without subcommand interception. The catch does not cache the failure — subsequent calls will retry loading the route tree, so the function self-heals once the generated file becomes available. Co-authored-by: Miguel Betegón --- src/lib/list-command.ts | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/lib/list-command.ts b/src/lib/list-command.ts index d814ed97c..78383d733 100644 --- a/src/lib/list-command.ts +++ b/src/lib/list-command.ts @@ -326,31 +326,38 @@ let _subcommandsByRoute: Map> | undefined; */ function getSubcommandsForRoute(routeName: string): Set { if (!_subcommandsByRoute) { - _subcommandsByRoute = new Map(); - - const { routes } = require("../app.js") as { - routes: { - getAllEntries: () => readonly { - name: { original: string }; - target: unknown; - }[]; + try { + const { routes } = require("../app.js") as { + routes: { + getAllEntries: () => readonly { + name: { original: string }; + target: unknown; + }[]; + }; }; - }; - for (const entry of routes.getAllEntries()) { - const target = entry.target as unknown as Record; - if (typeof target?.getAllEntries === "function") { - const children = ( - target.getAllEntries as () => readonly { - name: { original: string }; - }[] - )(); - const names = new Set(); - for (const child of children) { - names.add(child.name.original); + const map = new Map>(); + for (const entry of routes.getAllEntries()) { + const target = entry.target as unknown as Record; + if (typeof target?.getAllEntries === "function") { + const children = ( + target.getAllEntries as () => readonly { + name: { original: string }; + }[] + )(); + const names = new Set(); + for (const child of children) { + names.add(child.name.original); + } + map.set(entry.name.original, names); } - _subcommandsByRoute.set(entry.name.original, names); } + _subcommandsByRoute = map; + } catch { + // Route tree may fail to load if optional generated files (e.g. + // api-schema.json) are missing. Return empty set — subcommand + // interception is a UX nicety, not critical functionality. + return new Set(); } } From 2e7146fbca964e17beccba33c8b1455914875758 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:42:43 +0000 Subject: [PATCH 2/5] fix(arg-parsing): reject negative values in parseSpanDepth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseSpanDepth() accepted negative numbers like -5 or -100 and returned them as-is. Negative depth values are nonsensical for span tree display and cause the span tree to be silently skipped (since the downstream `spans > 0` check evaluates to false) without any feedback to the user. Now treats negative values the same as non-numeric input: falls back to the default depth (3). Co-authored-by: Miguel Betegón --- src/lib/arg-parsing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/arg-parsing.ts b/src/lib/arg-parsing.ts index 4105f90f5..118531831 100644 --- a/src/lib/arg-parsing.ts +++ b/src/lib/arg-parsing.ts @@ -315,7 +315,7 @@ export function parseSpanDepth(input: string): number { return 0; } const n = Number(input); - if (Number.isNaN(n)) { + if (Number.isNaN(n) || n < 0) { return DEFAULT_SPAN_DEPTH; } return n; From db53c78f3d16fb9fb494ecfdc4badb8660fb85da Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:44:01 +0000 Subject: [PATCH 3/5] fix(pagination): handle corrupted cursor_stack JSON gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getPaginationState() calls JSON.parse(row.cursor_stack) without error handling. If the stored JSON is corrupted (e.g., from an interrupted write, manual DB edit, or storage bug), this throws an unhandled SyntaxError that crashes the command. Now wraps the parse in a try-catch. On failure, deletes the corrupt row and returns undefined (treating it as missing state), which causes the command to start fresh pagination — the same behavior as an expired cursor. Co-authored-by: Miguel Betegón --- src/lib/db/pagination.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/db/pagination.ts b/src/lib/db/pagination.ts index c702918d9..c51029fd7 100644 --- a/src/lib/db/pagination.ts +++ b/src/lib/db/pagination.ts @@ -84,7 +84,15 @@ export function getPaginationState( return; } - const stack = JSON.parse(row.cursor_stack) as string[]; + let stack: string[]; + try { + stack = JSON.parse(row.cursor_stack) as string[]; + } catch { + db.query( + "DELETE FROM pagination_cursors WHERE command_key = ? AND context = ?" + ).run(commandKey, contextKey); + return; + } return { stack, index: row.page_index }; } From 5f9207f8fd38e3c7d4aaab4e8f1e5394c76a7459 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:26:51 +0000 Subject: [PATCH 4/5] revert: remove try-catch from getSubcommandsForRoute (crash is intentional) Co-authored-by: Burak Yigit Kaya --- src/lib/list-command.ts | 49 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/lib/list-command.ts b/src/lib/list-command.ts index 78383d733..d99cdcb0c 100644 --- a/src/lib/list-command.ts +++ b/src/lib/list-command.ts @@ -326,39 +326,32 @@ let _subcommandsByRoute: Map> | undefined; */ function getSubcommandsForRoute(routeName: string): Set { if (!_subcommandsByRoute) { - try { - const { routes } = require("../app.js") as { - routes: { - getAllEntries: () => readonly { - name: { original: string }; - target: unknown; - }[]; - }; + const { routes } = require("../app.js") as { + routes: { + getAllEntries: () => readonly { + name: { original: string }; + target: unknown; + }[]; }; + }; - const map = new Map>(); - for (const entry of routes.getAllEntries()) { - const target = entry.target as unknown as Record; - if (typeof target?.getAllEntries === "function") { - const children = ( - target.getAllEntries as () => readonly { - name: { original: string }; - }[] - )(); - const names = new Set(); - for (const child of children) { - names.add(child.name.original); - } - map.set(entry.name.original, names); + const map = new Map>(); + for (const entry of routes.getAllEntries()) { + const target = entry.target as unknown as Record; + if (typeof target?.getAllEntries === "function") { + const children = ( + target.getAllEntries as () => readonly { + name: { original: string }; + }[] + )(); + const names = new Set(); + for (const child of children) { + names.add(child.name.original); } + map.set(entry.name.original, names); } - _subcommandsByRoute = map; - } catch { - // Route tree may fail to load if optional generated files (e.g. - // api-schema.json) are missing. Return empty set — subcommand - // interception is a UX nicety, not critical functionality. - return new Set(); } + _subcommandsByRoute = map; } return _subcommandsByRoute.get(routeName) ?? new Set(); From 797b15be58366f07200ab112266d5928b9259c82 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:48:44 +0000 Subject: [PATCH 5/5] revert: restore original eager Map assignment in getSubcommandsForRoute Co-authored-by: Burak Yigit Kaya --- src/lib/list-command.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/list-command.ts b/src/lib/list-command.ts index d99cdcb0c..d814ed97c 100644 --- a/src/lib/list-command.ts +++ b/src/lib/list-command.ts @@ -326,6 +326,8 @@ let _subcommandsByRoute: Map> | undefined; */ function getSubcommandsForRoute(routeName: string): Set { if (!_subcommandsByRoute) { + _subcommandsByRoute = new Map(); + const { routes } = require("../app.js") as { routes: { getAllEntries: () => readonly { @@ -335,7 +337,6 @@ function getSubcommandsForRoute(routeName: string): Set { }; }; - const map = new Map>(); for (const entry of routes.getAllEntries()) { const target = entry.target as unknown as Record; if (typeof target?.getAllEntries === "function") { @@ -348,10 +349,9 @@ function getSubcommandsForRoute(routeName: string): Set { for (const child of children) { names.add(child.name.original); } - map.set(entry.name.original, names); + _subcommandsByRoute.set(entry.name.original, names); } } - _subcommandsByRoute = map; } return _subcommandsByRoute.get(routeName) ?? new Set();