poe-code 3.0.311 → 3.0.313

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/dist/index.js CHANGED
@@ -3473,21 +3473,21 @@ function sliceOffsetMap(offsets, start, end) {
3473
3473
  }
3474
3474
  function createOffsetMap(input, absoluteStart = 0) {
3475
3475
  const offsets = new Array(input.length + 1).fill(absoluteStart);
3476
- let byteOffset = absoluteStart;
3476
+ let byteOffset2 = absoluteStart;
3477
3477
  let index = 0;
3478
3478
  while (index < input.length) {
3479
- offsets[index] = byteOffset;
3479
+ offsets[index] = byteOffset2;
3480
3480
  const codePoint = input.codePointAt(index) ?? 0;
3481
3481
  const codeUnitLength = codePoint > 65535 ? 2 : 1;
3482
3482
  const byteLength = codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
3483
3483
  for (let offsetIndex = 1; offsetIndex < codeUnitLength; offsetIndex += 1) {
3484
- offsets[index + offsetIndex] = byteOffset;
3484
+ offsets[index + offsetIndex] = byteOffset2;
3485
3485
  }
3486
- byteOffset += byteLength;
3486
+ byteOffset2 += byteLength;
3487
3487
  index += codeUnitLength;
3488
- offsets[index] = byteOffset;
3488
+ offsets[index] = byteOffset2;
3489
3489
  }
3490
- offsets[input.length] = byteOffset;
3490
+ offsets[input.length] = byteOffset2;
3491
3491
  return offsets;
3492
3492
  }
3493
3493
  var INLINE_HTML_TAGS;
@@ -4102,21 +4102,21 @@ function getTextNodeSourceOffsets(node) {
4102
4102
  }
4103
4103
  function createOffsetMap2(input, absoluteStart = 0, absoluteEnd) {
4104
4104
  const offsets = new Array(input.length + 1).fill(absoluteStart);
4105
- let byteOffset = absoluteStart;
4105
+ let byteOffset2 = absoluteStart;
4106
4106
  let index = 0;
4107
4107
  while (index < input.length) {
4108
- offsets[index] = byteOffset;
4108
+ offsets[index] = byteOffset2;
4109
4109
  const codePoint = input.codePointAt(index) ?? 0;
4110
4110
  const codeUnitLength = codePoint > 65535 ? 2 : 1;
4111
4111
  const byteLength = codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
4112
4112
  for (let offsetIndex = 1; offsetIndex < codeUnitLength; offsetIndex += 1) {
4113
- offsets[index + offsetIndex] = byteOffset;
4113
+ offsets[index + offsetIndex] = byteOffset2;
4114
4114
  }
4115
- byteOffset += byteLength;
4115
+ byteOffset2 += byteLength;
4116
4116
  index += codeUnitLength;
4117
- offsets[index] = byteOffset;
4117
+ offsets[index] = byteOffset2;
4118
4118
  }
4119
- offsets[input.length] = absoluteEnd ?? byteOffset;
4119
+ offsets[input.length] = absoluteEnd ?? byteOffset2;
4120
4120
  return offsets;
4121
4121
  }
4122
4122
  function createMappedText(value, offsets) {
@@ -16932,6 +16932,9 @@ async function configuredTimeout(options) {
16932
16932
  async function cacheEnabled(options) {
16933
16933
  return (await resolveMemoryConfig(options)).cacheEnabled;
16934
16934
  }
16935
+ async function mcpWritesAllowed(options) {
16936
+ return (await resolveMemoryConfig(options)).mcpWritesAllowed;
16937
+ }
16935
16938
  async function defaultQueryBudget(options) {
16936
16939
  return (await resolveMemoryConfig(options)).defaultQueryBudget;
16937
16940
  }
@@ -21298,9 +21301,9 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
21298
21301
  const sinceCondition = opts?.since === void 0 ? "" : ` && test $(stat -c %Y ${logFile} 2>/dev/null || stat -f %m ${logFile}) -ge ${Math.ceil(
21299
21302
  opts.since.getTime() / 1e3
21300
21303
  )}`;
21301
- let byteOffset = opts?.sinceByte ?? 0;
21304
+ let byteOffset2 = opts?.sinceByte ?? 0;
21302
21305
  let pendingBytes = Buffer.alloc(0);
21303
- let pendingByteOffset = byteOffset;
21306
+ let pendingByteOffset = byteOffset2;
21304
21307
  while (true) {
21305
21308
  const stdout = await runAndReadBytes(runner, {
21306
21309
  command: engine,
@@ -21310,7 +21313,7 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
21310
21313
  containerId,
21311
21314
  "sh",
21312
21315
  "-c",
21313
- `test -f ${logFile}${sinceCondition} && tail -c +${byteOffset + 1} ${logFile} || true`
21316
+ `test -f ${logFile}${sinceCondition} && tail -c +${byteOffset2 + 1} ${logFile} || true`
21314
21317
  ],
21315
21318
  stdout: "pipe",
21316
21319
  stderr: "pipe"
@@ -21318,7 +21321,7 @@ function createContainerJob(containerId, runner, engine, context, detachedJobCon
21318
21321
  if (stdout.byteLength > 0) {
21319
21322
  const combined = pendingBytes.byteLength === 0 ? stdout : Buffer.concat([pendingBytes, stdout]);
21320
21323
  const completeLength = completeUtf8PrefixLength(combined);
21321
- byteOffset += stdout.byteLength;
21324
+ byteOffset2 += stdout.byteLength;
21322
21325
  pendingBytes = combined.subarray(completeLength);
21323
21326
  const data = combined.subarray(0, completeLength).toString("utf8");
21324
21327
  if (data.length > 0) {
@@ -26084,19 +26087,19 @@ async function* streamLogFile(env, jobId, opts) {
26084
26087
  assertSafeJobId2(jobId);
26085
26088
  const fs28 = env.fs ?? nodeFs3;
26086
26089
  const file = jobLogPath(jobId);
26087
- let byteOffset = opts.sinceByte ?? (opts.since === void 0 ? 0 : await readCurrentByteLength(fs28, file));
26090
+ let byteOffset2 = opts.sinceByte ?? (opts.since === void 0 ? 0 : await readCurrentByteLength(fs28, file));
26088
26091
  let pendingBytes = Buffer.alloc(0);
26089
- let pendingByteOffset = byteOffset;
26092
+ let pendingByteOffset = byteOffset2;
26090
26093
  while (true) {
26091
26094
  if (opts.since !== void 0 && !await wasModifiedSince(fs28, file, opts.since)) {
26092
26095
  await waitForLogChange(fs28, file);
26093
26096
  continue;
26094
26097
  }
26095
- const result = await readLogChunk(fs28, file, byteOffset);
26098
+ const result = await readLogChunk(fs28, file, byteOffset2);
26096
26099
  if (result !== null) {
26097
26100
  const combined = pendingBytes.length === 0 ? result.bytes : Buffer.concat([pendingBytes, result.bytes]);
26098
26101
  const completeLength = completeUtf8PrefixLength2(combined);
26099
- byteOffset = result.nextByteOffset;
26102
+ byteOffset2 = result.nextByteOffset;
26100
26103
  pendingBytes = combined.subarray(completeLength);
26101
26104
  const data = combined.subarray(0, completeLength).toString("utf8");
26102
26105
  if (data.length > 0) {
@@ -26163,13 +26166,13 @@ function assertSafeJobId2(jobId) {
26163
26166
  throw new Error(`Invalid job id "${jobId}". Job ids must be single filename components.`);
26164
26167
  }
26165
26168
  }
26166
- async function readLogChunk(fs28, file, byteOffset) {
26169
+ async function readLogChunk(fs28, file, byteOffset2) {
26167
26170
  const contents = await readFileIfExists3(fs28, file);
26168
- if (contents === null || byteOffset >= contents.byteLength) {
26171
+ if (contents === null || byteOffset2 >= contents.byteLength) {
26169
26172
  return null;
26170
26173
  }
26171
26174
  return {
26172
- bytes: contents.subarray(byteOffset),
26175
+ bytes: contents.subarray(byteOffset2),
26173
26176
  nextByteOffset: contents.byteLength
26174
26177
  };
26175
26178
  }
@@ -70934,7 +70937,8 @@ function scanMarkdown(source) {
70934
70937
  if (ast.type !== "root") {
70935
70938
  return [];
70936
70939
  }
70937
- const headings = ast.children.filter(isHeadingNode);
70940
+ const htmlCommentRanges = collectHtmlCommentRanges(source);
70941
+ const headings = ast.children.filter(isHeadingNode).filter((heading) => !isInsideHtmlComment(getRequiredRange(heading).start, htmlCommentRanges));
70938
70942
  if (headings.length === 0) {
70939
70943
  return [];
70940
70944
  }
@@ -70955,6 +70959,30 @@ function scanMarkdown(source) {
70955
70959
  applyNumbers(sections, baselineDepth);
70956
70960
  return sections;
70957
70961
  }
70962
+ function collectHtmlCommentRanges(source) {
70963
+ const ranges = [];
70964
+ let searchStart = 0;
70965
+ while (searchStart < source.length) {
70966
+ const commentStart = source.indexOf("<!--", searchStart);
70967
+ if (commentStart === -1) {
70968
+ return ranges;
70969
+ }
70970
+ const commentEndMarker = source.indexOf("-->", commentStart + "<!--".length);
70971
+ const commentEnd = commentEndMarker === -1 ? source.length : commentEndMarker + "-->".length;
70972
+ ranges.push({
70973
+ start: byteOffset(source, commentStart),
70974
+ end: byteOffset(source, commentEnd)
70975
+ });
70976
+ searchStart = commentEnd;
70977
+ }
70978
+ return ranges;
70979
+ }
70980
+ function isInsideHtmlComment(offset, ranges) {
70981
+ return ranges.some((range) => offset >= range.start && offset < range.end);
70982
+ }
70983
+ function byteOffset(source, index) {
70984
+ return Buffer.byteLength(source.slice(0, index), "utf8");
70985
+ }
70958
70986
  function isHeadingNode(node) {
70959
70987
  return node.type === "heading";
70960
70988
  }
@@ -71050,6 +71078,9 @@ async function loadMarkdownDocument(file, dependencies = {}) {
71050
71078
  };
71051
71079
  }
71052
71080
  function resolveMarkdownPath(file, cwd = process.cwd()) {
71081
+ if (file.trim().length === 0) {
71082
+ throw new UserError("invalid file: expected a non-empty path");
71083
+ }
71053
71084
  return path92.isAbsolute(file) ? file : path92.resolve(cwd, file);
71054
71085
  }
71055
71086
  function sliceMarkdownBytes(source, start, end) {
@@ -71085,6 +71116,9 @@ function hasYamlLikeLeadingFrontmatter(source) {
71085
71116
  if (lines[0] !== "---") {
71086
71117
  return false;
71087
71118
  }
71119
+ if (lines[1]?.trim().length === 0) {
71120
+ return false;
71121
+ }
71088
71122
  for (const line of lines.slice(1)) {
71089
71123
  const trimmed = line.trim();
71090
71124
  if (trimmed.length === 0) {
@@ -71150,16 +71184,19 @@ var init_read_markdown = __esm({
71150
71184
  // packages/markdown-reader/src/core/resolve.ts
71151
71185
  function resolveSection(sections, id) {
71152
71186
  const trimmedId = id.trim();
71187
+ if (trimmedId.length === 0) {
71188
+ throw new UserError("invalid section: expected a non-empty section id");
71189
+ }
71190
+ const sectionByNumber = sections.find((section) => section.number === trimmedId);
71191
+ if (sectionByNumber !== void 0) {
71192
+ return sectionByNumber;
71193
+ }
71153
71194
  const unnumberedTitleMatch = sections.find(
71154
71195
  (section) => section.number === null && section.title === trimmedId
71155
71196
  );
71156
71197
  if (unnumberedTitleMatch !== void 0) {
71157
71198
  return unnumberedTitleMatch;
71158
71199
  }
71159
- const sectionByNumber = sections.find((section) => section.number === trimmedId);
71160
- if (sectionByNumber !== void 0) {
71161
- return sectionByNumber;
71162
- }
71163
71200
  const titleMatches = sections.filter((section) => section.title === trimmedId);
71164
71201
  if (titleMatches.length === 1) {
71165
71202
  return titleMatches[0];
@@ -71181,6 +71218,9 @@ var init_resolve5 = __esm({
71181
71218
  // packages/markdown-reader/src/core/read-section.ts
71182
71219
  function createReadSection(dependencies = {}) {
71183
71220
  return async function readSection2(params) {
71221
+ if (params.section.trim().length === 0) {
71222
+ throw new UserError("invalid section: expected a non-empty section id");
71223
+ }
71184
71224
  const { source, sections } = await loadMarkdownDocument(params.file, dependencies);
71185
71225
  const section = resolveSection(sections, params.section);
71186
71226
  const end = params.includeChildren === false ? section.bodyEndNoChildren : section.bodyEnd;
@@ -71201,6 +71241,7 @@ var init_read_section = __esm({
71201
71241
  "use strict";
71202
71242
  init_document();
71203
71243
  init_resolve5();
71244
+ init_src21();
71204
71245
  readSection = createReadSection();
71205
71246
  }
71206
71247
  });
@@ -71215,8 +71256,14 @@ var init_tools2 = __esm({
71215
71256
  init_read_markdown();
71216
71257
  init_read_section();
71217
71258
  readParams = S.Object({
71218
- file: S.String({ description: "Path to the markdown file" }),
71219
- depth: S.Optional(S.Number({ description: "Limit TOC to headings at depth <= n" }))
71259
+ file: S.String({ description: "Path to the markdown file", minLength: 1 }),
71260
+ depth: S.Optional(
71261
+ S.Number({
71262
+ description: "Limit TOC to headings at depth <= n",
71263
+ jsonType: "integer",
71264
+ minimum: 0
71265
+ })
71266
+ )
71220
71267
  });
71221
71268
  tocEntryResult = S.Object({
71222
71269
  depth: S.Number(),
@@ -71236,8 +71283,11 @@ var init_tools2 = __esm({
71236
71283
  handler: async ({ params }) => readMarkdown(params)
71237
71284
  });
71238
71285
  readSectionParams = S.Object({
71239
- file: S.String({ description: "Path to the markdown file" }),
71240
- section: S.String({ description: "Numeric path or exact heading text to read" }),
71286
+ file: S.String({ description: "Path to the markdown file", minLength: 1 }),
71287
+ section: S.String({
71288
+ description: "Numeric path or exact heading text to read",
71289
+ minLength: 1
71290
+ }),
71241
71291
  includeChildren: S.Optional(S.Boolean({ description: "Include nested child sections" }))
71242
71292
  });
71243
71293
  readSectionTool = defineCommand({
@@ -100415,6 +100465,9 @@ async function assertMemoryRootIsNotSymlink(root) {
100415
100465
  try {
100416
100466
  const stat33 = await fs12.lstat(currentPath);
100417
100467
  if (stat33.isSymbolicLink()) {
100468
+ if (await isAllowedMacSystemAlias(currentPath)) {
100469
+ continue;
100470
+ }
100418
100471
  throw new MemoryPathError(`Memory root "${root}" cannot be a symbolic link.`);
100419
100472
  }
100420
100473
  } catch (error3) {
@@ -100425,6 +100478,17 @@ async function assertMemoryRootIsNotSymlink(root) {
100425
100478
  }
100426
100479
  }
100427
100480
  }
100481
+ async function isAllowedMacSystemAlias(currentPath) {
100482
+ if (currentPath !== "/var") {
100483
+ return false;
100484
+ }
100485
+ try {
100486
+ const target = await fs12.readlink(currentPath);
100487
+ return target === "/private/var" || target === "private/var";
100488
+ } catch {
100489
+ return false;
100490
+ }
100491
+ }
100428
100492
  function isMissing(error3) {
100429
100493
  return hasOwnErrorCode27(error3, "ENOENT");
100430
100494
  }
@@ -100574,7 +100638,11 @@ function serializeFrontmatter(frontmatter, body) {
100574
100638
  return stringifyFrontmatter(serialized, body);
100575
100639
  }
100576
100640
  function parseSourceRef(serialized) {
100577
- const [rawPath, rawAnchor] = serialized.split("#", 2);
100641
+ const parts = serialized.split("#");
100642
+ if (parts.length > 2) {
100643
+ throw new Error(`Invalid source ref "${serialized}".`);
100644
+ }
100645
+ const [rawPath, rawAnchor] = parts;
100578
100646
  const normalizedPath = rawPath?.trim();
100579
100647
  if (normalizedPath === void 0 || normalizedPath.length === 0) {
100580
100648
  throw new Error(`Invalid source ref "${serialized}".`);
@@ -100828,7 +100896,7 @@ async function searchMemory(root, query) {
100828
100896
  if (normalizedQuery.length === 0) {
100829
100897
  throw new Error("Search query cannot be empty.");
100830
100898
  }
100831
- const relPaths = await collectMarkdownRelPaths(root);
100899
+ const relPaths = await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH);
100832
100900
  const hits = [];
100833
100901
  for (const relPath of relPaths) {
100834
100902
  await assertNoSymlinkSegments(root, relPath);
@@ -101351,7 +101419,7 @@ async function appendToPage(root, relPath, content, opts) {
101351
101419
  const before = await snapshot(root);
101352
101420
  await fs19.mkdir(path158.dirname(pagePath), { recursive: true });
101353
101421
  await assertNoSymlinkSegments(root, pageRelPath);
101354
- const parsed = originalPage === void 0 ? { frontmatter: {}, body: "" } : parseFrontmatter4(originalPage);
101422
+ const parsed = parseAppendTarget(originalPage, pageRelPath);
101355
101423
  try {
101356
101424
  await writeFileAtomically7(
101357
101425
  pagePath,
@@ -101363,6 +101431,18 @@ async function appendToPage(root, relPath, content, opts) {
101363
101431
  throw error3;
101364
101432
  }
101365
101433
  }
101434
+ function parseAppendTarget(originalPage, relPath) {
101435
+ if (originalPage === void 0) {
101436
+ return { frontmatter: {}, body: "" };
101437
+ }
101438
+ try {
101439
+ return parseFrontmatter4(originalPage);
101440
+ } catch (error3) {
101441
+ const message2 = error3 instanceof Error ? error3.message : String(error3);
101442
+ console.warn(`Failed to parse frontmatter for "${relPath}": ${message2}`);
101443
+ return { frontmatter: {}, body: originalPage };
101444
+ }
101445
+ }
101366
101446
  async function clearMemory(root) {
101367
101447
  await assertMemoryRootIsNotSymlink(root);
101368
101448
  const stagedRoot = `${root}.clear-${randomUUID43()}`;
@@ -101728,17 +101808,21 @@ async function clearCache(root, opts = {}) {
101728
101808
  async function assertIngestCachePathIsNotSymlink(root) {
101729
101809
  await assertNoSymlinkSegments(root, MEMORY_INGEST_CACHE_DIR_RELPATH);
101730
101810
  }
101731
- function parseCacheEntry(value, _key) {
101811
+ function parseCacheEntry(value, requestedKey) {
101732
101812
  const object = expectRecord2(value);
101813
+ const key2 = expectString2(getOwnEntry36(object, "key"), "key");
101814
+ if (key2 !== requestedKey) {
101815
+ throw new Error(`Cache entry key "${key2}" does not match requested key "${requestedKey}".`);
101816
+ }
101733
101817
  return {
101734
- key: expectString2(getOwnEntry36(object, "key"), "key"),
101818
+ key: key2,
101735
101819
  ingestedAt: expectString2(getOwnEntry36(object, "ingestedAt"), "ingestedAt"),
101736
101820
  sourceLabel: expectString2(getOwnEntry36(object, "sourceLabel"), "sourceLabel"),
101737
101821
  diff: parseMemoryDiff(getOwnEntry36(object, "diff")),
101738
- exitCode: expectNumber(getOwnEntry36(object, "exitCode"), "exitCode"),
101739
- durationMs: expectNumber(getOwnEntry36(object, "durationMs"), "durationMs"),
101740
- memoryTokens: expectNumber(getOwnEntry36(object, "memoryTokens"), "memoryTokens"),
101741
- sourceTokens: expectNumber(getOwnEntry36(object, "sourceTokens"), "sourceTokens"),
101822
+ exitCode: expectNonNegativeInteger2(getOwnEntry36(object, "exitCode"), "exitCode"),
101823
+ durationMs: expectNonNegativeInteger2(getOwnEntry36(object, "durationMs"), "durationMs"),
101824
+ memoryTokens: expectNonNegativeInteger2(getOwnEntry36(object, "memoryTokens"), "memoryTokens"),
101825
+ sourceTokens: expectNonNegativeInteger2(getOwnEntry36(object, "sourceTokens"), "sourceTokens"),
101742
101826
  promptTemplateVersion: expectString2(
101743
101827
  getOwnEntry36(object, "promptTemplateVersion"),
101744
101828
  "promptTemplateVersion"
@@ -101766,9 +101850,9 @@ function expectString2(value, field) {
101766
101850
  }
101767
101851
  return value;
101768
101852
  }
101769
- function expectNumber(value, field) {
101770
- if (typeof value !== "number" || Number.isNaN(value)) {
101771
- throw new Error(`Expected number at "${field}".`);
101853
+ function expectNonNegativeInteger2(value, field) {
101854
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
101855
+ throw new Error(`Expected non-negative integer at "${field}".`);
101772
101856
  }
101773
101857
  return value;
101774
101858
  }
@@ -102427,7 +102511,7 @@ async function installMemory(options) {
102427
102511
  }
102428
102512
  throw error3;
102429
102513
  }
102430
- mcpConfigPath = options.agent === "codex" ? `${options.homeDir}/.config/codex/mcp-config.json` : `${options.homeDir}/.mcp.json`;
102514
+ mcpConfigPath = resolveMcpConfigPath(options.agent, options.homeDir, options.platform);
102431
102515
  }
102432
102516
  return {
102433
102517
  skillInstalled: !options.mcpOnly,
@@ -102436,6 +102520,14 @@ async function installMemory(options) {
102436
102520
  mcpConfigPath
102437
102521
  };
102438
102522
  }
102523
+ function resolveMcpConfigPath(agent3, homeDir, platform) {
102524
+ const support = resolveAgentSupport3(agent3);
102525
+ if (support.status !== "supported" || support.config === void 0) {
102526
+ throw new Error(`Unsupported agent: ${agent3}`);
102527
+ }
102528
+ const configFile = typeof support.config.configFile === "function" ? support.config.configFile(platform) : support.config.configFile;
102529
+ return configFile.startsWith("~/") ? path164.join(homeDir, configFile.slice(2)) : configFile;
102530
+ }
102439
102531
  async function removeInstalledSkill(options, skillPath) {
102440
102532
  const baseDir = options.scope === "global" ? options.homeDir : options.cwd;
102441
102533
  const displayPath = skillPath.startsWith("~/") ? skillPath.slice(2) : skillPath;
@@ -102456,6 +102548,45 @@ var init_install3 = __esm({
102456
102548
  }
102457
102549
  });
102458
102550
 
102551
+ // packages/memory/src/agent-response.ts
102552
+ function parseMemoryAgentResponse(stdout) {
102553
+ let value;
102554
+ try {
102555
+ value = JSON.parse(stdout);
102556
+ } catch {
102557
+ throw new Error("Memory agent returned invalid JSON output.");
102558
+ }
102559
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
102560
+ throw new Error("Memory agent returned an invalid result payload.");
102561
+ }
102562
+ const result = value;
102563
+ if (typeof result.answer !== "string" || !Array.isArray(result.citations) || !isNonNegativeInteger(result.tokensUsed) || !result.citations.every(isQueryCitation)) {
102564
+ throw new Error("Memory agent returned an invalid result payload.");
102565
+ }
102566
+ return {
102567
+ answer: result.answer,
102568
+ citations: result.citations,
102569
+ tokensUsed: result.tokensUsed
102570
+ };
102571
+ }
102572
+ function isQueryCitation(value) {
102573
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
102574
+ return false;
102575
+ }
102576
+ const citation = value;
102577
+ return typeof citation.relPath === "string" && (citation.section === void 0 || typeof citation.section === "string") && typeof citation.confidence === "string" && validCitationConfidences.has(citation.confidence);
102578
+ }
102579
+ function isNonNegativeInteger(value) {
102580
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
102581
+ }
102582
+ var validCitationConfidences;
102583
+ var init_agent_response = __esm({
102584
+ "packages/memory/src/agent-response.ts"() {
102585
+ "use strict";
102586
+ validCitationConfidences = /* @__PURE__ */ new Set(["extracted", "inferred", "ambiguous"]);
102587
+ }
102588
+ });
102589
+
102459
102590
  // packages/memory/src/query.ts
102460
102591
  import * as fs25 from "node:fs/promises";
102461
102592
  import path165 from "node:path";
@@ -102478,7 +102609,7 @@ async function queryMemory(root, options) {
102478
102609
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
102479
102610
  const context = await selectQueryContext(root, options.question, options.budget);
102480
102611
  const spawned = await spawn5(agentId, { prompt: context.prompt });
102481
- const result = parseQueryResponse(spawned.stdout);
102612
+ const result = parseMemoryAgentResponse(spawned.stdout);
102482
102613
  return {
102483
102614
  answer: result.answer,
102484
102615
  citations: result.citations,
@@ -102487,26 +102618,6 @@ async function queryMemory(root, options) {
102487
102618
  exitCode: spawned.exitCode
102488
102619
  };
102489
102620
  }
102490
- function parseQueryResponse(stdout) {
102491
- let value;
102492
- try {
102493
- value = JSON.parse(stdout);
102494
- } catch {
102495
- throw new Error("Memory agent returned invalid JSON output.");
102496
- }
102497
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
102498
- throw new Error("Memory agent returned an invalid result payload.");
102499
- }
102500
- const result = value;
102501
- if (typeof result.answer !== "string" || !Array.isArray(result.citations) || typeof result.tokensUsed !== "number" || !Number.isFinite(result.tokensUsed)) {
102502
- throw new Error("Memory agent returned an invalid result payload.");
102503
- }
102504
- return {
102505
- answer: result.answer,
102506
- citations: result.citations,
102507
- tokensUsed: result.tokensUsed
102508
- };
102509
- }
102510
102621
  async function selectQueryContext(root, question, budget) {
102511
102622
  if (!Number.isFinite(budget) || budget < 0) {
102512
102623
  throw new Error("budget must be a finite non-negative number");
@@ -102599,6 +102710,7 @@ var init_query = __esm({
102599
102710
  init_src44();
102600
102711
  init_src17();
102601
102712
  init_src9();
102713
+ init_agent_response();
102602
102714
  init_pages();
102603
102715
  init_paths2();
102604
102716
  }
@@ -102634,7 +102746,7 @@ async function explainPage(root, options) {
102634
102746
  };
102635
102747
  const agentId = await resolveAgent(configOptions, options.agent ?? null) ?? options.agent ?? "claude-code";
102636
102748
  const spawned = await spawn5(agentId, { prompt });
102637
- const response = parseExplainResponse(spawned.stdout);
102749
+ const response = parseMemoryAgentResponse(spawned.stdout);
102638
102750
  return {
102639
102751
  answer: response.answer,
102640
102752
  citations: response.citations,
@@ -102645,26 +102757,6 @@ async function explainPage(root, options) {
102645
102757
  outboundSources: targetPage.frontmatter.sources ?? []
102646
102758
  };
102647
102759
  }
102648
- function parseExplainResponse(stdout) {
102649
- let value;
102650
- try {
102651
- value = JSON.parse(stdout);
102652
- } catch {
102653
- throw new Error("Memory agent returned invalid JSON output.");
102654
- }
102655
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
102656
- throw new Error("Memory agent returned an invalid result payload.");
102657
- }
102658
- const response = value;
102659
- if (typeof response.answer !== "string" || !Array.isArray(response.citations) || typeof response.tokensUsed !== "number" || !Number.isFinite(response.tokensUsed)) {
102660
- throw new Error("Memory agent returned an invalid result payload.");
102661
- }
102662
- return {
102663
- answer: response.answer,
102664
- citations: response.citations,
102665
- tokensUsed: response.tokensUsed
102666
- };
102667
- }
102668
102760
  function collectRelatedPages(pages, targetRelPath, outboundSources) {
102669
102761
  const memorySourcePaths = new Set(outboundSources.map((source) => source.path));
102670
102762
  return pages.filter((page) => {
@@ -102708,6 +102800,7 @@ var init_explain = __esm({
102708
102800
  init_src44();
102709
102801
  init_src17();
102710
102802
  init_src9();
102803
+ init_agent_response();
102711
102804
  init_errors7();
102712
102805
  init_pages();
102713
102806
  init_query();
@@ -102802,7 +102895,7 @@ var init_src45 = __esm({
102802
102895
  var SKILL_memory_default;
102803
102896
  var init_SKILL_memory = __esm({
102804
102897
  "packages/memory/src/templates/SKILL_memory.md"() {
102805
- SKILL_memory_default = '## CLI \u2014 `poe-code memory <subcommand>`\n| Command | Purpose (when the agent should reach for it) |\n|---|---|\n| `init` | create `.poe-code/memory/` if missing; safe before first write |\n| `ls` | list pages + one-line descriptions; first step for recall questions |\n| `show <path>` | print one page verbatim after `ls` identifies a candidate |\n| `search <query>` | ripgrep over memory when the page is not obvious from `ls` |\n| `write <path> --reason <text>` | replace a page from stdin when authoring or rewriting |\n| `append <path>` | append stdin to a page; intended for `LOG.md`-style updates |\n| `edit <path>` | open `$EDITOR`; avoid from agents, prefer `write`/`append` |\n| `ingest <source>` | spawn an ingest agent to fold a file or URL into memory |\n| `query "<question>"` | answer a question from memory only, with citations |\n| `explain <path>` | summarize a page plus its inbound/outbound links |\n| `lint [--fix]` | find stale citations, untagged claims, contradictions |\n| `status [--no-tokens]` | show counts, bytes, and token-reduction ratio |\n| `cache status` / `cache clear` | inspect or clear the ingest cache |\n| `clear --yes` | wipe memory; destructive, user-request only |\n| `install` | install this skill and register the `poe-code-memory` MCP server |\n\n## MCP \u2014 `poe-code-memory` server\n| Tool | Purpose |\n|---|---|\n| `list_pages` | enumerate pages (preferred over shelling out to `memory ls`) |\n| `read_page` | read one page (preferred over `memory show`) |\n| `search_memory` | search memory text (preferred over `memory search`) |\n| `append_to_page` | append to a page when writes are enabled |\n| `status` | read counts and token ratio |\n\n- Prefer MCP tools over shell commands when both surfaces are available.\n- Confidence-tag non-trivial claims with `extracted`, `inferred`, or `ambiguous`.\n- Keep memory edits focused; update the minimal relevant pages.\n- Never call `memory clear` without explicit user request.\n';
102898
+ SKILL_memory_default = '## CLI \u2014 `poe-code memory <subcommand>`\n| Command | Purpose (when the agent should reach for it) |\n|---|---|\n| `init` | create `.poe-code/memory/` if missing; safe before first write |\n| `ls` | list pages + one-line descriptions; first step for recall questions |\n| `show <path>` | print one page verbatim after `ls` identifies a candidate |\n| `search <query>` | ripgrep over memory when the page is not obvious from `ls` |\n| `write <path> --reason <text>` | replace a page from stdin when authoring or rewriting |\n| `append <path>` | append stdin to a page; intended for `LOG.md`-style updates |\n| `edit <path>` | open `$EDITOR`; avoid from agents, prefer `write`/`append` |\n| `ingest <source>` | spawn an ingest agent to fold a file or URL into memory |\n| `query "<question>"` | answer a question from memory only, with citations |\n| `explain <path>` | summarize a page plus its inbound/outbound links |\n| `lint` | find stale citations, untagged claims, contradictions |\n| `status [--no-tokens]` | show counts, bytes, and token-reduction ratio |\n| `cache status` / `cache clear` | inspect or clear the ingest cache |\n| `clear --yes` | wipe memory; destructive, user-request only |\n| `install` | install this skill and register the `poe-code-memory` MCP server |\n\n## MCP \u2014 `poe-code-memory` server\n| Tool | Purpose |\n|---|---|\n| `list_pages` | enumerate pages (preferred over shelling out to `memory ls`) |\n| `read_page` | read one page (preferred over `memory show`) |\n| `search_memory` | search memory text (preferred over `memory search`) |\n| `append_to_page` | append to a page when writes are enabled |\n| `status` | read counts and token ratio |\n\n- Prefer MCP tools over shell commands when both surfaces are available.\n- Confidence-tag non-trivial claims with `extracted`, `inferred`, or `ambiguous`.\n- Keep memory edits focused; update the minimal relevant pages.\n- Never call `memory clear` without explicit user request.\n';
102806
102899
  }
102807
102900
  });
102808
102901
 
@@ -102874,6 +102967,28 @@ async function queryBudget(container, value) {
102874
102967
  projectFilePath: container.env.projectConfigPath
102875
102968
  });
102876
102969
  }
102970
+ function parseDecimalNonNegativeInteger(value, label) {
102971
+ if (value === void 0) {
102972
+ return void 0;
102973
+ }
102974
+ if (!isDecimalNonNegativeIntegerText(value)) {
102975
+ throw new ValidationError(`${label} must be a decimal non-negative integer.`);
102976
+ }
102977
+ const parsed = Number(value);
102978
+ if (!Number.isSafeInteger(parsed)) {
102979
+ throw new ValidationError(`${label} must be a decimal non-negative integer.`);
102980
+ }
102981
+ return parsed;
102982
+ }
102983
+ function isDecimalNonNegativeIntegerText(value) {
102984
+ if (value.length === 0) {
102985
+ return false;
102986
+ }
102987
+ if (value.length > 1 && value[0] === "0") {
102988
+ return false;
102989
+ }
102990
+ return [...value].every((char) => char >= "0" && char <= "9");
102991
+ }
102877
102992
  async function readCommandContent(content) {
102878
102993
  if (content !== void 0) {
102879
102994
  return content;
@@ -103012,6 +103127,8 @@ function registerMemoryCommand(program, container) {
103012
103127
  memory.command("edit").description("Open a page in $EDITOR.").argument("<path>", "Page path (relative to memory pages/)").option("--reason <text>", "Reason for the memory update", "edit").action(async (pagePath, options) => {
103013
103128
  const flags = resolveCommandFlags(program);
103014
103129
  const root = await resolveRoot(container);
103130
+ const mem = openMemory({ root });
103131
+ await assertInitialized(mem);
103015
103132
  const relPath = resolvePageRelPath(pagePath);
103016
103133
  const editor = resolveEditor4(container);
103017
103134
  if (flags.dryRun) {
@@ -103030,10 +103147,7 @@ function registerMemoryCommand(program, container) {
103030
103147
  const root = await resolveRoot(container);
103031
103148
  const mem = openMemory({ root });
103032
103149
  await assertInitialized(mem);
103033
- const timeoutMs = options.timeoutMs === void 0 ? void 0 : Number(options.timeoutMs);
103034
- if (timeoutMs !== void 0 && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
103035
- throw new ValidationError("Timeout must be a finite non-negative number.");
103036
- }
103150
+ const timeoutMs = parseDecimalNonNegativeInteger(options.timeoutMs, "Timeout");
103037
103151
  const result = await mem.ingest({
103038
103152
  source: resolveIngestSource(container.env.cwd, source),
103039
103153
  agent: options.agent,
@@ -103046,10 +103160,7 @@ function registerMemoryCommand(program, container) {
103046
103160
  process.stdout.write(`${result.cacheHit ? "Cache hit" : "Ingested"}: ${result.diff.created.length} created, ${result.diff.updated.length} updated, ${result.diff.deleted.length} deleted.
103047
103161
  `);
103048
103162
  });
103049
- memory.command("lint").description("Audit memory confidence and provenance claims.").option("--fix", "Reserved for automated repair").action(async (options) => {
103050
- if (options.fix) {
103051
- throw new ValidationError("Automated memory lint repair is not available yet.");
103052
- }
103163
+ memory.command("lint").description("Audit memory confidence and provenance claims.").action(async () => {
103053
103164
  const root = await resolveRoot(container);
103054
103165
  const mem = openMemory({ root });
103055
103166
  await assertInitialized(mem);
@@ -103066,24 +103177,41 @@ function registerMemoryCommand(program, container) {
103066
103177
  }
103067
103178
  });
103068
103179
  memory.command("query").description("Answer a question using memory-only context.").argument("<question>", "Question").option("--budget <tokens>", "Token budget").option("--agent <agent>", "Agent override").action(async (question, options) => {
103180
+ const flags = resolveCommandFlags(program);
103069
103181
  const root = await resolveRoot(container);
103070
103182
  const mem = openMemory({ root });
103071
103183
  await assertInitialized(mem);
103184
+ const budget = await queryBudget(container, options.budget);
103185
+ if (flags.dryRun) {
103186
+ createExecutionResources(container, flags, "memory:query").logger.dryRun(
103187
+ `Would query memory with budget ${budget}.`
103188
+ );
103189
+ return;
103190
+ }
103072
103191
  const result = await mem.query({
103073
103192
  question,
103074
- budget: await queryBudget(container, options.budget),
103193
+ budget,
103075
103194
  agent: options.agent
103076
103195
  });
103077
103196
  process.stdout.write(`${result.answer}
103078
103197
  `);
103079
103198
  });
103080
103199
  memory.command("explain").description("Summarize a memory page and its relationships.").argument("<path>", "Page path (relative to memory pages/)").option("--budget <tokens>", "Token budget").option("--agent <agent>", "Agent override").action(async (pagePath, options) => {
103200
+ const flags = resolveCommandFlags(program);
103081
103201
  const root = await resolveRoot(container);
103082
103202
  const mem = openMemory({ root });
103083
103203
  await assertInitialized(mem);
103204
+ const relPath = resolvePageRelPath(pagePath);
103205
+ const budget = await queryBudget(container, options.budget);
103206
+ if (flags.dryRun) {
103207
+ createExecutionResources(container, flags, "memory:explain").logger.dryRun(
103208
+ `Would explain ${relPath} with budget ${budget}.`
103209
+ );
103210
+ return;
103211
+ }
103084
103212
  const result = await mem.explainPage({
103085
- relPath: resolvePageRelPath(pagePath),
103086
- budget: await queryBudget(container, options.budget),
103213
+ relPath,
103214
+ budget,
103087
103215
  agent: options.agent
103088
103216
  });
103089
103217
  process.stdout.write(`${result.answer}
@@ -103217,7 +103345,12 @@ function registerMemoryMcpCommand(program, container) {
103217
103345
  projectConfigPath: container.env.projectConfigPath
103218
103346
  });
103219
103347
  const handle = openMemory({ root });
103220
- const { server } = await startMemoryMcpServer(handle, { allowWrites: options.allowWrites === true });
103348
+ const allowWrites = options.allowWrites === true || await mcpWritesAllowed({
103349
+ fs: container.fs,
103350
+ filePath: container.env.configPath,
103351
+ projectFilePath: container.env.projectConfigPath
103352
+ });
103353
+ const { server } = await startMemoryMcpServer(handle, { allowWrites });
103221
103354
  await server.listen();
103222
103355
  });
103223
103356
  }
@@ -103225,6 +103358,7 @@ var init_memory_mcp = __esm({
103225
103358
  "src/cli/commands/memory-mcp.ts"() {
103226
103359
  "use strict";
103227
103360
  init_src45();
103361
+ init_src9();
103228
103362
  }
103229
103363
  });
103230
103364
 
@@ -137375,7 +137509,7 @@ var init_package2 = __esm({
137375
137509
  "package.json"() {
137376
137510
  package_default2 = {
137377
137511
  name: "poe-code",
137378
- version: "3.0.311",
137512
+ version: "3.0.313",
137379
137513
  description: "CLI tool to configure Poe API for developer workflows.",
137380
137514
  type: "module",
137381
137515
  main: "./dist/index.js",