poe-code 3.0.268 → 3.0.269

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.268",
3
+ "version": "3.0.269",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -2,6 +2,7 @@ import { lstat, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from
2
2
  import { homedir } from "node:os";
3
3
  import { createConfigStore, defineScope, resolveConfigPath, resolveProjectConfigPath } from "@poe-code/poe-code-config";
4
4
  import { codeReviewConfigScope, parseCodeReviewConfigDocument, parseCodeReviewProfileDirectories } from "./config-scope.js";
5
+ import { requireSafeDocumentSegment } from "./document-schemas.js";
5
6
  import { hasOwnErrorCode } from "./error-codes.js";
6
7
  import { resolveCodeReviewStoreDirectory } from "./review-store.js";
7
8
  const poeCoreAgentScope = defineScope("core", {
@@ -111,7 +112,7 @@ export async function resolveCodeReviewRunOptions(input, configOptions) {
111
112
  profileDirectories: parseCodeReviewProfileDirectories(inputProfileDirectories ?? configProfileDirectories),
112
113
  ...(inputProfilePath === undefined ? {} : { profilePath: inputProfilePath }),
113
114
  ...(inputPromptPath === undefined ? {} : { promptPath: inputPromptPath }),
114
- ...(inputProfiles === undefined ? {} : { profiles: inputProfiles }),
115
+ ...(inputProfiles === undefined ? {} : { profiles: requireProfileFilters(inputProfiles) }),
115
116
  ...(inputAdditionalFeedback === undefined
116
117
  ? {}
117
118
  : { additionalFeedback: inputAdditionalFeedback })
@@ -165,6 +166,19 @@ function requireNonEmptyString(value, field) {
165
166
  }
166
167
  return normalized;
167
168
  }
169
+ function requireProfileFilters(value) {
170
+ if (!Array.isArray(value)) {
171
+ throw new Error("profiles must be an array of safe profile names.");
172
+ }
173
+ return value.map((profile) => {
174
+ try {
175
+ return requireSafeDocumentSegment(profile, "profiles");
176
+ }
177
+ catch {
178
+ throw new Error("profiles must be an array of safe profile names.");
179
+ }
180
+ });
181
+ }
168
182
  function isMissingFileError(error) {
169
183
  return hasOwnErrorCode(error, "ENOENT");
170
184
  }
@@ -8,7 +8,7 @@ const CODE_REVIEW_PROMPT_ROLES = [
8
8
  ];
9
9
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/;
10
10
  const SAFE_SEGMENT_RE = /^[A-Za-z0-9._-]+$/;
11
- const SAFE_GITHUB_ACTOR_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
11
+ const SAFE_GITHUB_ACTOR_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
12
12
  export function requireSafeDocumentSegment(value, field) {
13
13
  if (typeof value !== "string" ||
14
14
  value.trim() !== value ||
@@ -179,9 +179,6 @@ export function serializeCodeReviewIngestSource(source, filePath = "code-review
179
179
  function parseOptionalFrontmatter(content, filePath) {
180
180
  const match = content.match(FRONTMATTER_RE);
181
181
  if (!match) {
182
- if (/^---\r?\n/.test(content)) {
183
- throw new Error(`${filePath}: frontmatter is missing a closing --- delimiter.`);
184
- }
185
182
  return { body: content };
186
183
  }
187
184
  try {
@@ -11,11 +11,17 @@ import { buildCodeReviewReviewerPrompt } from "./prompt-builders.js";
11
11
  export const CODE_REVIEW_AGENT_MCP_ROLES = ["agent", "orchestrator", "subagent"];
12
12
  const inlineCommentSchema = S.Object({
13
13
  path: S.String({ description: "Repository-relative path in the PR diff." }),
14
- line: S.Number({ description: "Right-side line number in the PR diff." }),
14
+ line: S.Number({
15
+ description: "Right-side line number in the PR diff.",
16
+ jsonType: "integer",
17
+ minimum: 1
18
+ }),
15
19
  body: S.String({ description: "Inline review comment body." })
16
20
  });
17
21
  const inlineCommentIndexSchema = S.Number({
18
- description: "Zero-based merged review inline comment index."
22
+ description: "Zero-based merged review inline comment index.",
23
+ jsonType: "integer",
24
+ minimum: 0
19
25
  });
20
26
  const prParam = S.String({ description: "GitHub pull request URL." });
21
27
  export function parseCodeReviewAgentMcpArgs(argv) {
@@ -140,7 +146,9 @@ export function createCodeReviewAgentMcpGroup(context, dependencies = {}) {
140
146
  handler: async ({ params }) => {
141
147
  const pr = canonicalPullRequestUrl(params.pr);
142
148
  const draft = validateDraft(params);
143
- const currentState = await ensureState(store, context, pr);
149
+ const currentState = context.role === "orchestrator"
150
+ ? await ensureState(store, context, pr)
151
+ : await requireState(store, context, pr);
144
152
  if (context.role === "orchestrator") {
145
153
  const unfinishedProfiles = Object.values(currentState.subagents)
146
154
  .filter(({ status }) => status === "pending" || status === "running")
@@ -342,7 +350,11 @@ export function createCodeReviewAgentMcpGroup(context, dependencies = {}) {
342
350
  pr: prParam,
343
351
  index: inlineCommentIndexSchema,
344
352
  path: S.String({ description: "Repository-relative path in the PR diff." }),
345
- line: S.Number({ description: "Right-side line number in the PR diff." }),
353
+ line: S.Number({
354
+ description: "Right-side line number in the PR diff.",
355
+ jsonType: "integer",
356
+ minimum: 1
357
+ }),
346
358
  body: S.String({ description: "Inline review comment body." })
347
359
  }),
348
360
  handler: async ({ params }) => {
@@ -436,8 +436,8 @@ async function withFileLock(lockPath, lockTimeoutMs, operation) {
436
436
  }
437
437
  async function isStaleLock(lockPath, lockTimeoutMs) {
438
438
  try {
439
- const ownerPid = Number.parseInt((await readFile(lockPath, "utf8")).trim(), 10);
440
- if (Number.isSafeInteger(ownerPid) && ownerPid > 0) {
439
+ const ownerPid = parseLockOwnerPid((await readFile(lockPath, "utf8")).trim());
440
+ if (ownerPid !== undefined) {
441
441
  return !isRunningProcess(ownerPid);
442
442
  }
443
443
  return Date.now() - (await stat(lockPath)).mtimeMs >= lockTimeoutMs;
@@ -449,6 +449,13 @@ async function isStaleLock(lockPath, lockTimeoutMs) {
449
449
  throw error;
450
450
  }
451
451
  }
452
+ function parseLockOwnerPid(value) {
453
+ const processId = Number(value);
454
+ if (!Number.isSafeInteger(processId) || processId <= 0) {
455
+ return undefined;
456
+ }
457
+ return String(processId) === value ? processId : undefined;
458
+ }
452
459
  function isRunningProcess(processId) {
453
460
  try {
454
461
  process.kill(processId, 0);