poe-code 3.0.283 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "3.0.283",
3
+ "version": "3.0.284",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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}".`);