@bumpyclock/pi-tasque 0.2.0 → 0.2.1

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.
@@ -0,0 +1,123 @@
1
+ import {
2
+ fieldRequired,
3
+ requireStringField,
4
+ } from "../shared/validation.js";
5
+ import {
6
+ validateBulkItems,
7
+ validateCreateTreeNode,
8
+ } from "./bulk-contract.js";
9
+ import type { TaskAction, TaskParams } from "./task-schema.js";
10
+
11
+ export function validateTaskParams(
12
+ params: TaskParams,
13
+ ): { readonly ok: true } | { readonly ok: false; readonly message: string } {
14
+ switch (params.action) {
15
+ case "find":
16
+ if (params.view === "tree") return { ok: true };
17
+ if (params.tasks === undefined) return fieldRequired("tasks");
18
+ return { ok: true };
19
+ case "show":
20
+ case "deps":
21
+ case "notes":
22
+ case "note":
23
+ case "finish":
24
+ case "reopen":
25
+ case "defer":
26
+ case "start":
27
+ case "claim":
28
+ case "import":
29
+ return requireStringField(params.task, "task");
30
+ case "create": {
31
+ const task = requireStringField(params.task, "task");
32
+ if (!task.ok) return task;
33
+ const kind = requireStringField(params.kind, "kind");
34
+ if (!kind.ok) return kind;
35
+ return typeof params.priority === "number"
36
+ ? { ok: true }
37
+ : fieldRequired("priority");
38
+ }
39
+ case "similar":
40
+ return requireStringField(params.query, "query");
41
+ case "block":
42
+ case "unblock": {
43
+ const task = requireStringField(params.task, "task");
44
+ if (!task.ok) return task;
45
+ return requireStringField(params.by, "by");
46
+ }
47
+ case "order":
48
+ case "unorder": {
49
+ const task = requireStringField(params.task, "task");
50
+ if (!task.ok) return task;
51
+ return requireStringField(params.after, "after");
52
+ }
53
+ case "link": {
54
+ const todo = requireTodoId(params.todo);
55
+ if (!todo.ok) return todo;
56
+ return requireStringField(params.task, "task");
57
+ }
58
+ case "promote":
59
+ return requireTodoId(params.todo);
60
+ case "mark_planned":
61
+ return requireStringField(params.task, "task");
62
+ case "spec": {
63
+ const task = requireStringField(params.task, "task");
64
+ if (!task.ok) return task;
65
+ const mode = params.mode as string | undefined;
66
+ if (mode === undefined || mode.trim().length === 0)
67
+ return fieldRequired("mode");
68
+ const isRead = mode === "show" || mode === "check";
69
+ const isWrite = mode === "set" || mode === "update";
70
+ if (!isRead && !isWrite)
71
+ return {
72
+ ok: false,
73
+ message: "mode must be show, check, set, or update",
74
+ };
75
+ if (isRead && params.text !== undefined)
76
+ return {
77
+ ok: false,
78
+ message: `spec ${mode} does not accept text`,
79
+ };
80
+ if (isWrite) {
81
+ const text = params.text?.trim();
82
+ if (text === undefined || text.length === 0)
83
+ return {
84
+ ok: false,
85
+ message: `spec ${mode} requires text`,
86
+ };
87
+ }
88
+ return { ok: true };
89
+ }
90
+ case "bulk":
91
+ return validateBulkItems(params.items);
92
+ case "create_tree":
93
+ return validateCreateTreeNode(params.root);
94
+ case "handoff_check":
95
+ case "doctor":
96
+ case "list_links":
97
+ return { ok: true };
98
+ }
99
+ }
100
+
101
+ export function actionUsesTasque(action: TaskAction): boolean {
102
+ return (
103
+ action !== "link" && action !== "list_links" && action !== "handoff_check"
104
+ );
105
+ }
106
+
107
+ export function hasWith(params: TaskParams, value: string): boolean {
108
+ return Array.isArray(params.with) && params.with.includes(value);
109
+ }
110
+
111
+ export function getTodoId(
112
+ value: boolean | number | undefined,
113
+ ): number | undefined {
114
+ return typeof value === "number" ? value : undefined;
115
+ }
116
+
117
+ export function requireTodoId(
118
+ value: boolean | number | undefined,
119
+ ): { readonly ok: true } | { readonly ok: false; readonly message: string } {
120
+ return typeof value === "number" && Number.isInteger(value) && value >= 1
121
+ ? { ok: true }
122
+ : fieldRequired("todo");
123
+ }
@@ -12,6 +12,7 @@ import type {
12
12
  ExtensionContext,
13
13
  } from "@earendil-works/pi-coding-agent";
14
14
  import type { BulkItem, BulkItemAction, BulkResult } from "./bulk-contract.js";
15
+ import { isRecord } from "../shared/error-utils.js";
15
16
  import type { StandardToolDetails } from "../shared/tool-result.js";
16
17
  import { okToolDetails, textToolResult } from "../shared/tool-result.js";
17
18
  import {
@@ -138,7 +139,3 @@ function formatBulkText(result: BulkResult, total: number): string {
138
139
  }
139
140
 
140
141
  // ── Utilities ──────────────────────────────────────────────────────
141
-
142
- function isRecord(value: unknown): value is Record<string, unknown> {
143
- return typeof value === "object" && value !== null && !Array.isArray(value);
144
- }
@@ -10,31 +10,26 @@ import {
10
10
  CHANGE_TASKS_PROMPT_GUIDELINES,
11
11
  CHANGE_TASKS_PROMPT_SNIPPET,
12
12
  } from "../guidelines/internal-tools.js";
13
+ import {
14
+ asRecord,
15
+ copyKnownErrorFields,
16
+ } from "../shared/error-utils.js";
13
17
  import {
14
18
  errorToolDetails,
15
19
  okToolDetails,
16
20
  textToolResult,
17
21
  } from "../shared/tool-result.js";
22
+ import {
23
+ buildMutationCommand,
24
+ TSQ_CHANGE_ACTIONS,
25
+ type TsqChangeAction,
26
+ } from "./change-command-builder.js";
18
27
  import { runQueuedMutation } from "./mutation-queue.js";
19
28
  import { runTsqJson } from "./runner.js";
20
29
 
21
30
  export const TSQ_CHANGE_TOOL_NAME = "tsq_change";
22
31
 
23
- const TSQ_CHANGE_ACTIONS = [
24
- "create",
25
- "note",
26
- "done",
27
- "reopen",
28
- "defer",
29
- "start",
30
- "claim_assign_only",
31
- "block",
32
- "unblock",
33
- "order",
34
- "unorder",
35
- ] as const;
36
-
37
- export type TsqChangeAction = (typeof TSQ_CHANGE_ACTIONS)[number];
32
+ export type { TsqChangeAction } from "./change-command-builder.js";
38
33
 
39
34
  export const TsqChangeParamsSchema = Type.Object(
40
35
  {
@@ -116,14 +111,6 @@ export type TsqChangeDetails = ReturnType<
116
111
  typeof okToolDetails<TsqChangeSuccessData>
117
112
  >;
118
113
 
119
- type ValidationResult =
120
- | {
121
- readonly ok: true;
122
- readonly action: TsqChangeAction;
123
- readonly argv: string[];
124
- }
125
- | { readonly ok: false; readonly message: string };
126
-
127
114
  export function registerTsqChangeTool(pi: ExtensionAPI): void {
128
115
  pi.registerTool(
129
116
  defineTool({
@@ -241,294 +228,6 @@ function runMutation(
241
228
  );
242
229
  }
243
230
 
244
- function buildMutationCommand(
245
- params: Readonly<Record<string, unknown>>,
246
- ): ValidationResult {
247
- const action = params.action;
248
- if (!isTsqChangeAction(action)) {
249
- return {
250
- ok: false,
251
- message: "action must be a supported durable task mutation",
252
- };
253
- }
254
-
255
- switch (action) {
256
- case "create":
257
- return buildCreateArgv(params, action);
258
- case "note":
259
- return buildNoteArgv(params, action);
260
- case "done":
261
- return buildOptionalNoteArgv(params, action, "done");
262
- case "reopen":
263
- return buildIdOnlyArgv(params, action, "reopen");
264
- case "defer":
265
- return buildOptionalNoteArgv(params, action, "defer");
266
- case "start":
267
- return buildIdOnlyArgv(params, action, "start");
268
- case "claim_assign_only":
269
- return buildClaimAssignOnlyArgv(params, action);
270
- case "block":
271
- return buildBlockArgv(params, action, "block");
272
- case "unblock":
273
- return buildBlockArgv(params, action, "unblock");
274
- case "order":
275
- return buildOrderArgv(params, action, "order");
276
- case "unorder":
277
- return buildOrderArgv(params, action, "unorder");
278
- }
279
- }
280
-
281
- function buildCreateArgv(
282
- params: Readonly<Record<string, unknown>>,
283
- action: TsqChangeAction,
284
- ): ValidationResult {
285
- const title = requireNonEmptyString(params, "title");
286
- if (!title.ok) {
287
- return title;
288
- }
289
- const kind = requireNonEmptyString(params, "kind");
290
- if (!kind.ok) {
291
- return kind;
292
- }
293
- const priority = requireInteger(params, "priority");
294
- if (!priority.ok) {
295
- return priority;
296
- }
297
- const planned = getOptionalBoolean(params, "planned");
298
- if (!planned.ok) {
299
- return planned;
300
- }
301
- const needsPlan = getOptionalBoolean(params, "needsPlan");
302
- if (!needsPlan.ok) {
303
- return needsPlan;
304
- }
305
- if (planned.value === true && needsPlan.value === true) {
306
- return {
307
- ok: false,
308
- message: "planned and needsPlan cannot both be true",
309
- };
310
- }
311
-
312
- const argv = ["create", `--kind=${kind.value}`, "-p", String(priority.value)];
313
- const description = appendOptionalStringFlag(
314
- argv,
315
- params,
316
- "description",
317
- "--description",
318
- );
319
- if (description !== undefined) {
320
- return description;
321
- }
322
- const parent = appendOptionalStringFlag(argv, params, "parent", "--parent");
323
- if (parent !== undefined) {
324
- return parent;
325
- }
326
- if (planned.value === true) {
327
- argv.push("--planned");
328
- } else if (needsPlan.value === true) {
329
- argv.push("--needs-plan");
330
- }
331
- argv.push("--", title.value);
332
-
333
- return { ok: true, action, argv };
334
- }
335
-
336
- function buildNoteArgv(
337
- params: Readonly<Record<string, unknown>>,
338
- action: TsqChangeAction,
339
- ): ValidationResult {
340
- const id = requireNonEmptyString(params, "id");
341
- if (!id.ok) {
342
- return id;
343
- }
344
- const note = requireNonEmptyString(params, "note");
345
- if (!note.ok) {
346
- return note;
347
- }
348
- return { ok: true, action, argv: ["note", id.value, "--", note.value] };
349
- }
350
-
351
- function buildOptionalNoteArgv(
352
- params: Readonly<Record<string, unknown>>,
353
- action: TsqChangeAction,
354
- command: "done" | "defer",
355
- ): ValidationResult {
356
- const id = requireNonEmptyString(params, "id");
357
- if (!id.ok) {
358
- return id;
359
- }
360
- const argv = [command, id.value];
361
- const note = getOptionalNonEmptyString(params, "note");
362
- if (!note.ok) {
363
- return note;
364
- }
365
- if (note.value !== undefined) {
366
- argv.push(`--note=${note.value}`);
367
- }
368
- return { ok: true, action, argv };
369
- }
370
-
371
- function buildIdOnlyArgv(
372
- params: Readonly<Record<string, unknown>>,
373
- action: TsqChangeAction,
374
- command: "reopen" | "start",
375
- ): ValidationResult {
376
- const id = requireNonEmptyString(params, "id");
377
- if (!id.ok) {
378
- return id;
379
- }
380
- return { ok: true, action, argv: [command, id.value] };
381
- }
382
-
383
- function buildClaimAssignOnlyArgv(
384
- params: Readonly<Record<string, unknown>>,
385
- action: TsqChangeAction,
386
- ): ValidationResult {
387
- const id = requireNonEmptyString(params, "id");
388
- if (!id.ok) {
389
- return id;
390
- }
391
- const assignee = requireNonEmptyString(params, "assignee");
392
- if (!assignee.ok) {
393
- return assignee;
394
- }
395
- return {
396
- ok: true,
397
- action,
398
- argv: ["claim", id.value, `--assignee=${assignee.value}`],
399
- };
400
- }
401
-
402
- function buildBlockArgv(
403
- params: Readonly<Record<string, unknown>>,
404
- action: TsqChangeAction,
405
- command: "block" | "unblock",
406
- ): ValidationResult {
407
- const child = requireNonEmptyString(params, "child");
408
- if (!child.ok) {
409
- return child;
410
- }
411
- const blocker = requireNonEmptyString(params, "blocker");
412
- if (!blocker.ok) {
413
- return blocker;
414
- }
415
- if (child.value === blocker.value) {
416
- return { ok: false, message: "child and blocker cannot be the same task" };
417
- }
418
- return {
419
- ok: true,
420
- action,
421
- argv: [command, child.value, "by", blocker.value],
422
- };
423
- }
424
-
425
- function buildOrderArgv(
426
- params: Readonly<Record<string, unknown>>,
427
- action: TsqChangeAction,
428
- command: "order" | "unorder",
429
- ): ValidationResult {
430
- const later = requireNonEmptyString(params, "later");
431
- if (!later.ok) {
432
- return later;
433
- }
434
- const earlier = requireNonEmptyString(params, "earlier");
435
- if (!earlier.ok) {
436
- return earlier;
437
- }
438
- if (later.value === earlier.value) {
439
- return { ok: false, message: "later and earlier cannot be the same task" };
440
- }
441
- return {
442
- ok: true,
443
- action,
444
- argv: [command, later.value, "after", earlier.value],
445
- };
446
- }
447
-
448
- function appendOptionalStringFlag(
449
- argv: string[],
450
- params: Readonly<Record<string, unknown>>,
451
- field: "description" | "parent",
452
- flag: string,
453
- ): ValidationResult | undefined {
454
- const value = getOptionalNonEmptyString(params, field);
455
- if (!value.ok) {
456
- return value;
457
- }
458
- if (value.value !== undefined) {
459
- argv.push(`${flag}=${value.value}`);
460
- }
461
- return undefined;
462
- }
463
-
464
- function requireNonEmptyString(
465
- params: Readonly<Record<string, unknown>>,
466
- field: string,
467
- ):
468
- | { readonly ok: true; readonly value: string }
469
- | { readonly ok: false; readonly message: string } {
470
- const value = params[field];
471
- if (typeof value !== "string" || value.trim().length === 0) {
472
- return { ok: false, message: `${field} is required` };
473
- }
474
- return { ok: true, value };
475
- }
476
-
477
- function getOptionalNonEmptyString(
478
- params: Readonly<Record<string, unknown>>,
479
- field: string,
480
- ):
481
- | { readonly ok: true; readonly value: string | undefined }
482
- | { readonly ok: false; readonly message: string } {
483
- const value = params[field];
484
- if (value === undefined) {
485
- return { ok: true, value: undefined };
486
- }
487
- if (typeof value !== "string") {
488
- return { ok: false, message: `${field} must be a string` };
489
- }
490
- if (value.trim().length === 0) {
491
- return { ok: true, value: undefined };
492
- }
493
- return { ok: true, value };
494
- }
495
-
496
- function requireInteger(
497
- params: Readonly<Record<string, unknown>>,
498
- field: string,
499
- ):
500
- | { readonly ok: true; readonly value: number }
501
- | { readonly ok: false; readonly message: string } {
502
- const value = params[field];
503
- if (typeof value !== "number" || !Number.isInteger(value)) {
504
- return { ok: false, message: `${field} is required` };
505
- }
506
- return { ok: true, value };
507
- }
508
-
509
- function getOptionalBoolean(
510
- params: Readonly<Record<string, unknown>>,
511
- field: string,
512
- ):
513
- | { readonly ok: true; readonly value: boolean | undefined }
514
- | { readonly ok: false; readonly message: string } {
515
- const value = params[field];
516
- if (value === undefined) {
517
- return { ok: true, value: undefined };
518
- }
519
- if (typeof value !== "boolean") {
520
- return { ok: false, message: `${field} must be a boolean` };
521
- }
522
- return { ok: true, value };
523
- }
524
-
525
- function isTsqChangeAction(value: unknown): value is TsqChangeAction {
526
- return (
527
- typeof value === "string" &&
528
- (TSQ_CHANGE_ACTIONS as readonly string[]).includes(value)
529
- );
530
- }
531
-
532
231
  function validationErrorResult(message: string) {
533
232
  return textToolResult(
534
233
  `Error: ${message}`,
@@ -602,13 +301,6 @@ function extractTaskLike(result: unknown): {
602
301
  };
603
302
  }
604
303
 
605
- function asRecord(value: unknown): Record<string, unknown> | undefined {
606
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
607
- return undefined;
608
- }
609
- return value as Record<string, unknown>;
610
- }
611
-
612
304
  function getErrorMessage(error: unknown): string {
613
305
  if (error instanceof Error) {
614
306
  return error.message;
@@ -635,22 +327,3 @@ function serializeError(error: unknown): Record<string, unknown> {
635
327
  }
636
328
  return { value: String(error) };
637
329
  }
638
-
639
- function copyKnownErrorFields(error: Error): Record<string, unknown> {
640
- const record = error as unknown as Record<string, unknown>;
641
- const output: Record<string, unknown> = {};
642
- for (const key of [
643
- "code",
644
- "command",
645
- "details",
646
- "stderr",
647
- "stdout",
648
- "killed",
649
- "args",
650
- ] as const) {
651
- if (record[key] !== undefined) {
652
- output[key] = record[key];
653
- }
654
- }
655
- return output;
656
- }
@@ -15,6 +15,11 @@ import {
15
15
  } from "../session-todos/state/state-reducer.js";
16
16
  import { commitState, getState } from "../session-todos/state/store.js";
17
17
  import type { Task } from "../session-todos/tool/types.js";
18
+ import {
19
+ asRecord,
20
+ copyKnownErrorFields,
21
+ isRecord,
22
+ } from "../shared/error-utils.js";
18
23
  import {
19
24
  errorToolDetails,
20
25
  okToolDetails,
@@ -391,33 +396,3 @@ function serializeError(error: unknown): Record<string, unknown> {
391
396
  }
392
397
  return { value: String(error) };
393
398
  }
394
-
395
- function copyKnownErrorFields(error: Error): Record<string, unknown> {
396
- const record = error as unknown as Record<string, unknown>;
397
- const output: Record<string, unknown> = {};
398
- for (const key of [
399
- "code",
400
- "command",
401
- "details",
402
- "stderr",
403
- "stdout",
404
- "killed",
405
- "args",
406
- ] as const) {
407
- if (record[key] !== undefined) {
408
- output[key] = record[key];
409
- }
410
- }
411
- return output;
412
- }
413
-
414
- function asRecord(value: unknown): Record<string, unknown> | undefined {
415
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
416
- return undefined;
417
- }
418
- return value as Record<string, unknown>;
419
- }
420
-
421
- function isRecord(value: unknown): value is Record<string, unknown> {
422
- return asRecord(value) !== undefined;
423
- }
@@ -0,0 +1,95 @@
1
+ import type {
2
+ AgentToolResult,
3
+ ExtensionAPI,
4
+ ExtensionContext,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import { errorToolDetails, textToolResult } from "../shared/tool-result.js";
7
+ import {
8
+ collectHandoffStatus,
9
+ type HandoffCheckResult,
10
+ } from "./handoff-guard.js";
11
+
12
+ export async function executeHandoffCheck(
13
+ pi: ExtensionAPI,
14
+ signal: AbortSignal | undefined,
15
+ ctx: ExtensionContext,
16
+ ): Promise<AgentToolResult<unknown>> {
17
+ const result = await collectHandoffStatus({
18
+ pi,
19
+ cwd: ctx.cwd,
20
+ ...(signal != null ? { signal } : {}),
21
+ });
22
+
23
+ if (!result.ok) {
24
+ return textToolResult(
25
+ `Error: ${result.message}`,
26
+ errorToolDetails({ code: result.code, message: result.message }),
27
+ );
28
+ }
29
+
30
+ return textToolResult(formatHandoffText(result), {
31
+ ok: true,
32
+ ready: result.ready,
33
+ ...formatHandoffDetails(result),
34
+ });
35
+ }
36
+
37
+ function formatHandoffText(result: HandoffCheckResult & { ok: true }): string {
38
+ const lines: string[] = [
39
+ result.ready
40
+ ? "Handoff ready: all session todos complete and linked tasks resolved."
41
+ : "Handoff not ready.",
42
+ ];
43
+
44
+ if ("todoBlockers" in result && result.todoBlockers?.length) {
45
+ lines.push("", "Todo blockers:");
46
+ for (const b of result.todoBlockers) {
47
+ lines.push(`- #${b.todoId} "${b.subject}" — ${b.reason}`);
48
+ }
49
+ }
50
+
51
+ if ("linkedBlockers" in result && result.linkedBlockers?.length) {
52
+ lines.push("", "Linked task blockers:");
53
+ for (const b of result.linkedBlockers) {
54
+ lines.push(`- ${b.tsqId} (todo #${b.todoId}) — ${b.status}`);
55
+ }
56
+ }
57
+
58
+ if ("linkedWarnings" in result && result.linkedWarnings?.length) {
59
+ lines.push("", "Warnings:");
60
+ for (const w of result.linkedWarnings) {
61
+ lines.push(`- ${w.tsqId} (todo #${w.todoId}) — ${w.status}`);
62
+ }
63
+ }
64
+
65
+ if ("readErrors" in result && result.readErrors?.length) {
66
+ lines.push("", "Read errors:");
67
+ for (const e of result.readErrors) {
68
+ lines.push(`- ${e.tsqId} — ${e.code}: ${e.message}`);
69
+ }
70
+ }
71
+
72
+ return lines.join("\n");
73
+ }
74
+
75
+ function formatHandoffDetails(
76
+ result: HandoffCheckResult & { ok: true },
77
+ ): Record<string, unknown> {
78
+ const details: Record<string, unknown> = {};
79
+ if (result.projectRoot !== undefined) {
80
+ details.projectRoot = result.projectRoot;
81
+ }
82
+ if ("todoBlockers" in result && result.todoBlockers?.length) {
83
+ details.todoBlockers = result.todoBlockers;
84
+ }
85
+ if ("linkedBlockers" in result && result.linkedBlockers?.length) {
86
+ details.linkedBlockers = result.linkedBlockers;
87
+ }
88
+ if ("linkedWarnings" in result && result.linkedWarnings?.length) {
89
+ details.linkedWarnings = result.linkedWarnings;
90
+ }
91
+ if ("readErrors" in result && result.readErrors?.length) {
92
+ details.readErrors = result.readErrors;
93
+ }
94
+ return details;
95
+ }
@@ -9,6 +9,7 @@ import {
9
9
  READ_TASKS_PROMPT_GUIDELINES,
10
10
  READ_TASKS_PROMPT_SNIPPET,
11
11
  } from "../guidelines/internal-tools.js";
12
+ import { isRecord } from "../shared/error-utils.js";
12
13
  import { truncatedTextToolResult } from "../shared/tool-result.js";
13
14
  import type { TruncatedText } from "../shared/truncation.js";
14
15
  import { runTsqJson } from "./runner.js";
@@ -522,7 +523,3 @@ function isTaskTreeNode(value: unknown): value is TsqTaskTreeNode {
522
523
  isRecord(value) && isTsqTask(value.task) && Array.isArray(value.children)
523
524
  );
524
525
  }
525
-
526
- function isRecord(value: unknown): value is Record<string, unknown> {
527
- return typeof value === "object" && value !== null && !Array.isArray(value);
528
- }
@@ -17,6 +17,7 @@ import type {
17
17
  import type { CreateTreeNode, CreateTreeResult } from "./bulk-contract.js";
18
18
  import { runQueuedMutation } from "./mutation-queue.js";
19
19
  import { runTsqJson } from "./runner.js";
20
+ import { asRecord } from "../shared/error-utils.js";
20
21
  import {
21
22
  okToolDetails,
22
23
  textToolResult,
@@ -218,10 +219,3 @@ function formatResultText(result: CreateTreeResult): string {
218
219
  function getErrorMessage(error: unknown): string {
219
220
  return error instanceof Error ? error.message : String(error);
220
221
  }
221
-
222
- function asRecord(value: unknown): Record<string, unknown> | undefined {
223
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
224
- return undefined;
225
- }
226
- return value as Record<string, unknown>;
227
- }