poe-code 3.0.268 → 3.0.270

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.270",
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);
@@ -1,10 +1,13 @@
1
- import type { SessionUpdate, ToolKind } from "../../../poe-acp-client/dist/index.js";
2
- import type { AcpEvent } from "./types.js";
1
+ import type { SessionUpdate as AcpClientSessionUpdate, ToolKind as AcpClientToolKind } from "../../../poe-acp-client/dist/index.js";
2
+ import type { AcpEvent, SessionUpdate as LegacySessionUpdate, ToolKind as LegacyToolKind } from "./types.js";
3
+ type ConvertibleSessionUpdate = AcpClientSessionUpdate | LegacySessionUpdate;
4
+ type ConvertibleToolKind = AcpClientToolKind | LegacyToolKind;
3
5
  export interface ToolRenderState {
4
6
  startedToolCalls: Set<string>;
5
7
  toolCallKinds: Map<string, string>;
6
8
  toolCallTitles: Map<string, string>;
7
9
  }
8
10
  export declare function createToolRenderState(): ToolRenderState;
9
- export declare function toRenderKind(kind: ToolKind | undefined | null): string;
10
- export declare function sessionUpdateToEvents(update: SessionUpdate, state: ToolRenderState): AcpEvent[];
11
+ export declare function toRenderKind(kind: ConvertibleToolKind | undefined | null): string;
12
+ export declare function sessionUpdateToEvents(update: ConvertibleSessionUpdate, state: ToolRenderState): AcpEvent[];
13
+ export {};
@@ -2,13 +2,13 @@ export function createToolRenderState() {
2
2
  return {
3
3
  startedToolCalls: new Set(),
4
4
  toolCallKinds: new Map(),
5
- toolCallTitles: new Map(),
5
+ toolCallTitles: new Map()
6
6
  };
7
7
  }
8
8
  export function toRenderKind(kind) {
9
9
  if (kind === "execute")
10
10
  return "exec";
11
- if (kind === "write")
11
+ if (kind === "write" || kind === "edit")
12
12
  return "edit";
13
13
  if (kind === "read")
14
14
  return "read";
@@ -60,7 +60,7 @@ export function sessionUpdateToEvents(update, state) {
60
60
  const usage = {
61
61
  event: "usage",
62
62
  inputTokens,
63
- outputTokens,
63
+ outputTokens
64
64
  };
65
65
  if (cachedTokens > 0) {
66
66
  usage.cachedTokens = cachedTokens;
@@ -79,31 +79,33 @@ export function sessionUpdateToEvents(update, state) {
79
79
  return [];
80
80
  }
81
81
  state.startedToolCalls.add(update.toolCallId);
82
- return [{
82
+ return [
83
+ {
83
84
  event: "tool_start",
84
85
  kind: renderKind,
85
86
  title,
86
- id: update.toolCallId,
87
- }];
87
+ id: update.toolCallId
88
+ }
89
+ ];
88
90
  }
89
91
  if (update.sessionUpdate === "tool_call_update") {
90
- const renderKind = toRenderKind(update.kind ?? undefined)
91
- || state.toolCallKinds.get(update.toolCallId)
92
- || "other";
92
+ const renderKind = (update.kind == null ? undefined : toRenderKind(update.kind)) ||
93
+ state.toolCallKinds.get(update.toolCallId) ||
94
+ "other";
93
95
  state.toolCallKinds.set(update.toolCallId, renderKind);
94
96
  const events = [];
95
97
  const toolTitle = toToolTitle(state.toolCallTitles.get(update.toolCallId) ?? update.toolCallId, update.locations);
96
98
  state.toolCallTitles.set(update.toolCallId, toolTitle);
97
99
  const status = update.status;
98
- const shouldStart = !state.startedToolCalls.has(update.toolCallId)
99
- && (status === "pending" || status === "in_progress");
100
+ const shouldStart = !state.startedToolCalls.has(update.toolCallId) &&
101
+ (status === "pending" || status === "in_progress");
100
102
  if (shouldStart) {
101
103
  state.startedToolCalls.add(update.toolCallId);
102
104
  events.push({
103
105
  event: "tool_start",
104
106
  kind: renderKind,
105
107
  title: toolTitle,
106
- id: update.toolCallId,
108
+ id: update.toolCallId
107
109
  });
108
110
  }
109
111
  if (status === "completed" || status === "failed" || status === "cancelled") {
@@ -113,14 +115,14 @@ export function sessionUpdateToEvents(update, state) {
113
115
  event: "tool_start",
114
116
  kind: renderKind,
115
117
  title: toolTitle,
116
- id: update.toolCallId,
118
+ id: update.toolCallId
117
119
  });
118
120
  }
119
121
  events.push({
120
122
  event: "tool_complete",
121
123
  kind: renderKind,
122
124
  path: extractToolOutputText(update),
123
- id: update.toolCallId,
125
+ id: update.toolCallId
124
126
  });
125
127
  }
126
128
  return events;
@@ -18,7 +18,7 @@
18
18
  */
19
19
  export type ToolKind = "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
20
20
  /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
21
- export type ToolCallStatus = "pending" | "in_progress" | "completed" | "failed";
21
+ export type ToolCallStatus = "pending" | "in_progress" | "completed" | "failed" | "cancelled";
22
22
  /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
23
23
  export interface ContentChunk {
24
24
  type: "text";
@@ -39,9 +39,12 @@ export interface ToolCall {
39
39
  sessionUpdate: "tool_call";
40
40
  toolCallId: string;
41
41
  title: string;
42
+ content?: ToolCallContent[];
42
43
  kind?: ToolKind;
44
+ locations?: ToolCallLocation[];
43
45
  status?: ToolCallStatus;
44
46
  rawInput?: unknown;
47
+ rawOutput?: unknown;
45
48
  _meta?: Record<string, unknown>;
46
49
  }
47
50
  /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
@@ -54,13 +57,22 @@ export type ToolCallContent = {
54
57
  data: string;
55
58
  };
56
59
  /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
60
+ export interface ToolCallLocation {
61
+ path: string;
62
+ lineNumber?: number | null;
63
+ _meta?: Record<string, unknown>;
64
+ }
65
+ /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
57
66
  export interface ToolCallUpdate {
58
67
  sessionUpdate: "tool_call_update";
59
68
  toolCallId: string;
60
69
  kind?: ToolKind;
61
- status?: ToolCallStatus;
70
+ status?: ToolCallStatus | null;
62
71
  rawOutput?: unknown;
63
- content?: ToolCallContent[];
72
+ rawInput?: unknown;
73
+ content?: ToolCallContent[] | null;
74
+ locations?: ToolCallLocation[] | null;
75
+ title?: string | null;
64
76
  _meta?: Record<string, unknown>;
65
77
  }
66
78
  /** ACP-compatible type - @see https://agentclientprotocol.com/ - no package dependency, structural compatibility only */
@@ -17,6 +17,8 @@ export { spawnInteractive } from "./spawn-interactive.js";
17
17
  export { spawnAutonomous } from "./autonomous.js";
18
18
  export type { AutonomousOptions, StreamingSpawnFn, StreamingSpawnReturn } from "./autonomous.js";
19
19
  export { renderAcpEvent, renderAcpStream, renderSessionUpdateStream } from "./acp/renderer.js";
20
+ export { createToolRenderState, sessionUpdateToEvents } from "./acp/session-update-converter.js";
21
+ export type { ToolRenderState } from "./acp/session-update-converter.js";
20
22
  export type { LogEntry, MalformedSpawnLogRecord, ReadSpawnLogOptions } from "./acp/replay.js";
21
23
  export { findLatestLog, listSpawnLogs, pickRandomLog, readSpawnLog, replaySpawnLog } from "./acp/replay.js";
22
24
  export type { SpawnStreamingOptions, SpawnStreamingResult } from "./acp/spawn.js";
@@ -14,6 +14,7 @@ export { createSpawnParallel, SpawnParallelError } from "./parallel.js";
14
14
  export { spawnInteractive } from "./spawn-interactive.js";
15
15
  export { spawnAutonomous } from "./autonomous.js";
16
16
  export { renderAcpEvent, renderAcpStream, renderSessionUpdateStream } from "./acp/renderer.js";
17
+ export { createToolRenderState, sessionUpdateToEvents } from "./acp/session-update-converter.js";
17
18
  export { findLatestLog, listSpawnLogs, pickRandomLog, readSpawnLog, replaySpawnLog } from "./acp/replay.js";
18
19
  export { spawnStreaming } from "./acp/spawn.js";
19
20
  export { spawnAcp } from "./acp/spawn-acp.js";