poe-code 3.0.282 → 3.0.284

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.
@@ -588,6 +588,20 @@ function parseArrayValue(value, schema, label) {
588
588
  }
589
589
  return splitArrayInput(value).map((item) => parseScalarValue(item, itemSchema, label));
590
590
  }
591
+ function isNegativeNumericArrayToken(token, schema) {
592
+ if (!token.startsWith("-") || token.startsWith("--")) {
593
+ return false;
594
+ }
595
+ const itemSchema = unwrapOptional(schema.item);
596
+ if (itemSchema.kind !== "number") {
597
+ return false;
598
+ }
599
+ const items = splitArrayInput(token);
600
+ return (items.length > 0 && items.every((item) => isValidNumberSchemaValue(Number(item), itemSchema)));
601
+ }
602
+ function isNextArrayOptionToken(token, schema) {
603
+ return token.startsWith("-") && !isNegativeNumericArrayToken(token, schema);
604
+ }
591
605
  function validateArrayBounds(value, schema, label) {
592
606
  if (schema.minItems !== undefined && value.length < schema.minItems) {
593
607
  throw new UserError(`Invalid value for "${label}". Expected an array with at least ${schema.minItems} items, got array(${value.length}).`);
@@ -2044,7 +2058,7 @@ function consumeFieldValue(args, index, schema, label, inlineValue) {
2044
2058
  let cursor = index + 1;
2045
2059
  while (cursor < args.length) {
2046
2060
  const token = args[cursor] ?? "";
2047
- if (token.startsWith("-")) {
2061
+ if (isNextArrayOptionToken(token, schema)) {
2048
2062
  break;
2049
2063
  }
2050
2064
  const parsed = parseArrayValue(token, schema, label);
@@ -2817,6 +2831,12 @@ function handleRunError(error, options) {
2817
2831
  }));
2818
2832
  return;
2819
2833
  }
2834
+ logger.error(appendUsagePointer(formatCommanderErrorMessage(error), {
2835
+ rootUsageName: options.rootUsageName,
2836
+ commandPath: options.commandPath.length > 0
2837
+ ? options.commandPath
2838
+ : findCurrentCommanderCommandPath(options.program, options.argv ?? process.argv)
2839
+ }));
2820
2840
  return;
2821
2841
  }
2822
2842
  if (isHttpErrorLike(error)) {
@@ -2831,6 +2851,9 @@ function handleRunError(error, options) {
2831
2851
  }
2832
2852
  process.exitCode = 1;
2833
2853
  }
2854
+ function formatCommanderErrorMessage(error) {
2855
+ return error.message.startsWith("error:") ? error.message : `error: ${error.message}`;
2856
+ }
2834
2857
  function formatInvalidEnumMessage(label, value, values, opts = {}) {
2835
2858
  const suggestions = suggest(value, opts.candidates ?? values.map((candidate) => String(candidate)), opts);
2836
2859
  const suggestionLine = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?\n` : " ";
@@ -1,10 +1,14 @@
1
1
  import { Budget, lint, run } from "@poe-code/safejs/core";
2
- import { defineCommand } from "toolcraft";
2
+ import { defineCommand, UserError } from "toolcraft";
3
3
  import { S } from "toolcraft-schema";
4
4
  import { getOwnErrorCode } from "./error-codes.js";
5
5
  import { buildHostModules } from "./host-modules.js";
6
6
  const executeParams = S.Object({
7
- source: S.String({ description: "SafeJS source to execute." })
7
+ source: S.String({
8
+ description: "SafeJS source to execute.",
9
+ minLength: 1,
10
+ pattern: "\\S"
11
+ })
8
12
  });
9
13
  function createBudget(options) {
10
14
  return new Budget({
@@ -90,6 +94,9 @@ export function makeExecuteCommand({ root, sdk, entries, budget, scope = ["mcp",
90
94
  scope,
91
95
  params: executeParams,
92
96
  handler: async ({ params }) => {
97
+ if (params.source.trim().length === 0) {
98
+ throw new UserError("source must not be empty or whitespace");
99
+ }
93
100
  const { lintModules, modules } = await buildHostModules(root, sdk, entries);
94
101
  let diagnostics;
95
102
  try {
@@ -16,6 +16,9 @@ function resolveSdkMember(sdk, path) {
16
16
  if (typeof current !== "object" && typeof current !== "function") {
17
17
  return undefined;
18
18
  }
19
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) {
20
+ return undefined;
21
+ }
19
22
  current = current[segment];
20
23
  }
21
24
  return current;
@@ -4,18 +4,21 @@ import { resolveCommandEntries } from "./tree.js";
4
4
  const K1 = 1.5;
5
5
  const B = 0.75;
6
6
  const FALLBACK_LIMIT = 10;
7
+ const SEARCH_DETAILS = ["brief", "detailed", "full"];
7
8
  const searchParams = S.Object({
8
9
  query: S.String({ description: "Search query." }),
9
- limit: S.Optional(S.Number({ description: "Maximum result count." })),
10
- detail: S.Optional(S.Enum(["brief", "detailed", "full"], {
10
+ limit: S.Optional(S.Number({
11
+ description: "Maximum result count.",
12
+ jsonType: "integer",
13
+ minimum: 0
14
+ })),
15
+ detail: S.Optional(S.Enum(SEARCH_DETAILS, {
11
16
  description: "Result detail level."
12
17
  }))
13
18
  });
14
19
  function isWordCharacter(character) {
15
20
  const code = character.charCodeAt(0);
16
- return ((code >= 97 && code <= 122) ||
17
- (code >= 48 && code <= 57) ||
18
- character === "_");
21
+ return (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || character === "_";
19
22
  }
20
23
  function tokenize(value) {
21
24
  const tokens = [];
@@ -89,6 +92,12 @@ function normalizeLimit(limit) {
89
92
  }
90
93
  return limit;
91
94
  }
95
+ function normalizeDetail(detail) {
96
+ if (SEARCH_DETAILS.includes(detail)) {
97
+ return detail;
98
+ }
99
+ throw new UserError(`detail must be one of: ${SEARCH_DETAILS.join(", ")}, received ${JSON.stringify(detail)}`);
100
+ }
92
101
  function toSearchResult(entry, detail) {
93
102
  const result = {
94
103
  path: entry.path,
@@ -115,7 +124,7 @@ export function makeSearchCommand({ entries, defaults = {}, scope = ["mcp", "sdk
115
124
  if (limit === 0) {
116
125
  return [];
117
126
  }
118
- const detail = params.detail ?? defaults.detail ?? "brief";
127
+ const detail = normalizeDetail(params.detail ?? defaults.detail ?? "brief");
119
128
  const indexedEntries = resolvedEntries.map(indexEntry);
120
129
  const frequencies = documentFrequencies(indexedEntries);
121
130
  const averageLength = averageDocumentLength(indexedEntries);
@@ -22,7 +22,9 @@ function splitWords(value) {
22
22
  const isUppercase = character !== lower && character === upper;
23
23
  const previous = value[index - 1];
24
24
  const next = value[index + 1];
25
- const previousIsLowercase = previous !== undefined && previous === previous.toLowerCase() && previous !== previous.toUpperCase();
25
+ const previousIsLowercase = previous !== undefined &&
26
+ previous === previous.toLowerCase() &&
27
+ previous !== previous.toUpperCase();
26
28
  const nextIsLowercase = next !== undefined && next === next.toLowerCase() && next !== next.toUpperCase();
27
29
  if (isUppercase && current.length > 0 && (previousIsLowercase || nextIsLowercase)) {
28
30
  words.push(current.toLowerCase());
@@ -44,6 +46,13 @@ export function formatSdkSegment(segment) {
44
46
  export function formatModuleSegment(segment) {
45
47
  return splitWords(segment).join("_");
46
48
  }
49
+ function formatNamedModuleSegment(segment, kind) {
50
+ const formatted = formatModuleSegment(segment);
51
+ if (formatted.length === 0) {
52
+ throw new Error(`Codemode ${kind} name "${segment}" must include at least one non-separator character.`);
53
+ }
54
+ return formatted;
55
+ }
47
56
  function commandIsExecutable(command) {
48
57
  return command.scope.includes("sdk");
49
58
  }
@@ -61,7 +70,9 @@ export async function resolveCommandTree(root, options = {}) {
61
70
  const exportsByGroupPath = new Map();
62
71
  const paths = new Set();
63
72
  function visit(group, groupSegments) {
64
- const groupPath = groupSegments.map(formatModuleSegment).join(".");
73
+ const groupPath = groupSegments
74
+ .map((segment) => formatNamedModuleSegment(segment, "group"))
75
+ .join(".");
65
76
  for (const child of group.children) {
66
77
  if (child.kind === "group") {
67
78
  visit(child, [...groupSegments, child.name]);
@@ -70,7 +81,7 @@ export async function resolveCommandTree(root, options = {}) {
70
81
  if (!commandIsExecutable(child)) {
71
82
  continue;
72
83
  }
73
- const name = formatModuleSegment(child.name);
84
+ const name = formatNamedModuleSegment(child.name, "command");
74
85
  const path = groupPath.length === 0 ? name : `${groupPath}.${name}`;
75
86
  if (paths.has(path)) {
76
87
  throw new Error(`Duplicate codemode command path "${path}".`);