@c4a/context-cli 0.7.29 → 0.7.34

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/cli.js CHANGED
@@ -42637,7 +42637,7 @@ function parseDocumentResourceMaterialization(value, field) {
42637
42637
  ...reason !== undefined ? { reason } : {}
42638
42638
  };
42639
42639
  });
42640
- const expectedStatus = items.some((item) => item.status === "failed" && item.required && !isNonBlockingDocumentResourceFailureReasonCode(item.reason_code)) ? "error" : items.some((item) => item.status === "failed") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true) ? "warning" : "complete";
42640
+ const expectedStatus = items.some((item) => item.status === "failed" && item.required && !isNonBlockingDocumentResourceFailureReasonCode(item.reason_code)) ? "error" : items.some((item) => item.status === "failed" || item.reason_code === "document.resource.preview") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true) ? "warning" : "complete";
42641
42641
  if (value.status !== expectedStatus)
42642
42642
  throw new TypeError(`${field}.status must be ${expectedStatus} for its items`);
42643
42643
  const expected = (status) => {
@@ -90705,7 +90705,7 @@ function documentSnapshotFidelityState(manifest, captureReport) {
90705
90705
  ],
90706
90706
  warnings: [
90707
90707
  ...report === undefined ? [] : render((issue) => issue.impact === "projection" || issue.severity === "warning"),
90708
- ...(resources?.items ?? []).filter((item) => item.status === "failed" && (!item.required || isNonBlockingDocumentResourceFailureReasonCode(item.reason_code))).map((item) => `${item.reason_code ?? "lark.capture.resource-materialization-warning"}: ${item.kind}: ${item.reason ?? item.locator}`)
90708
+ ...(resources?.items ?? []).filter((item) => item.reason_code === "document.resource.preview" || item.status === "failed" && (!item.required || isNonBlockingDocumentResourceFailureReasonCode(item.reason_code))).map((item) => `${item.reason_code ?? "lark.capture.resource-materialization-warning"}: ${item.kind}: ${item.reason ?? item.locator}`)
90709
90709
  ]
90710
90710
  };
90711
90711
  }
@@ -97826,32 +97826,6 @@ var init_larkDocxXml = __esm(() => {
97826
97826
  ]);
97827
97827
  });
97828
97828
 
97829
- // src/lib/larkImagePolicy.ts
97830
- function omitLarkImage(resource, policy, items, replacements, mediaType, failure2) {
97831
- if (resource.kind !== "image" && !mediaType?.startsWith("image/"))
97832
- return false;
97833
- const gif = mediaType === "image/gif" || /\.gif$/iu.test(resource.title ?? "") || Object.entries(resource.attributes).some(([key, value]) => ["mime_type", "content_type"].includes(key) && value === "image/gif");
97834
- if (!failure2 && policy.images !== "reference-only" && !(gif && policy.gifs === "reference-only"))
97835
- return false;
97836
- const title2 = (resource.title ?? "image").replace(/[\r\n<>]/gu, " ");
97837
- replacements.set(resource.locator, `> Image omitted: ${title2}. ${failure2 ? "Resource size limit exceeded." : "Excluded by selected policy."} See the source document. <!-- ${resource.locator} -->`);
97838
- items.push({
97839
- kind: resource.kind,
97840
- locator: resource.locator,
97841
- status: "reference-only",
97842
- required: false,
97843
- asset_paths: [],
97844
- reason_code: failure2 ? "image-budget-exceeded" : "image-excluded-by-policy",
97845
- reason: failure2 ?? "Image bytes were not retained; the selected image policy preserves a placeholder and source reference"
97846
- });
97847
- return true;
97848
- }
97849
- var LarkResourceBudgetError;
97850
- var init_larkImagePolicy = __esm(() => {
97851
- LarkResourceBudgetError = class LarkResourceBudgetError extends Error {
97852
- };
97853
- });
97854
-
97855
97829
  // src/lib/larkResourceCommand.ts
97856
97830
  function stableJson(value) {
97857
97831
  if (value === undefined)
@@ -97880,13 +97854,13 @@ function commandError(fallback, detail) {
97880
97854
  if (detail === undefined)
97881
97855
  return new LarkResourceCommandError(fallback);
97882
97856
  const message = [detail.message, detail.hint, detail.code].filter((value) => typeof value === "string" || typeof value === "number").map(String).join("; ");
97883
- return new LarkResourceCommandError(message.length > 0 ? message : stableJson(detail), typeof detail.type === "string" ? detail.type : undefined, typeof detail.subtype === "string" ? detail.subtype : undefined);
97857
+ return new LarkResourceCommandError(message.length > 0 ? message : stableJson(detail), typeof detail.type === "string" ? detail.type : undefined, typeof detail.subtype === "string" ? detail.subtype : undefined, typeof detail.code === "string" || typeof detail.code === "number" ? detail.code : undefined, typeof detail.log_id === "string" ? detail.log_id : undefined);
97884
97858
  }
97885
97859
  async function runLarkResourceCommand(runner2, args, options) {
97886
97860
  const result = await runner2(args, options);
97887
97861
  if (result.exitCode !== 0) {
97888
97862
  const fallback = result.stderr.trim() || `lark-cli ${args.slice(0, 2).join(" ")} failed with exit code ${result.exitCode ?? "unknown"}`;
97889
- throw commandError(fallback, errorDetail(result.stderr));
97863
+ throw commandError(fallback, errorDetail(result.stderr) ?? errorDetail(result.stdout));
97890
97864
  }
97891
97865
  const trimmed = result.stdout.trim();
97892
97866
  if (!trimmed.startsWith("{"))
@@ -97906,24 +97880,78 @@ async function runLarkResourceCommand(runner2, args, options) {
97906
97880
  }
97907
97881
  return result.stdout;
97908
97882
  }
97883
+ function isLarkResourcePermissionDenied(error) {
97884
+ return error instanceof LarkResourceCommandError && (String(error.code) === "403" || error.errorType === "authorization" && ["permission_denied", "access_denied", "missing_scope"].includes(error.errorSubtype ?? ""));
97885
+ }
97909
97886
  var LarkResourceCommandError;
97910
97887
  var init_larkResourceCommand = __esm(() => {
97911
97888
  LarkResourceCommandError = class LarkResourceCommandError extends Error {
97912
97889
  errorType;
97913
97890
  errorSubtype;
97914
- constructor(message, errorType, errorSubtype) {
97891
+ code;
97892
+ logId;
97893
+ constructor(message, errorType, errorSubtype, code3, logId) {
97915
97894
  super(message);
97916
97895
  this.errorType = errorType;
97917
97896
  this.errorSubtype = errorSubtype;
97897
+ this.code = code3;
97898
+ this.logId = logId;
97918
97899
  this.name = "LarkResourceCommandError";
97919
97900
  }
97920
97901
  };
97921
97902
  });
97922
97903
 
97904
+ // src/lib/larkMediaDownload.ts
97905
+ import { readdir as readdir21, rm as rm20 } from "node:fs/promises";
97906
+ import { join as join94 } from "node:path";
97907
+ async function downloadLarkMedia(input) {
97908
+ const shared = ["--as", input.identity, "--token", input.token, "--output", "./resource", "--format", "json"];
97909
+ try {
97910
+ await runLarkResourceCommand(input.runner, ["docs", "+media-download", ...shared, "--type", input.type, "--overwrite"], { cwd: input.cwd });
97911
+ return "original";
97912
+ } catch (error) {
97913
+ if (!input.allowPreview || !isLarkResourcePermissionDenied(error))
97914
+ throw error;
97915
+ for (const entry of await readdir21(input.cwd))
97916
+ await rm20(join94(input.cwd, entry), { recursive: true, force: true });
97917
+ await runLarkResourceCommand(input.runner, ["docs", "+media-preview", ...shared], { cwd: input.cwd });
97918
+ return "preview";
97919
+ }
97920
+ }
97921
+ var init_larkMediaDownload = __esm(() => {
97922
+ init_larkResourceCommand();
97923
+ });
97924
+
97925
+ // src/lib/larkImagePolicy.ts
97926
+ function omitLarkImage(resource, policy, items, replacements, mediaType, failure2) {
97927
+ if (resource.kind !== "image" && !mediaType?.startsWith("image/"))
97928
+ return false;
97929
+ const gif = mediaType === "image/gif" || /\.gif$/iu.test(resource.title ?? "") || Object.entries(resource.attributes).some(([key, value]) => ["mime_type", "content_type"].includes(key) && value === "image/gif");
97930
+ if (!failure2 && policy.images !== "reference-only" && !(gif && policy.gifs === "reference-only"))
97931
+ return false;
97932
+ const title2 = (resource.title ?? "image").replace(/[\r\n<>]/gu, " ");
97933
+ replacements.set(resource.locator, `> Image omitted: ${title2}. ${failure2 ? "Resource size limit exceeded." : "Excluded by selected policy."} See the source document. <!-- ${resource.locator} -->`);
97934
+ items.push({
97935
+ kind: resource.kind,
97936
+ locator: resource.locator,
97937
+ status: "reference-only",
97938
+ required: false,
97939
+ asset_paths: [],
97940
+ reason_code: failure2 ? "image-budget-exceeded" : "image-excluded-by-policy",
97941
+ reason: failure2 ?? "Image bytes were not retained; the selected image policy preserves a placeholder and source reference"
97942
+ });
97943
+ return true;
97944
+ }
97945
+ var LarkResourceBudgetError;
97946
+ var init_larkImagePolicy = __esm(() => {
97947
+ LarkResourceBudgetError = class LarkResourceBudgetError extends Error {
97948
+ };
97949
+ });
97950
+
97923
97951
  // src/lib/larkResourceMaterialization.ts
97924
97952
  import { createHash as createHash28 } from "node:crypto";
97925
- import { mkdtemp as mkdtemp4, readFile as readFile74, readdir as readdir21, rm as rm20 } from "node:fs/promises";
97926
- import { extname as extname13, join as join94 } from "node:path";
97953
+ import { mkdtemp as mkdtemp4, readFile as readFile74, readdir as readdir22, rm as rm21 } from "node:fs/promises";
97954
+ import { extname as extname13, join as join95 } from "node:path";
97927
97955
  import { tmpdir } from "node:os";
97928
97956
  function countByKind(items, status) {
97929
97957
  const counts2 = new Map;
@@ -98029,33 +98057,19 @@ function findBooleanField(value, name3) {
98029
98057
  async function downloadedFile(input) {
98030
98058
  if (input.localPath !== undefined) {
98031
98059
  const bytes = await readFile74(input.localPath);
98032
- return { path: input.localPath, bytes, mediaType: mediaTypeFor(input.localPath, bytes) };
98060
+ return { path: input.localPath, bytes, mediaType: mediaTypeFor(input.localPath, bytes), representation: "original" };
98033
98061
  }
98034
- const tempRoot = await mkdtemp4(join94(tmpdir(), "context-lark-resource-"));
98062
+ const tempRoot = await mkdtemp4(join95(tmpdir(), "context-lark-resource-"));
98035
98063
  try {
98036
- await runLarkResourceCommand(input.runner, [
98037
- "docs",
98038
- "+media-download",
98039
- "--as",
98040
- input.identity,
98041
- "--token",
98042
- input.token,
98043
- "--type",
98044
- input.type,
98045
- "--output",
98046
- "./resource",
98047
- "--overwrite",
98048
- "--format",
98049
- "json"
98050
- ], { cwd: tempRoot });
98051
- const entries2 = (await readdir21(tempRoot, { withFileTypes: true })).filter((entry) => entry.isFile());
98064
+ const representation = await downloadLarkMedia({ ...input, cwd: tempRoot });
98065
+ const entries2 = (await readdir22(tempRoot, { withFileTypes: true })).filter((entry) => entry.isFile());
98052
98066
  if (entries2.length !== 1)
98053
98067
  throw new Error(`media download produced ${entries2.length} files, expected exactly one`);
98054
98068
  const path3 = entries2[0]?.name ?? "resource.bin";
98055
- const bytes = await readFile74(join94(tempRoot, path3));
98056
- return { path: path3, bytes, mediaType: mediaTypeFor(path3, bytes) };
98069
+ const bytes = await readFile74(join95(tempRoot, path3));
98070
+ return { path: path3, bytes, mediaType: mediaTypeFor(path3, bytes), representation };
98057
98071
  } finally {
98058
- await rm20(tempRoot, { recursive: true, force: true });
98072
+ await rm21(tempRoot, { recursive: true, force: true });
98059
98073
  }
98060
98074
  }
98061
98075
  function parseCsv(value) {
@@ -98143,9 +98157,9 @@ async function sheetMaterialization(resource, runner2, identity2) {
98143
98157
  const sheetId = resource.attributes["sheet-id"];
98144
98158
  if (token === undefined || sheetId === undefined)
98145
98159
  throw new Error("embedded Sheet requires token and sheet-id");
98146
- const tempRoot = await mkdtemp4(join94(tmpdir(), "context-lark-sheet-"));
98160
+ const tempRoot = await mkdtemp4(join95(tmpdir(), "context-lark-sheet-"));
98147
98161
  try {
98148
- const outputPath = join94(tempRoot, "sheet.json");
98162
+ const outputPath = join95(tempRoot, "sheet.json");
98149
98163
  const stdout = await runLarkResourceCommand(runner2, [
98150
98164
  "sheets",
98151
98165
  "+csv-get",
@@ -98187,7 +98201,7 @@ ${markdownTable2(parseCsv(csv))}
98187
98201
  [CSV snapshot](${sourceAssetTarget(path3)}) <!-- ${resource.locator} -->`
98188
98202
  };
98189
98203
  } finally {
98190
- await rm20(tempRoot, { recursive: true, force: true });
98204
+ await rm21(tempRoot, { recursive: true, force: true });
98191
98205
  }
98192
98206
  }
98193
98207
  function basePageRecords(value) {
@@ -98301,7 +98315,7 @@ async function whiteboardMaterialization(resource, runner2, identity2) {
98301
98315
  if (token === undefined)
98302
98316
  throw new Error(`${resource.kind} has no whiteboard token`);
98303
98317
  const preview = await downloadedFile({ runner: runner2, identity: identity2, token, type: "whiteboard" });
98304
- const tempRoot = await mkdtemp4(join94(tmpdir(), "context-lark-whiteboard-"));
98318
+ const tempRoot = await mkdtemp4(join95(tmpdir(), "context-lark-whiteboard-"));
98305
98319
  let rawPayload;
98306
98320
  try {
98307
98321
  await runLarkResourceCommand(runner2, [
@@ -98319,9 +98333,9 @@ async function whiteboardMaterialization(resource, runner2, identity2) {
98319
98333
  "--format",
98320
98334
  "json"
98321
98335
  ], { cwd: tempRoot });
98322
- rawPayload = JSON.parse(await readFile74(join94(tempRoot, "raw.json"), "utf8"));
98336
+ rawPayload = JSON.parse(await readFile74(join95(tempRoot, "raw.json"), "utf8"));
98323
98337
  } finally {
98324
- await rm20(tempRoot, { recursive: true, force: true });
98338
+ await rm21(tempRoot, { recursive: true, force: true });
98325
98339
  }
98326
98340
  const digest6 = resourceDigest(resource);
98327
98341
  const previewPath = `materialized/${resource.kind}/${digest6}${extensionFor2(preview.mediaType, preview.path)}`;
@@ -98380,7 +98394,7 @@ function referenceOnlyReason(resource) {
98380
98394
  }
98381
98395
  function materializationReport(items) {
98382
98396
  const hasRequiredFailure = items.some((item) => item.status === "failed" && item.required && !isNonBlockingDocumentResourceFailureReasonCode(item.reason_code));
98383
- const hasOptionalFailure = items.some((item) => item.status === "failed" || item.reason_code === "image-budget-exceeded") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
98397
+ const hasOptionalFailure = items.some((item) => item.status === "failed" || item.reason_code === "image-budget-exceeded" || item.reason_code === "document.resource.preview") || items.some((item) => item.status === "reference-only" && item.kind === "poll" && item.reason?.includes("absent") === true);
98384
98398
  return {
98385
98399
  status: hasRequiredFailure ? "error" : hasOptionalFailure ? "warning" : "complete",
98386
98400
  discovered: countByKind(items),
@@ -98391,7 +98405,7 @@ function materializationReport(items) {
98391
98405
  };
98392
98406
  }
98393
98407
  function resourceFailureReasonCode(resource, error) {
98394
- if (error instanceof LarkResourceCommandError && error.errorType === "authorization" && error.errorSubtype === "permission_denied") {
98408
+ if (isLarkResourcePermissionDenied(error)) {
98395
98409
  return DOCUMENT_RESOURCE_PERMISSION_DENIED_REASON_CODE;
98396
98410
  }
98397
98411
  if (resource.kind !== "diagram" && resource.kind !== "whiteboard")
@@ -98511,6 +98525,7 @@ async function materializeLarkResources(input) {
98511
98525
  identity: identity2,
98512
98526
  token,
98513
98527
  type: "media",
98528
+ allowPreview: resource.kind === "image",
98514
98529
  ...input.mediaFiles?.[token] === undefined ? {} : { localPath: input.mediaFiles[token] }
98515
98530
  });
98516
98531
  if (omitLarkImage(resource, input.policy, items, replacements, downloaded.mediaType))
@@ -98522,7 +98537,7 @@ async function materializeLarkResources(input) {
98522
98537
  bytes: downloaded.bytes,
98523
98538
  mediaType: downloaded.mediaType,
98524
98539
  role: "evidence",
98525
- source: { kind: resource.kind, locator: resource.locator }
98540
+ source: { kind: resource.kind, locator: resource.locator, identity: identity2, representation: downloaded.representation }
98526
98541
  };
98527
98542
  assertBudget(asset, input.policy, totalBytes);
98528
98543
  totalBytes += asset.bytes.byteLength;
@@ -98530,8 +98545,17 @@ async function materializeLarkResources(input) {
98530
98545
  const title2 = markdownLabel(safeLabel(resource.title, resource.kind));
98531
98546
  const target = sourceAssetTarget(asset.path);
98532
98547
  const replacement = downloaded.mediaType.startsWith("image/") ? `![${title2}](${target}) <!-- ${resource.locator} -->` : `[${title2}](${target}) <!-- ${resource.locator} -->`;
98533
- replacements.set(resource.locator, replacement);
98534
- items.push({ kind: resource.kind, locator: resource.locator, status: "materialized", required, asset_paths: [asset.path] });
98548
+ replacements.set(resource.locator, downloaded.representation === "preview" ? `${replacement}
98549
+
98550
+ > Image preview captured; the original could not be downloaded.` : replacement);
98551
+ items.push({
98552
+ kind: resource.kind,
98553
+ locator: resource.locator,
98554
+ status: "materialized",
98555
+ required,
98556
+ asset_paths: [asset.path],
98557
+ ...downloaded.representation === "preview" ? { reason_code: "document.resource.preview", reason: "Same-identity preview captured; original download permission denied" } : {}
98558
+ });
98535
98559
  } catch (error) {
98536
98560
  if (error instanceof LarkResourceBudgetError && omitLarkImage(resource, input.policy, items, replacements, undefined, error.message))
98537
98561
  return;
@@ -98579,6 +98603,7 @@ function applyLarkResourceReplacements(markdown, resources, replacements) {
98579
98603
  }
98580
98604
  var REQUIRED_KINDS, MEDIA_TYPES_BY_EXTENSION;
98581
98605
  var init_larkResourceMaterialization = __esm(() => {
98606
+ init_larkMediaDownload();
98582
98607
  init_larkImagePolicy();
98583
98608
  init_src3();
98584
98609
  init_larkResourceCommand();
@@ -98654,6 +98679,89 @@ var init_larkMarkdownImages = __esm(() => {
98654
98679
  init_markdownLinks();
98655
98680
  });
98656
98681
 
98682
+ // src/lib/larkReadIdentity.ts
98683
+ function resolveLarkReadIdentity() {
98684
+ const value = process.env.CONTEXT_LARK_IDENTITY?.trim() || "user";
98685
+ if (value === "user" || value === "bot")
98686
+ return value;
98687
+ throw new ContextError(ExitCode.UserError, "CONTEXT_LARK_IDENTITY must be user or bot", {
98688
+ reason_code: "lark.invalid-read-identity",
98689
+ valid_values: ["user", "bot"],
98690
+ next: "Set CONTEXT_LARK_IDENTITY to user or bot and rerun the same command"
98691
+ });
98692
+ }
98693
+ function credentialFailure(result) {
98694
+ for (const output of [result.stdout, result.stderr]) {
98695
+ const start2 = output.indexOf("{");
98696
+ if (start2 < 0)
98697
+ continue;
98698
+ try {
98699
+ const parsed = JSON.parse(output.slice(start2));
98700
+ if (!parsed || typeof parsed !== "object" || !("error" in parsed))
98701
+ continue;
98702
+ if (result.exitCode === 0 && (!("ok" in parsed) || parsed.ok !== false))
98703
+ continue;
98704
+ const error = parsed.error;
98705
+ if (!error || typeof error !== "object")
98706
+ continue;
98707
+ const type = "type" in error ? error.type : undefined;
98708
+ const subtype = "subtype" in error ? error.subtype : undefined;
98709
+ if (type === "authorization" || type === "authentication" || ["missing_scope", "permission_denied", "access_denied", "need_user_authorization"].includes(String(subtype))) {
98710
+ return true;
98711
+ }
98712
+ } catch {}
98713
+ }
98714
+ return result.exitCode !== 0 && /reason=(?:bot_unavailable|not_authorized)\b|\b(?:missing_scope|permission_denied|access_denied)\b/iu.test(result.stderr);
98715
+ }
98716
+ function createLarkReadSession(runner2, initial, fallback = initial === "bot") {
98717
+ let identity2 = initial;
98718
+ let usedFallback = false;
98719
+ let readingDocument = false;
98720
+ let locked = false;
98721
+ const restart = new Error("Restart document with user identity");
98722
+ const run = async (args, options) => {
98723
+ const explicit = args.indexOf("--as");
98724
+ const requested = explicit >= 0 ? args[explicit + 1] : identity2;
98725
+ const selected = locked || usedFallback ? identity2 : requested === "bot" ? "bot" : "user";
98726
+ const withIdentity = [...args];
98727
+ if (explicit >= 0)
98728
+ withIdentity[explicit + 1] = selected;
98729
+ else
98730
+ withIdentity.push("--as", selected);
98731
+ const result = await runner2(withIdentity, options);
98732
+ identity2 = selected;
98733
+ if (readingDocument && identity2 === "bot" && fallback && !usedFallback && !args.includes("--help") && credentialFailure(result))
98734
+ throw restart;
98735
+ return result;
98736
+ };
98737
+ async function readDocument(read) {
98738
+ readingDocument = true;
98739
+ try {
98740
+ try {
98741
+ return await read();
98742
+ } catch (error) {
98743
+ if (error !== restart)
98744
+ throw error;
98745
+ usedFallback = true;
98746
+ identity2 = "user";
98747
+ return await read();
98748
+ }
98749
+ } finally {
98750
+ readingDocument = false;
98751
+ locked = true;
98752
+ }
98753
+ }
98754
+ return { run, readDocument, get identity() {
98755
+ return identity2;
98756
+ }, get usedFallback() {
98757
+ return usedFallback;
98758
+ } };
98759
+ }
98760
+ var init_larkReadIdentity = __esm(() => {
98761
+ init_errors3();
98762
+ init_exitCode();
98763
+ });
98764
+
98657
98765
  // src/lib/feishu.ts
98658
98766
  import { spawn as spawn4 } from "node:child_process";
98659
98767
  async function detectDocsFetchCapabilities(runner2) {
@@ -98670,14 +98778,19 @@ ${result.stderr}`;
98670
98778
  supportsDocFormat: result.exitCode === 0 && /\s--doc-format\b/u.test(help)
98671
98779
  };
98672
98780
  }
98673
- function resolveDocsFetchPlan(requested, runner2) {
98781
+ function resolveDocsFetchPlan(requested, runner2, cacheKey = runner2, identity2 = "user") {
98674
98782
  if (requested === "v1" || requested === "v2") {
98675
98783
  return Promise.resolve({ apiVersion: requested, docFormat: "xml" });
98676
98784
  }
98677
- let capabilitiesPromise = docsFetchCapabilitiesCache.get(runner2);
98785
+ let cached = docsFetchCapabilitiesCache.get(cacheKey);
98786
+ if (!cached) {
98787
+ cached = new Map;
98788
+ docsFetchCapabilitiesCache.set(cacheKey, cached);
98789
+ }
98790
+ let capabilitiesPromise = cached.get(identity2);
98678
98791
  if (capabilitiesPromise === undefined) {
98679
98792
  capabilitiesPromise = detectDocsFetchCapabilities(runner2);
98680
- docsFetchCapabilitiesCache.set(runner2, capabilitiesPromise);
98793
+ cached.set(identity2, capabilitiesPromise);
98681
98794
  }
98682
98795
  return capabilitiesPromise.then((capabilities) => {
98683
98796
  if (!capabilities.supportsDocFormat) {
@@ -98974,20 +99087,25 @@ function hasEmptySubPageList(projection) {
98974
99087
  return projection.fidelity.issues.some((issue) => issue.code === LARK_EMPTY_SUB_PAGE_LIST_CODE);
98975
99088
  }
98976
99089
  async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
98977
- const docsFetchPlan = input.prefetched ? { apiVersion: "v2", docFormat: "xml" } : await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2);
99090
+ const preference = input.prefetched?.identity ?? input.identity ?? resolveLarkReadIdentity();
99091
+ const cacheKey = runner2;
99092
+ const session = createLarkReadSession(runner2, input.prefetched?.identity ?? (preference === "bot" ? "bot" : "user"), !input.prefetched && preference === "bot");
99093
+ runner2 = session.run;
99094
+ const docsFetchPlan = input.prefetched ? { apiVersion: "v2", docFormat: "xml" } : await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2, cacheKey, preference);
98978
99095
  const identityFetch = input.prefetched ? {
98979
99096
  fetched: await fetchDocsResponse(input, docsFetchPlan, runner2, input.prefetched.identity),
98980
99097
  identity: input.prefetched.identity,
98981
99098
  fallback: false
98982
- } : await fetchWithIdentity(input, docsFetchPlan, runner2);
99099
+ } : await session.readDocument(() => fetchWithIdentity({ ...input, identity: preference }, docsFetchPlan, runner2));
98983
99100
  let fetched = identityFetch.fetched;
98984
- const accessIdentity = identityFetch.identity;
99101
+ let accessIdentity = input.prefetched?.identity ?? session.identity;
98985
99102
  let projection;
98986
99103
  for (let attempt = 0;attempt < MAX_STRUCTURAL_FETCH_ATTEMPTS && fetched.contentFormat === "xml"; attempt++) {
98987
99104
  projection = projectLarkDocxXml({ xml: fetched.body, sourceUrl: input.url });
98988
99105
  if (input.prefetched || !hasEmptySubPageList(projection) || attempt === MAX_STRUCTURAL_FETCH_ATTEMPTS - 1)
98989
99106
  break;
98990
99107
  fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
99108
+ accessIdentity = session.identity;
98991
99109
  }
98992
99110
  let body2 = fetched.body;
98993
99111
  let title2 = fetched.title;
@@ -99073,7 +99191,7 @@ ${body2}`,
99073
99191
  fidelity,
99074
99192
  resourceMaterialization,
99075
99193
  accessIdentity,
99076
- identityFallback: identityFetch.fallback
99194
+ identityFallback: identityFetch.fallback || session.usedFallback
99077
99195
  };
99078
99196
  }
99079
99197
  return {
@@ -99084,7 +99202,7 @@ ${body2}`,
99084
99202
  fidelity,
99085
99203
  resourceMaterialization,
99086
99204
  accessIdentity,
99087
- identityFallback: identityFetch.fallback
99205
+ identityFallback: identityFetch.fallback || session.usedFallback
99088
99206
  };
99089
99207
  }
99090
99208
  var LARK_BIN = "lark-cli", MAX_FETCH_PAGES = 50, MAX_STRUCTURAL_FETCH_ATTEMPTS = 2, LarkCliNotInstalledError, LarkCliError, defaultRunner = (args, options) => new Promise((resolve8, reject) => {
@@ -99116,6 +99234,7 @@ var init_feishu = __esm(() => {
99116
99234
  init_larkResourceMaterialization();
99117
99235
  init_larkCaptureReport();
99118
99236
  init_larkMarkdownImages();
99237
+ init_larkReadIdentity();
99119
99238
  LarkCliNotInstalledError = class LarkCliNotInstalledError extends Error {
99120
99239
  constructor() {
99121
99240
  super(`${LARK_BIN} not installed — see https://github.com/larksuite/cli for install instructions`);
@@ -99168,8 +99287,8 @@ var init_sensitiveSourceLiteral = __esm(() => {
99168
99287
  });
99169
99288
 
99170
99289
  // src/project/documentCaptureLark.ts
99171
- import { readdir as readdir22, readFile as readFile75 } from "node:fs/promises";
99172
- import { basename as basename10, extname as extname14, join as join95 } from "node:path";
99290
+ import { readdir as readdir23, readFile as readFile75 } from "node:fs/promises";
99291
+ import { basename as basename10, extname as extname14, join as join96 } from "node:path";
99173
99292
  function titleFromMarkdown2(markdown, fallbackPath) {
99174
99293
  const heading2 = markdown.split(`
99175
99294
  `).find((line) => /^#\s+\S/u.test(line));
@@ -99196,7 +99315,7 @@ async function fileContentMatches(path3, content3) {
99196
99315
  }
99197
99316
  }
99198
99317
  function sourceManifestPath2(entry) {
99199
- return entry.snapshot?.manifest ?? join95(entry.materializedAt, "manifest.json");
99318
+ return entry.snapshot?.manifest ?? join96(entry.materializedAt, "manifest.json");
99200
99319
  }
99201
99320
  function larkRuntimeError(message, detail) {
99202
99321
  return new ContextError(ExitCode.ExternalToolError, message, {
@@ -99276,7 +99395,7 @@ function larkErrorRecovery(error, sourceName) {
99276
99395
  if (/auth|login|permission|forbidden|unauthori[sz]ed|scope/iu.test(message)) {
99277
99396
  return {
99278
99397
  reasonCode: "external.authorization-required",
99279
- next: "Run lark-cli auth login with an account that can read the document, then rerun capture"
99398
+ next: "Check the selected Lark identity and document access. For host-managed credentials use the host authorization flow; use lark-cli auth login only for a locally managed user login, then rerun capture"
99280
99399
  };
99281
99400
  }
99282
99401
  return {
@@ -99323,12 +99442,12 @@ function assetManifestEntry(asset, assetRoot) {
99323
99442
  };
99324
99443
  }
99325
99444
  async function listSnapshotAssetFiles(root2, assetRoot) {
99326
- const assetsRoot = join95(root2, assetRoot);
99445
+ const assetsRoot = join96(root2, assetRoot);
99327
99446
  const files = [];
99328
99447
  const visit4 = async (dir, prefix = assetRoot) => {
99329
99448
  let entries2;
99330
99449
  try {
99331
- entries2 = await readdir22(dir, { withFileTypes: true });
99450
+ entries2 = await readdir23(dir, { withFileTypes: true });
99332
99451
  } catch (error) {
99333
99452
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
99334
99453
  return;
@@ -99336,7 +99455,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
99336
99455
  }
99337
99456
  for (const entry of entries2) {
99338
99457
  const relPath = `${prefix}/${entry.name}`;
99339
- const absolutePath = join95(dir, entry.name);
99458
+ const absolutePath = join96(dir, entry.name);
99340
99459
  if (entry.isDirectory()) {
99341
99460
  await visit4(absolutePath, relPath);
99342
99461
  continue;
@@ -99351,7 +99470,7 @@ async function listSnapshotAssetFiles(root2, assetRoot) {
99351
99470
  }
99352
99471
  async function staleSnapshotAssetPaths(input) {
99353
99472
  const existingPaths = await listSnapshotAssetFiles(input.materializedAtAbsPath, input.assetRoot);
99354
- return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) => join95(input.materializedAtAbsPath, path3));
99473
+ return existingPaths.filter((path3) => !input.currentPaths.has(path3)).map((path3) => join96(input.materializedAtAbsPath, path3));
99355
99474
  }
99356
99475
  function normalizeLarkError(error, sourceName) {
99357
99476
  if (error instanceof ContextError)
@@ -99421,6 +99540,8 @@ async function runCaptureLarkPhaseUnlocked(input) {
99421
99540
  ...input.prefetched === undefined ? {} : { prefetched: input.prefetched }
99422
99541
  }, input.larkRunner);
99423
99542
  } catch (error) {
99543
+ if (error instanceof ContextError)
99544
+ throw error;
99424
99545
  const failure2 = normalizeLarkError(error, resolved.sourceName);
99425
99546
  if (failure2.detail?.reason_code === "external.authorization-required" || failure2.detail?.reason_code === "external.tool-failed" && error instanceof LarkCliError && error.exitCode !== 0) {
99426
99547
  throw new ContextError(failure2.code, failure2.message, { ...failure2.detail, reason_code: DOCUMENT_ACQUISITION_UNAVAILABLE, acquisition_cause: failure2.detail?.reason_code });
@@ -99463,9 +99584,9 @@ async function runCaptureLarkPhaseUnlocked(input) {
99463
99584
  locator
99464
99585
  }];
99465
99586
  const manifestPath = sourceManifestPath2(entry);
99466
- const manifestAbsPath = join95(input.projectRoot, manifestPath);
99587
+ const manifestAbsPath = join96(input.projectRoot, manifestPath);
99467
99588
  const materializedAt = entry.materializedAt;
99468
- const materializedAtAbsPath = join95(input.projectRoot, materializedAt);
99589
+ const materializedAtAbsPath = join96(input.projectRoot, materializedAt);
99469
99590
  const manifest = createDocumentSnapshotManifest({
99470
99591
  sourceType: "lark",
99471
99592
  sourceName: resolved.sourceName,
@@ -99499,13 +99620,13 @@ async function runCaptureLarkPhaseUnlocked(input) {
99499
99620
  }));
99500
99621
  try {
99501
99622
  const requestedWrites = [{
99502
- path: join95(materializedAtAbsPath, documentPath),
99623
+ path: join96(materializedAtAbsPath, documentPath),
99503
99624
  bytes: normalized
99504
99625
  }];
99505
99626
  for (const asset of assets) {
99506
99627
  if (asset.bytes !== undefined) {
99507
99628
  requestedWrites.push({
99508
- path: join95(materializedAtAbsPath, asset.entry.path),
99629
+ path: join96(materializedAtAbsPath, asset.entry.path),
99509
99630
  bytes: asset.bytes
99510
99631
  });
99511
99632
  }
@@ -99522,7 +99643,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
99522
99643
  currentPaths: new Set(assets.filter((asset) => asset.bytes !== undefined).map((asset) => asset.entry.path))
99523
99644
  });
99524
99645
  await applyAtomicFileBatch({
99525
- transactionRoot: join95(input.projectRoot, ".tmp", "context-runtime", "capture-transactions"),
99646
+ transactionRoot: join96(input.projectRoot, ".tmp", "context-runtime", "capture-transactions"),
99526
99647
  writes,
99527
99648
  removals
99528
99649
  });
@@ -99594,7 +99715,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
99594
99715
  resource_materialization: fetched.resourceMaterialization,
99595
99716
  diagnostics: [
99596
99717
  ...recovery === undefined ? [] : [recovery.diagnostic],
99597
- ...fetched.identityFallback ? [`info: lark.capture.identity-fallback: user credentials unavailable; captured with ${fetched.accessIdentity} identity`] : [],
99718
+ ...fetched.identityFallback ? [`info: lark.capture.identity-fallback: capture used an identity fallback; document identity: ${fetched.accessIdentity}; embedded reads may use the fallback identity`] : [],
99598
99719
  ...fetched.fidelity.issues.map((issue) => `${issue.severity}: ${issue.code}: ${issue.block_type} × ${issue.count}: ${issue.reason}`),
99599
99720
  ...fetched.resourceMaterialization.items.filter((item) => item.status === "failed" || item.kind === "poll" && item.reason?.includes("absent") === true).map((item) => `${item.status}: lark.capture.resource-${item.status}: ${item.kind}: ${item.reason ?? item.locator}`)
99600
99721
  ],
@@ -99668,7 +99789,7 @@ __export(exports_articleRetirement, {
99668
99789
  retireArticles: () => retireArticles,
99669
99790
  articleRetirementSchema: () => articleRetirementSchema
99670
99791
  });
99671
- import { readFile as readFile81, readdir as readdir23 } from "node:fs/promises";
99792
+ import { readFile as readFile81, readdir as readdir24 } from "node:fs/promises";
99672
99793
  import { posix as posix10 } from "node:path";
99673
99794
  function rebuildInput(revision) {
99674
99795
  return JSON.stringify({ id: `retirement-${revision.slice(7)}`, operation: "rebuild", timing: "priority", targets: [] });
@@ -99801,7 +99922,7 @@ async function retireArticles(input) {
99801
99922
  }
99802
99923
  async function inspectTemplates(directory2) {
99803
99924
  const absolute = await safeProjectTarget(input.projectRoot, directory2);
99804
- const entries2 = await readdir23(absolute, { withFileTypes: true }).catch((error) => {
99925
+ const entries2 = await readdir24(absolute, { withFileTypes: true }).catch((error) => {
99805
99926
  if (error.code === "ENOENT")
99806
99927
  return [];
99807
99928
  throw error;
@@ -99951,8 +100072,8 @@ __export(exports_writeLockRecovery, {
99951
100072
  recoverWriterLock: () => recoverWriterLock,
99952
100073
  inspectWriterLock: () => inspectWriterLock
99953
100074
  });
99954
- import { lstat as lstat10, mkdir as mkdir34, readFile as readFile82, readdir as readdir24, rename as rename8, rmdir as rmdir2 } from "node:fs/promises";
99955
- import { join as join100 } from "node:path";
100075
+ import { lstat as lstat10, mkdir as mkdir34, readFile as readFile82, readdir as readdir25, rename as rename8, rmdir as rmdir2 } from "node:fs/promises";
100076
+ import { join as join101 } from "node:path";
99956
100077
  import { createHash as createHash30, randomUUID as randomUUID7 } from "node:crypto";
99957
100078
  async function inspectWriterLock(root2) {
99958
100079
  const path3 = await safeProjectTarget(root2, lockRelative);
@@ -99999,19 +100120,19 @@ async function recoverWriterLock(input) {
99999
100120
  };
100000
100121
  if (input.plan_digest !== before.digest)
100001
100122
  throw new Error("Writer lock changed; preview recovery again.");
100002
- const path3 = join100(input.projectRoot, lockRelative);
100003
- const guard = join100(path3, ".recovery");
100123
+ const path3 = join101(input.projectRoot, lockRelative);
100124
+ const guard = join101(path3, ".recovery");
100004
100125
  await mkdir34(guard);
100005
100126
  let archived = false;
100006
100127
  try {
100007
100128
  const current2 = await inspectWriterLock(input.projectRoot);
100008
100129
  if (current2?.digest !== before.digest || current2.process_state !== "not-running")
100009
100130
  throw new Error("Writer lock changed or owner resumed; keep the lock.");
100010
- const names = await readdir24(path3);
100131
+ const names = await readdir25(path3);
100011
100132
  if (names.some((name3) => name3 !== "owner.json" && name3 !== ".recovery"))
100012
100133
  throw new Error("Unexpected lock contents; preserve for diagnosis.");
100013
100134
  const archive = `.tmp/context-runtime/locks/recovered-write-${randomUUID7()}.lock`;
100014
- await rename8(path3, join100(input.projectRoot, archive));
100135
+ await rename8(path3, join101(input.projectRoot, archive));
100015
100136
  archived = true;
100016
100137
  return { action: "writer-lock-recovered", archived_lock: archive, next: "context task recover --format json" };
100017
100138
  } finally {
@@ -100035,8 +100156,8 @@ __export(exports_taskRecovery, {
100035
100156
  RECOVERY_COMMAND: () => RECOVERY_COMMAND
100036
100157
  });
100037
100158
  import { existsSync as existsSync26 } from "node:fs";
100038
- import { dirname as dirname41, join as join101 } from "node:path";
100039
- import { lstat as lstat11, readdir as readdir25, readFile as readFile83 } from "node:fs/promises";
100159
+ import { dirname as dirname41, join as join102 } from "node:path";
100160
+ import { lstat as lstat11, readdir as readdir26, readFile as readFile83 } from "node:fs/promises";
100040
100161
  async function recoveryText(root2, path3) {
100041
100162
  const target = await safeProjectTarget(root2, path3);
100042
100163
  try {
@@ -100053,7 +100174,7 @@ async function recoveryJournals(root2) {
100053
100174
  await safeProjectTarget(root2, path3);
100054
100175
  let stat11;
100055
100176
  try {
100056
- stat11 = await lstat11(join101(root2, path3));
100177
+ stat11 = await lstat11(join102(root2, path3));
100057
100178
  } catch (error) {
100058
100179
  if (error.code === "ENOENT")
100059
100180
  return;
@@ -100064,7 +100185,7 @@ async function recoveryJournals(root2) {
100064
100185
  if (stat11.isDirectory()) {
100065
100186
  if (depth > 3)
100066
100187
  throw new TypeError("Unexpected transaction directory depth; preserve it for diagnosis.");
100067
- for (const name3 of (await readdir25(join101(root2, path3))).sort())
100188
+ for (const name3 of (await readdir26(join102(root2, path3))).sort())
100068
100189
  await visit4(`${path3}/${name3}`, depth + 1);
100069
100190
  } else if (stat11.isFile())
100070
100191
  entries2.push({ path: path3, digest: indexerProtocolDigest(await recoveryText(root2, path3)) });
@@ -100076,8 +100197,8 @@ function recoveryResources() {
100076
100197
  try {
100077
100198
  const root2 = dirname41(contextWorkflowProviderPath());
100078
100199
  const resources = {
100079
- skill: join101(root2, "skills/recover-workspace/SKILL.md"),
100080
- issue_template: join101(root2, "resources/templates/recovery-issue.md")
100200
+ skill: join102(root2, "skills/recover-workspace/SKILL.md"),
100201
+ issue_template: join102(root2, "resources/templates/recovery-issue.md")
100081
100202
  };
100082
100203
  if (!Object.values(resources).every((path3) => existsSync26(path3)))
100083
100204
  throw new Error("Recovery resources are absent from this Provider.");
@@ -100163,7 +100284,7 @@ __export(exports_taskLocalSourceAdjustment, {
100163
100284
  adjustLocalRevisionSources: () => adjustLocalRevisionSources
100164
100285
  });
100165
100286
  import { readFile as readFile84 } from "node:fs/promises";
100166
- import { join as join102 } from "node:path";
100287
+ import { join as join103 } from "node:path";
100167
100288
  async function adjustLocalRevisionSources(root2, input) {
100168
100289
  const { readMaintenance: readMaintenance2 } = await Promise.resolve().then(() => (init_maintenanceStorage(), exports_maintenanceStorage));
100169
100290
  if ((await readMaintenance2(root2)).active && await readProductionStage(root2))
@@ -100206,7 +100327,7 @@ async function adjustLocalRevisionSources(root2, input) {
100206
100327
  if (input.refresh && (!current2.refresh_sources || indexerProtocolDigest([...current2.refresh_sources].sort()) !== indexerProtocolDigest([...selected].sort()))) {
100207
100328
  throw new TypeError("No matching acquisition adjustment exists. Run task adjust without refresh first.");
100208
100329
  }
100209
- const raw = await readFile84(join102(root2, await revisionStoragePath(root2)), "utf8");
100330
+ const raw = await readFile84(join103(root2, await revisionStoragePath(root2)), "utf8");
100210
100331
  let next2;
100211
100332
  const discardIds = new Set;
100212
100333
  if (!input.refresh) {
@@ -100309,7 +100430,7 @@ ${input.instruction}` : revision.instruction
100309
100430
  content: content3
100310
100431
  }];
100311
100432
  if (discardIds.size > 0) {
100312
- const ledger = await readFile84(join102(root2, CANDIDATE_LEDGER_FILE), "utf8").catch((error) => {
100433
+ const ledger = await readFile84(join103(root2, CANDIDATE_LEDGER_FILE), "utf8").catch((error) => {
100313
100434
  if (error.code === "ENOENT")
100314
100435
  return;
100315
100436
  throw error;
@@ -100566,7 +100687,7 @@ __export(exports_managedDocumentRename, {
100566
100687
  renameManagedDocument: () => renameManagedDocument
100567
100688
  });
100568
100689
  import { readFile as readFile92 } from "node:fs/promises";
100569
- import { join as join110, posix as posix11 } from "node:path";
100690
+ import { join as join111, posix as posix11 } from "node:path";
100570
100691
  async function optionalText2(path3) {
100571
100692
  try {
100572
100693
  return await readFile92(path3, "utf8");
@@ -100653,7 +100774,7 @@ async function renameManagedDocument(input) {
100653
100774
  if (path3.split("/").includes("..") || path3.startsWith("/"))
100654
100775
  throw new TypeError("Source references contain an unsafe knowledge path; repair it before renaming.");
100655
100776
  await safeProjectTarget(input.projectRoot, path3);
100656
- const before = await optionalText2(join110(input.projectRoot, path3));
100777
+ const before = await optionalText2(join111(input.projectRoot, path3));
100657
100778
  if (before === undefined)
100658
100779
  continue;
100659
100780
  let after;
@@ -100742,7 +100863,7 @@ var init_managedDocumentRename = __esm(() => {
100742
100863
 
100743
100864
  // src/cli.ts
100744
100865
  import { existsSync as existsSync36, realpathSync as realpathSync3 } from "node:fs";
100745
- import { dirname as dirname49, join as join116 } from "node:path";
100866
+ import { dirname as dirname49, join as join117 } from "node:path";
100746
100867
  import { fileURLToPath as fileURLToPath11, pathToFileURL as pathToFileURL3 } from "node:url";
100747
100868
 
100748
100869
  // ../../node_modules/.bun/commander@11.1.0/node_modules/commander/esm.mjs
@@ -106025,14 +106146,14 @@ function compactJsonResult(result, verbose) {
106025
106146
  // src/project/runLog.ts
106026
106147
  import { randomUUID as randomUUID5 } from "node:crypto";
106027
106148
  import { mkdir as mkdir33, writeFile as writeFile26 } from "node:fs/promises";
106028
- import { dirname as dirname39, join as join96 } from "node:path";
106149
+ import { dirname as dirname39, join as join97 } from "node:path";
106029
106150
  var createPhaseRunId = () => {
106030
106151
  const timestamp = new Date().toISOString().replace(/[-:.TZ]/gu, "");
106031
106152
  return `run_${timestamp}_${randomUUID5().slice(0, 8)}`;
106032
106153
  };
106033
106154
  async function writePhaseRunLog(input) {
106034
- const relPath = join96(".tmp", "context-runtime", "runs", `${input.runId}.json`);
106035
- const absPath = join96(input.projectRoot, relPath);
106155
+ const relPath = join97(".tmp", "context-runtime", "runs", `${input.runId}.json`);
106156
+ const absPath = join97(input.projectRoot, relPath);
106036
106157
  await mkdir33(dirname39(absPath), { recursive: true });
106037
106158
  await writeFile26(absPath, `${JSON.stringify({
106038
106159
  run_id: input.runId,
@@ -107132,7 +107253,7 @@ init_reviewApplyIndexer();
107132
107253
  init_approvedKnowledgeSnapshots();
107133
107254
  import { existsSync as existsSync25 } from "node:fs";
107134
107255
  import { readFile as readFile76 } from "node:fs/promises";
107135
- import { join as join97 } from "node:path";
107256
+ import { join as join98 } from "node:path";
107136
107257
 
107137
107258
  // src/project/reviewCandidateAuthority.ts
107138
107259
  init_src2();
@@ -107195,7 +107316,7 @@ async function prepareApprovedPage(input) {
107195
107316
  next: "Refresh the current production or article revision, then reopen Review before approval."
107196
107317
  });
107197
107318
  }
107198
- const relPath = join97("knowledge", input.record.path);
107319
+ const relPath = join98("knowledge", input.record.path);
107199
107320
  const existingView = findApprovedPageForArticleId(input.record.indexer_candidate.artifact_ref, input.approvedPageIndex);
107200
107321
  const previousPath = input.record.approved_revision?.previous_path;
107201
107322
  if (previousPath !== undefined && (!isSafeKnowledgeTargetPath(previousPath.split("/")[0], previousPath) || previousPath.includes("\\")))
@@ -107210,13 +107331,13 @@ async function prepareApprovedPage(input) {
107210
107331
  next: "Resolve the approved page path migration explicitly before approving this candidate."
107211
107332
  });
107212
107333
  }
107213
- const absPath = join97(input.projectRoot, relPath);
107334
+ const absPath = join98(input.projectRoot, relPath);
107214
107335
  const existing = existsSync25(absPath) ? await readFile76(absPath, "utf8") : undefined;
107215
107336
  let previous3;
107216
107337
  if (previousPath !== undefined) {
107217
107338
  if (existing !== undefined || existingView?.relPath !== `knowledge/${previousPath}`)
107218
107339
  throw new TypeError("Page move destination or original identity changed; refresh its revision.");
107219
- previous3 = { path: `knowledge/${previousPath}`, content: await readFile76(join97(input.projectRoot, "knowledge", previousPath), "utf8") };
107340
+ previous3 = { path: `knowledge/${previousPath}`, content: await readFile76(join98(input.projectRoot, "knowledge", previousPath), "utf8") };
107220
107341
  }
107221
107342
  if (input.record.approved_revision !== undefined) {
107222
107343
  const base = previous3?.content ?? existing;
@@ -107263,7 +107384,7 @@ async function prepareApprovedPage(input) {
107263
107384
  }
107264
107385
  async function readProjectFileMaybe(projectRoot, relPath) {
107265
107386
  try {
107266
- return await readFile76(join97(projectRoot, relPath), "utf8");
107387
+ return await readFile76(join98(projectRoot, relPath), "utf8");
107267
107388
  } catch (error) {
107268
107389
  if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT") {
107269
107390
  return;
@@ -107486,7 +107607,7 @@ async function applyReviewDecisions(input) {
107486
107607
  });
107487
107608
  }
107488
107609
  seenApprovedIds.set(approvedRef, row.candidate_id);
107489
- const approvedPath = join97("knowledge", row.path);
107610
+ const approvedPath = join98("knowledge", row.path);
107490
107611
  const previousPathCandidate = seenApprovedPaths.get(knowledgeTargetPathKey(approvedPath));
107491
107612
  if (previousPathCandidate !== undefined) {
107492
107613
  throw new ContextError(ExitCode.UserError, `multiple approved review decisions target the same knowledge path: ${approvedPath}`, {
@@ -107536,7 +107657,7 @@ async function applyReviewDecisions(input) {
107536
107657
  for (const path3 of approvedPageIndex.byRelPath.keys()) {
107537
107658
  if (pagesToWrite.some((page) => page.relPath === path3 || page.previous?.path === path3))
107538
107659
  continue;
107539
- const before = await readFile76(join97(input.projectRoot, path3), "utf8");
107660
+ const before = await readFile76(join98(input.projectRoot, path3), "utf8");
107540
107661
  const local = path3.replace(/^knowledge\//u, "");
107541
107662
  const after = moveKnowledgeLinkTargets2(before, local, local, moved);
107542
107663
  navigationTargets.push(reviewFileTarget({ path: path3, baseContent: before, targetContent: after }));
@@ -107620,12 +107741,12 @@ init_candidateLedger();
107620
107741
 
107621
107742
  // src/project/localHtmlReport.ts
107622
107743
  import { execFile as execFile11 } from "node:child_process";
107623
- import { isAbsolute as isAbsolute17, join as join98 } from "node:path";
107744
+ import { isAbsolute as isAbsolute17, join as join99 } from "node:path";
107624
107745
  import { pathToFileURL as pathToFileURL2 } from "node:url";
107625
107746
  import { promisify as promisify11 } from "node:util";
107626
107747
  var execFileAsync5 = promisify11(execFile11);
107627
107748
  function htmlReportReference(input) {
107628
- const absolutePath = isAbsolute17(input.path) ? input.path : join98(input.projectRoot, input.path);
107749
+ const absolutePath = isAbsolute17(input.path) ? input.path : join99(input.projectRoot, input.path);
107629
107750
  return {
107630
107751
  format: "html",
107631
107752
  path: input.path,
@@ -108649,7 +108770,7 @@ init_maintenanceStorage();
108649
108770
  init_productionFeedback();
108650
108771
  import { randomUUID as randomUUID6 } from "node:crypto";
108651
108772
  import { readFile as readFile79 } from "node:fs/promises";
108652
- import { join as join99 } from "node:path";
108773
+ import { join as join100 } from "node:path";
108653
108774
  async function beginProductionRevision(input) {
108654
108775
  return withProductionFeedback({ operation: "revision" }, () => withProjectWriteLock(input.projectRoot, "production-revision", async () => {
108655
108776
  await recoverDurableMultiFileTransactions(input.projectRoot);
@@ -108688,7 +108809,7 @@ async function beginProductionRevision(input) {
108688
108809
  const formal = approved.byPath.get(path3);
108689
108810
  if (!prior && !formal)
108690
108811
  throw invalid2("Write the current task first; there is no article draft to revise yet.");
108691
- const markdown = prior?.body ?? await readFile79(await safeProjectTarget(input.projectRoot, join99("knowledge", path3)), "utf8");
108812
+ const markdown = prior?.body ?? await readFile79(await safeProjectTarget(input.projectRoot, join100("knowledge", path3)), "utf8");
108692
108813
  const sections = prior?.indexer_candidate.sections.map((section) => ({ id: section.section_key, references: section.references })) ?? formal?.sections;
108693
108814
  const sources = [];
108694
108815
  for (const source2 of owner.sources) {
@@ -109328,7 +109449,7 @@ init_atomicWrite();
109328
109449
  var import_yaml44 = __toESM(require_dist(), 1);
109329
109450
  import { Buffer as Buffer4 } from "node:buffer";
109330
109451
  import { createHash as createHash31 } from "node:crypto";
109331
- import { join as join103 } from "node:path";
109452
+ import { join as join104 } from "node:path";
109332
109453
  var INLINE_LIMIT = 16 * 1024;
109333
109454
  function record4(value) {
109334
109455
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
@@ -109354,8 +109475,8 @@ async function prepareActionCompletionOutput(input) {
109354
109475
  if (production && Buffer4.byteLength(full) <= INLINE_LIMIT)
109355
109476
  return input.result;
109356
109477
  const digest6 = createHash31("sha256").update(full).digest("hex");
109357
- const root2 = join103(input.projectRoot, ".tmp/context-runtime/action-results");
109358
- const resultFile = join103(root2, `${digest6}.json`);
109478
+ const root2 = join104(input.projectRoot, ".tmp/context-runtime/action-results");
109479
+ const resultFile = join104(root2, `${digest6}.json`);
109359
109480
  await atomicWriteFile(resultFile, full);
109360
109481
  if (production)
109361
109482
  return {
@@ -109366,7 +109487,7 @@ async function prepareActionCompletionOutput(input) {
109366
109487
  ...pick(result, ["next", "next_preparation"])
109367
109488
  };
109368
109489
  const next2 = record4(result.next) ?? record4(record4(result.workflow)?.current) ?? record4(record4(result.continuation)?.next);
109369
- const nextFile = next2 === undefined ? undefined : join103(root2, `${digest6}.next.json`);
109490
+ const nextFile = next2 === undefined ? undefined : join104(root2, `${digest6}.next.json`);
109370
109491
  if (nextFile !== undefined)
109371
109492
  await atomicWriteFile(nextFile, serializeActionCompletion(next2, "json"));
109372
109493
  const outcomes = (Array.isArray(result.outcomes) ? result.outcomes : []).map(record4).filter((item) => item !== undefined);
@@ -109771,9 +109892,9 @@ function registerProjectActionCommands(program2) {
109771
109892
  // src/commands/cleanClaudePluginCache.ts
109772
109893
  init_cliFeedback();
109773
109894
  import { existsSync as existsSync27 } from "node:fs";
109774
- import { readdir as readdir26, rm as rm21 } from "node:fs/promises";
109895
+ import { readdir as readdir27, rm as rm22 } from "node:fs/promises";
109775
109896
  import { homedir } from "node:os";
109776
- import { join as join104 } from "node:path";
109897
+ import { join as join105 } from "node:path";
109777
109898
  var ORPHAN_MARKER = ".orphaned_at";
109778
109899
  var CLAUDE_PLUGIN_CACHE_ROOT_ENV = "C4A_CLAUDE_PLUGIN_CACHE_ROOT";
109779
109900
  var CLAUDE_PLUGIN_CACHE_HOME_ENV = "C4A_CLAUDE_PLUGIN_CACHE_HOME";
@@ -109786,40 +109907,40 @@ async function cleanClaudePluginCache(opts = {}) {
109786
109907
  lines.push("· claude plugin cache: missing — nothing to clean");
109787
109908
  return { lines, removed, scanned };
109788
109909
  }
109789
- const marketplaces = await readdir26(cacheRoot, { withFileTypes: true });
109910
+ const marketplaces = await readdir27(cacheRoot, { withFileTypes: true });
109790
109911
  for (const mp of marketplaces) {
109791
109912
  if (!mp.isDirectory())
109792
109913
  continue;
109793
- const mpDir = join104(cacheRoot, mp.name);
109794
- const plugins = await readdir26(mpDir, { withFileTypes: true });
109914
+ const mpDir = join105(cacheRoot, mp.name);
109915
+ const plugins = await readdir27(mpDir, { withFileTypes: true });
109795
109916
  for (const pl of plugins) {
109796
109917
  if (!pl.isDirectory())
109797
109918
  continue;
109798
- const plDir = join104(mpDir, pl.name);
109799
- const versions = await readdir26(plDir, { withFileTypes: true });
109919
+ const plDir = join105(mpDir, pl.name);
109920
+ const versions = await readdir27(plDir, { withFileTypes: true });
109800
109921
  for (const ver of versions) {
109801
109922
  if (!ver.isDirectory())
109802
109923
  continue;
109803
109924
  scanned += 1;
109804
- const verDir = join104(plDir, ver.name);
109805
- const markerPath = join104(verDir, ORPHAN_MARKER);
109925
+ const verDir = join105(plDir, ver.name);
109926
+ const markerPath = join105(verDir, ORPHAN_MARKER);
109806
109927
  if (!existsSync27(markerPath))
109807
109928
  continue;
109808
109929
  const label2 = `${mp.name}/${pl.name}/${ver.name}`;
109809
109930
  if (opts.dryRun) {
109810
109931
  lines.push(` - would remove: ${label2}`);
109811
109932
  } else {
109812
- await rm21(verDir, { recursive: true, force: true });
109933
+ await rm22(verDir, { recursive: true, force: true });
109813
109934
  removed += 1;
109814
109935
  lines.push(` ✓ removed: ${label2}`);
109815
109936
  }
109816
109937
  }
109817
109938
  if (!opts.dryRun && await isEmptyDir(plDir)) {
109818
- await rm21(plDir, { recursive: true, force: true });
109939
+ await rm22(plDir, { recursive: true, force: true });
109819
109940
  }
109820
109941
  }
109821
109942
  if (!opts.dryRun && await isEmptyDir(mpDir)) {
109822
- await rm21(mpDir, { recursive: true, force: true });
109943
+ await rm22(mpDir, { recursive: true, force: true });
109823
109944
  }
109824
109945
  }
109825
109946
  if (scanned === 0) {
@@ -109834,11 +109955,11 @@ function resolveClaudePluginCacheRoot(opts) {
109834
109955
  if (explicitRoot)
109835
109956
  return explicitRoot;
109836
109957
  const home = opts.home ?? process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV] ?? homedir();
109837
- return join104(home, ".claude", "plugins", "cache");
109958
+ return join105(home, ".claude", "plugins", "cache");
109838
109959
  }
109839
109960
  async function isEmptyDir(dir) {
109840
109961
  try {
109841
- const entries2 = await readdir26(dir);
109962
+ const entries2 = await readdir27(dir);
109842
109963
  return entries2.length === 0;
109843
109964
  } catch {
109844
109965
  return false;
@@ -109866,13 +109987,13 @@ init_exitCode();
109866
109987
 
109867
109988
  // src/lib/packageVersion.ts
109868
109989
  import { existsSync as existsSync28, readFileSync as readFileSync9 } from "node:fs";
109869
- import { dirname as dirname42, join as join105 } from "node:path";
109990
+ import { dirname as dirname42, join as join106 } from "node:path";
109870
109991
  import { fileURLToPath as fileURLToPath8 } from "node:url";
109871
109992
  function readPackageVersion() {
109872
109993
  try {
109873
109994
  let dir = dirname42(fileURLToPath8(import.meta.url));
109874
109995
  for (let depth = 0;depth < 8; depth += 1) {
109875
- const packagePath = join105(dir, "package.json");
109996
+ const packagePath = join106(dir, "package.json");
109876
109997
  if (existsSync28(packagePath)) {
109877
109998
  const parsed = JSON.parse(readFileSync9(packagePath, "utf8"));
109878
109999
  if (typeof parsed.version === "string" && parsed.version.length > 0) {
@@ -109912,7 +110033,7 @@ init_repoSourceRegistry();
109912
110033
  init_writeLock();
109913
110034
  import { execFile as execFile12 } from "node:child_process";
109914
110035
  import { existsSync as existsSync29 } from "node:fs";
109915
- import { lstat as lstat12, mkdir as mkdir35, readlink as readlink2, realpath as realpath10, rm as rm22, symlink as symlink4 } from "node:fs/promises";
110036
+ import { lstat as lstat12, mkdir as mkdir35, readlink as readlink2, realpath as realpath10, rm as rm23, symlink as symlink4 } from "node:fs/promises";
109916
110037
  import { basename as basename11, dirname as dirname43, isAbsolute as isAbsolute19, relative as relative26, resolve as resolve34 } from "node:path";
109917
110038
  import { promisify as promisify12 } from "node:util";
109918
110039
  var execFileAsync6 = promisify12(execFile12);
@@ -110102,14 +110223,14 @@ async function cloneCheckout(input) {
110102
110223
  try {
110103
110224
  await execFileAsync6("git", cloneArgs, { encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
110104
110225
  } catch {
110105
- await rm22(target, { recursive: true, force: true });
110226
+ await rm23(target, { recursive: true, force: true });
110106
110227
  try {
110107
110228
  await execFileAsync6("git", ["clone", "--no-checkout", "--depth=1", input.remote, target], {
110108
110229
  encoding: "utf8",
110109
110230
  maxBuffer: 4 * 1024 * 1024
110110
110231
  });
110111
110232
  } catch (error) {
110112
- await rm22(target, { recursive: true, force: true });
110233
+ await rm23(target, { recursive: true, force: true });
110113
110234
  const detail = error !== null && typeof error === "object" && "stderr" in error ? String(error.stderr ?? "").trim() : error instanceof Error ? error.message : String(error);
110114
110235
  throw new ContextError(ExitCode.ExternalToolError, `cannot clone registered repository: ${input.remote}`, {
110115
110236
  category: ErrorCategory.ExternalToolFailed,
@@ -110134,7 +110255,7 @@ async function cloneCheckout(input) {
110134
110255
  await verifyCheckout({ checkout: target, remote: input.remote, ref: input.ref, subpaths: input.subpaths });
110135
110256
  return target;
110136
110257
  } catch (error) {
110137
- await rm22(target, { recursive: true, force: true });
110258
+ await rm23(target, { recursive: true, force: true });
110138
110259
  throw error;
110139
110260
  }
110140
110261
  }
@@ -110150,7 +110271,7 @@ async function bindLocalAlias(input) {
110150
110271
  const actualReal = await realpath10(actual).catch(() => null);
110151
110272
  if (actualReal !== null && actualReal === await realpath10(input.checkout))
110152
110273
  return;
110153
- await rm22(alias);
110274
+ await rm23(alias);
110154
110275
  } else {
110155
110276
  const actual = await realpath10(alias);
110156
110277
  if (actual === await realpath10(input.checkout))
@@ -110259,14 +110380,14 @@ init_src2();
110259
110380
  init_documentCapture();
110260
110381
  import { existsSync as existsSync30 } from "node:fs";
110261
110382
  import { readFile as readFile86 } from "node:fs/promises";
110262
- import { join as join107 } from "node:path";
110383
+ import { join as join108 } from "node:path";
110263
110384
 
110264
110385
  // src/project/sourceCommandViews.ts
110265
110386
  init_documentSiteDetection();
110266
110387
  init_workspace();
110267
110388
  init_documentBatchManifest();
110268
110389
  import { readFile as readFile85 } from "node:fs/promises";
110269
- import { join as join106 } from "node:path";
110390
+ import { join as join107 } from "node:path";
110270
110391
  function repoSourceAgentView(source2) {
110271
110392
  return {
110272
110393
  id: source2.id ?? source2.name,
@@ -110327,7 +110448,7 @@ async function fileSourceAgentViewWithNextAction(input) {
110327
110448
  };
110328
110449
  }
110329
110450
  function documentSourceManifestPath(source2) {
110330
- return source2.snapshot?.manifest ?? join106(source2.materializedAt, "manifest.json");
110451
+ return source2.snapshot?.manifest ?? join107(source2.materializedAt, "manifest.json");
110331
110452
  }
110332
110453
  async function fileSourceDocumentSiteHint(input) {
110333
110454
  const detection = await detectDocumentSiteFiles({
@@ -110337,7 +110458,7 @@ async function fileSourceDocumentSiteHint(input) {
110337
110458
  let snapshotConfigured = false;
110338
110459
  const manifest = documentSourceManifestPath(input.source);
110339
110460
  try {
110340
- const parsed = parseDocumentSnapshotForSource(JSON.parse(await readFile85(join106(input.projectRoot, manifest), "utf8")), input.source.name);
110461
+ const parsed = parseDocumentSnapshotForSource(JSON.parse(await readFile85(join107(input.projectRoot, manifest), "utf8")), input.source.name);
110341
110462
  snapshotConfigured = manifestUsesMdxJsonDocs(parsed);
110342
110463
  } catch {
110343
110464
  snapshotConfigured = false;
@@ -110370,11 +110491,11 @@ async function larkSourceAgentViewWithNextAction(input) {
110370
110491
  init_documentBatchManifest();
110371
110492
  init_documentSnapshotFidelity();
110372
110493
  function documentSourceManifestPath2(source2) {
110373
- return source2.snapshot?.manifest ?? join107(source2.materializedAt, "manifest.json");
110494
+ return source2.snapshot?.manifest ?? join108(source2.materializedAt, "manifest.json");
110374
110495
  }
110375
110496
  async function documentSnapshotState(input) {
110376
110497
  const manifest = documentSourceManifestPath2(input.source);
110377
- const manifestPath = join107(input.projectRoot, manifest);
110498
+ const manifestPath = join108(input.projectRoot, manifest);
110378
110499
  if (!existsSync30(manifestPath)) {
110379
110500
  return {
110380
110501
  snapshotReady: false,
@@ -110453,7 +110574,7 @@ async function documentSnapshotState(input) {
110453
110574
  const missing = [
110454
110575
  ...parsed.files.map((file) => file.path),
110455
110576
  ...(parsed.assets ?? []).filter((asset) => asset.content_hash !== undefined).map((asset) => asset.path)
110456
- ].find((path3) => !existsSync30(join107(input.projectRoot, input.source.materializedAt, path3)));
110577
+ ].find((path3) => !existsSync30(join108(input.projectRoot, input.source.materializedAt, path3)));
110457
110578
  if (missing !== undefined) {
110458
110579
  return {
110459
110580
  snapshotReady: false,
@@ -110555,7 +110676,7 @@ init_exitCode();
110555
110676
  var import_yaml47 = __toESM(require_dist(), 1);
110556
110677
  import { createHash as createHash32 } from "node:crypto";
110557
110678
  import { readFile as readFile87, realpath as realpath11 } from "node:fs/promises";
110558
- import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as join108, relative as relative27, resolve as resolve35 } from "node:path";
110679
+ import { basename as basename12, extname as extname15, isAbsolute as isAbsolute20, join as join109, relative as relative27, resolve as resolve35 } from "node:path";
110559
110680
  init_writeLock();
110560
110681
  var SOURCE_NAME_PATTERN3 = /^[a-z0-9][a-z0-9._-]*$/u;
110561
110682
  function isDateSourceNamespace(value) {
@@ -110651,7 +110772,7 @@ function assertSafeFileInclude(value) {
110651
110772
  }
110652
110773
  async function readRegistryDocument(projectRoot, registryPath2) {
110653
110774
  try {
110654
- const content3 = await readFile87(join108(projectRoot, registryPath2), "utf8");
110775
+ const content3 = await readFile87(join109(projectRoot, registryPath2), "utf8");
110655
110776
  return content3.trim().length === 0 ? { sources: [] } : import_yaml47.default.parse(content3);
110656
110777
  } catch (error) {
110657
110778
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
@@ -110769,7 +110890,7 @@ async function addFileSourceUnlocked(input) {
110769
110890
  const record6 = entry2;
110770
110891
  return record6.name !== input.name && record6.id !== input.name;
110771
110892
  }), nextEntry];
110772
- await atomicWriteFile(join108(input.projectRoot, DEFAULT_FILE_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
110893
+ await atomicWriteFile(join109(input.projectRoot, DEFAULT_FILE_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
110773
110894
  const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
110774
110895
  const entry = updated.files.find((source2) => source2.name === input.name || source2.id === input.name);
110775
110896
  if (entry === undefined) {
@@ -110825,7 +110946,7 @@ async function addLarkSourceUnlocked(input) {
110825
110946
  const record6 = entry2;
110826
110947
  return record6.name !== input.name && record6.id !== input.name;
110827
110948
  }), nextEntry];
110828
- await atomicWriteFile(join108(input.projectRoot, DEFAULT_LARK_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
110949
+ await atomicWriteFile(join109(input.projectRoot, DEFAULT_LARK_SOURCES_REGISTRY_PATH), import_yaml47.default.stringify({ sources: nextSources }));
110829
110950
  const updated = await loadSourcesRegistry({ rootDir: input.projectRoot });
110830
110951
  const entry = updated.larks.find((source2) => source2.name === input.name || source2.id === input.name);
110831
110952
  if (entry === undefined) {
@@ -111029,8 +111150,8 @@ init_writeLock();
111029
111150
  var import_yaml48 = __toESM(require_dist(), 1);
111030
111151
  import { existsSync as existsSync31 } from "node:fs";
111031
111152
  import { createHash as createHash33 } from "node:crypto";
111032
- import { readFile as readFile88, readdir as readdir27, rm as rm23 } from "node:fs/promises";
111033
- import { isAbsolute as isAbsolute21, join as join109, relative as relative28, resolve as resolve36, sep as sep8 } from "node:path";
111153
+ import { readFile as readFile88, readdir as readdir28, rm as rm24 } from "node:fs/promises";
111154
+ import { isAbsolute as isAbsolute21, join as join110, relative as relative28, resolve as resolve36, sep as sep8 } from "node:path";
111034
111155
  function sourceIdentity(source2) {
111035
111156
  if (source2.kind === "source.collection")
111036
111157
  return;
@@ -111062,7 +111183,7 @@ function collectStrings(value, output) {
111062
111183
  }
111063
111184
  }
111064
111185
  async function yamlReferences(input) {
111065
- const absolutePath = join109(input.projectRoot, input.path);
111186
+ const absolutePath = join110(input.projectRoot, input.path);
111066
111187
  if (!existsSync31(absolutePath))
111067
111188
  return false;
111068
111189
  const parsed = import_yaml48.default.parse(await readFile88(absolutePath, "utf8"));
@@ -111191,7 +111312,7 @@ async function registryRemovalWrite(projectRoot, source2) {
111191
111312
  const path3 = registryPath2(source2.type);
111192
111313
  if (path3 === null)
111193
111314
  return;
111194
- const absolutePath = join109(projectRoot, path3);
111315
+ const absolutePath = join110(projectRoot, path3);
111195
111316
  const document4 = existsSync31(absolutePath) ? import_yaml48.default.parse(await readFile88(absolutePath, "utf8")) : { sources: [] };
111196
111317
  return {
111197
111318
  path: absolutePath,
@@ -111210,7 +111331,7 @@ function safeManagedMaterializedPath(projectRoot, source2) {
111210
111331
  return absolute;
111211
111332
  }
111212
111333
  function safeManagedManifestPath(projectRoot, source2) {
111213
- const manifest = source2.manifest ?? join109(source2.materializedAt, "manifest.json");
111334
+ const manifest = source2.manifest ?? join110(source2.materializedAt, "manifest.json");
111214
111335
  if (isAbsolute21(manifest))
111215
111336
  throw unsafeOwnership(source2, manifest);
111216
111337
  const absolute = resolve36(projectRoot, manifest);
@@ -111372,7 +111493,7 @@ function publicRemovalResult(plan, action) {
111372
111493
  };
111373
111494
  }
111374
111495
  async function pruneExtractRuntime(projectRoot, source2) {
111375
- const fingerprintPath = join109(projectRoot, ".tmp/context-runtime/extract/source-fingerprints.json");
111496
+ const fingerprintPath = join110(projectRoot, ".tmp/context-runtime/extract/source-fingerprints.json");
111376
111497
  const removedPhaseIds = new Set;
111377
111498
  if (existsSync31(fingerprintPath)) {
111378
111499
  const parsed = JSON.parse(await readFile88(fingerprintPath, "utf8"));
@@ -111389,14 +111510,14 @@ async function pruneExtractRuntime(projectRoot, source2) {
111389
111510
  await atomicWriteFile(fingerprintPath, `${JSON.stringify({ ...parsed, phases: next2 }, null, 2)}
111390
111511
  `);
111391
111512
  }
111392
- const phaseOwnershipPath = join109(projectRoot, ".tmp/context-runtime/extract/custom-phase-candidates.json");
111513
+ const phaseOwnershipPath = join110(projectRoot, ".tmp/context-runtime/extract/custom-phase-candidates.json");
111393
111514
  if (existsSync31(phaseOwnershipPath) && removedPhaseIds.size > 0) {
111394
111515
  const parsed = JSON.parse(await readFile88(phaseOwnershipPath, "utf8"));
111395
111516
  const phases = Object.fromEntries(Object.entries(parsed.phases ?? {}).filter(([phaseId]) => !removedPhaseIds.has(phaseId)));
111396
111517
  await atomicWriteFile(phaseOwnershipPath, `${JSON.stringify({ ...parsed, phases }, null, 2)}
111397
111518
  `);
111398
111519
  }
111399
- const symbolPath = join109(projectRoot, ".tmp/context-runtime/extract/source-symbols.json");
111520
+ const symbolPath = join110(projectRoot, ".tmp/context-runtime/extract/source-symbols.json");
111400
111521
  if (existsSync31(symbolPath)) {
111401
111522
  const parsed = JSON.parse(await readFile88(symbolPath, "utf8"));
111402
111523
  const symbols = Array.isArray(parsed.symbols) ? parsed.symbols.filter((entry) => entry === null || typeof entry !== "object" || Array.isArray(entry) || entry.source !== source2.name) : [];
@@ -111404,12 +111525,12 @@ async function pruneExtractRuntime(projectRoot, source2) {
111404
111525
  await atomicWriteFile(symbolPath, `${JSON.stringify({ ...parsed, phaseFingerprints, symbols }, null, 2)}
111405
111526
  `);
111406
111527
  }
111407
- const snapshotRoot = join109(projectRoot, ".tmp/context-runtime/extract/candidates");
111528
+ const snapshotRoot = join110(projectRoot, ".tmp/context-runtime/extract/candidates");
111408
111529
  const visit4 = async (directory) => {
111409
111530
  if (!existsSync31(directory))
111410
111531
  return;
111411
- for (const entry of await readdir27(directory, { withFileTypes: true })) {
111412
- const path3 = join109(directory, entry.name);
111532
+ for (const entry of await readdir28(directory, { withFileTypes: true })) {
111533
+ const path3 = join110(directory, entry.name);
111413
111534
  if (entry.isDirectory()) {
111414
111535
  await visit4(path3);
111415
111536
  continue;
@@ -111420,7 +111541,7 @@ async function pruneExtractRuntime(projectRoot, source2) {
111420
111541
  const parsed = JSON.parse(await readFile88(path3, "utf8"));
111421
111542
  const refs = Array.isArray(parsed.source_refs) ? parsed.source_refs : [];
111422
111543
  if (parsed.source === source2.name || refs.some((ref2) => typeof ref2 === "string" && stringReferencesSource(ref2, source2))) {
111423
- await rm23(path3, { force: true });
111544
+ await rm24(path3, { force: true });
111424
111545
  }
111425
111546
  } catch {}
111426
111547
  }
@@ -111459,7 +111580,7 @@ async function removeProjectSource(input) {
111459
111580
  });
111460
111581
  }
111461
111582
  await applyAtomicFileBatch({
111462
- transactionRoot: join109(input.projectRoot, ".tmp", "context-runtime", "source-remove-transactions"),
111583
+ transactionRoot: join110(input.projectRoot, ".tmp", "context-runtime", "source-remove-transactions"),
111463
111584
  writes: [...plan.registryWrite === undefined ? [] : [plan.registryWrite], ...plan.manifestWrite !== undefined ? [plan.manifestWrite] : []],
111464
111585
  removals: plan.absoluteRemovals
111465
111586
  });
@@ -112101,8 +112222,8 @@ init_cliFeedback();
112101
112222
  init_exitCode();
112102
112223
  var import_yaml51 = __toESM(require_dist(), 1);
112103
112224
  import { constants as constants6, existsSync as existsSync32 } from "node:fs";
112104
- import { lstat as lstat13, open as open5, readdir as readdir28 } from "node:fs/promises";
112105
- import { dirname as dirname44, join as join111, resolve as resolve38 } from "node:path";
112225
+ import { lstat as lstat13, open as open5, readdir as readdir29 } from "node:fs/promises";
112226
+ import { dirname as dirname44, join as join112, resolve as resolve38 } from "node:path";
112106
112227
  import { fileURLToPath as fileURLToPath9 } from "node:url";
112107
112228
  function bundledSkillRoot() {
112108
112229
  const directory = dirname44(fileURLToPath9(import.meta.url));
@@ -112129,10 +112250,10 @@ async function listProductionSkills(root2) {
112129
112250
  }
112130
112251
  }
112131
112252
  async function readProductionSkills(root2) {
112132
- const entries2 = (await readdir28(root2, { withFileTypes: true })).filter((entry) => entry.isDirectory()).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
112253
+ const entries2 = (await readdir29(root2, { withFileTypes: true })).filter((entry) => entry.isDirectory()).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
112133
112254
  const skills = [];
112134
112255
  for (const directory of entries2) {
112135
- const entry = join111(root2, directory.name, "SKILL.md");
112256
+ const entry = join112(root2, directory.name, "SKILL.md");
112136
112257
  if (!(await lstat13(entry)).isFile())
112137
112258
  throw new TypeError(`Skill entry must be a regular file: ${entry}`);
112138
112259
  const handle2 = await open5(entry, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
@@ -112540,8 +112661,8 @@ init_cliFeedback();
112540
112661
  init_errors3();
112541
112662
  init_exitCode();
112542
112663
  import { createHash as createHash34 } from "node:crypto";
112543
- import { cp as cp2, lstat as lstat14, mkdir as mkdir36, mkdtemp as mkdtemp5, readFile as readFile94, readdir as readdir29, rename as rename9, rm as rm24, writeFile as writeFile28 } from "node:fs/promises";
112544
- import { dirname as dirname46, join as join112, resolve as resolve40 } from "node:path";
112664
+ import { cp as cp2, lstat as lstat14, mkdir as mkdir36, mkdtemp as mkdtemp5, readFile as readFile94, readdir as readdir30, rename as rename9, rm as rm25, writeFile as writeFile28 } from "node:fs/promises";
112665
+ import { dirname as dirname46, join as join113, resolve as resolve40 } from "node:path";
112545
112666
  var MARKER = ".context-skill-install.json";
112546
112667
  function conflict(path3) {
112547
112668
  throw new ContextError(ExitCode.UserError, `Local skill install conflict: ${path3}`, {
@@ -112569,10 +112690,10 @@ async function checkParents(path3) {
112569
112690
  async function digest7(root2) {
112570
112691
  const hash4 = createHash34("sha256");
112571
112692
  async function visit4(dir, prefix) {
112572
- for (const name3 of (await readdir29(dir)).sort()) {
112693
+ for (const name3 of (await readdir30(dir)).sort()) {
112573
112694
  if (!prefix && name3 === MARKER)
112574
112695
  continue;
112575
- const path3 = join112(dir, name3);
112696
+ const path3 = join113(dir, name3);
112576
112697
  const entry = await lstat14(path3);
112577
112698
  if (entry.isSymbolicLink())
112578
112699
  conflict(path3);
@@ -112592,31 +112713,31 @@ async function installLocalSkills(root2, path3, dryRun, agent) {
112592
112713
  if (!path3.trim())
112593
112714
  conflict("--local requires a non-empty path");
112594
112715
  const targetRoot = resolve40(path3);
112595
- const skillsRoot = join112(targetRoot, "skills");
112716
+ const skillsRoot = join113(targetRoot, "skills");
112596
112717
  await checkParents(skillsRoot);
112597
- const sourceRoot2 = join112(root2, "skills");
112718
+ const sourceRoot2 = join113(root2, "skills");
112598
112719
  const commandPlans = [];
112599
112720
  const commandEntries = new Set;
112600
112721
  if (agent === "claude" || agent === "cursor") {
112601
- for (const name3 of await readdir29(join112(root2, "claude", "commands"))) {
112722
+ for (const name3 of await readdir30(join113(root2, "claude", "commands"))) {
112602
112723
  if (name3.endsWith(".md"))
112603
112724
  commandEntries.add(name3.slice(0, -3));
112604
112725
  }
112605
- const commandsRoot = join112(targetRoot, "commands");
112726
+ const commandsRoot = join113(targetRoot, "commands");
112606
112727
  await checkParents(commandsRoot);
112607
- for (const entry of await readdir29(join112(root2, agent, "commands"), { withFileTypes: true })) {
112728
+ for (const entry of await readdir30(join113(root2, agent, "commands"), { withFileTypes: true })) {
112608
112729
  if (!entry.isFile() || !entry.name.endsWith(".md"))
112609
112730
  continue;
112610
- let content3 = await readFile94(join112(root2, agent, "commands", entry.name), "utf8");
112731
+ let content3 = await readFile94(join113(root2, agent, "commands", entry.name), "utf8");
112611
112732
  if (agent === "claude") {
112612
112733
  const frontmatter2 = /^(---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))/.exec(content3);
112613
112734
  if (!frontmatter2)
112614
- conflict(join112(root2, agent, "commands", entry.name));
112735
+ conflict(join113(root2, agent, "commands", entry.name));
112615
112736
  content3 = `${frontmatter2[1]}disable-model-invocation: true
112616
112737
  ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmatter2[3]}${content3.slice(frontmatter2[0].length)}`;
112617
112738
  }
112618
- const target = join112(commandsRoot, entry.name);
112619
- const marker = join112(commandsRoot, `.context-${entry.name}.json`);
112739
+ const target = join113(commandsRoot, entry.name);
112740
+ const marker = join113(commandsRoot, `.context-${entry.name}.json`);
112620
112741
  for (const file of [target, marker]) {
112621
112742
  const existing = await stat11(file);
112622
112743
  if (existing && (!existing.isFile() || existing.isSymbolicLink()))
@@ -112637,14 +112758,14 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112637
112758
  commandPlans.push({ name: entry.name, target, marker, content: content3, hash: createHash34("sha256").update(content3).digest("hex") });
112638
112759
  }
112639
112760
  if (!commandPlans.length)
112640
- conflict(join112(root2, agent, "commands"));
112761
+ conflict(join113(root2, agent, "commands"));
112641
112762
  }
112642
112763
  const plans = [];
112643
- for (const entry of (await readdir29(sourceRoot2, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
112644
- if (!entry.isDirectory() || !await stat11(join112(sourceRoot2, entry.name, "SKILL.md")))
112764
+ for (const entry of (await readdir30(sourceRoot2, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
112765
+ if (!entry.isDirectory() || !await stat11(join113(sourceRoot2, entry.name, "SKILL.md")))
112645
112766
  continue;
112646
- const source2 = join112(sourceRoot2, entry.name);
112647
- const target = join112(skillsRoot, entry.name);
112767
+ const source2 = join113(sourceRoot2, entry.name);
112768
+ const target = join113(skillsRoot, entry.name);
112648
112769
  if (commandEntries.has(entry.name)) {
112649
112770
  if (await stat11(target))
112650
112771
  conflict(`${target} (use a fresh host directory to avoid duplicate command/skill entries)`);
@@ -112654,7 +112775,7 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112654
112775
  if (existing) {
112655
112776
  if (!existing.isDirectory() || existing.isSymbolicLink())
112656
112777
  conflict(target);
112657
- const markerPath = join112(target, MARKER);
112778
+ const markerPath = join113(target, MARKER);
112658
112779
  if (!(await stat11(markerPath))?.isFile())
112659
112780
  conflict(target);
112660
112781
  let marker;
@@ -112673,14 +112794,14 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112673
112794
  if (!dryRun) {
112674
112795
  await mkdir36(skillsRoot, { recursive: true });
112675
112796
  for (const plan of plans) {
112676
- const staging = await mkdtemp5(join112(skillsRoot, ".context-install-"));
112677
- const candidate = join112(staging, "candidate");
112678
- const previous3 = join112(staging, "previous");
112797
+ const staging = await mkdtemp5(join113(skillsRoot, ".context-install-"));
112798
+ const candidate = join113(staging, "candidate");
112799
+ const previous3 = join113(staging, "previous");
112679
112800
  let moved = false;
112680
112801
  try {
112681
112802
  await cp2(plan.source, candidate, { recursive: true });
112682
112803
  if (agent === "claude") {
112683
- const skillFile = join112(candidate, "SKILL.md");
112804
+ const skillFile = join113(candidate, "SKILL.md");
112684
112805
  const content3 = await readFile94(skillFile, "utf8");
112685
112806
  const parts = /^(---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))/.exec(content3);
112686
112807
  if (parts && /^\s*context-public-entry:\s*["']?false["']?\s*$/mu.test(parts[2])) {
@@ -112689,7 +112810,7 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112689
112810
  ${frontmatter2}${parts[3]}${content3.slice(parts[0].length)}`);
112690
112811
  }
112691
112812
  }
112692
- await writeFile28(join112(candidate, MARKER), JSON.stringify({ owner: "context-plugin-local", digest: await digest7(candidate) }) + `
112813
+ await writeFile28(join113(candidate, MARKER), JSON.stringify({ owner: "context-plugin-local", digest: await digest7(candidate) }) + `
112693
112814
  `);
112694
112815
  if (plan.exists) {
112695
112816
  await rename9(plan.target, previous3);
@@ -112702,7 +112823,7 @@ ${frontmatter2}${parts[3]}${content3.slice(parts[0].length)}`);
112702
112823
  await rename9(previous3, plan.target);
112703
112824
  throw error;
112704
112825
  }
112705
- await rm24(staging, { recursive: true, force: true });
112826
+ await rm25(staging, { recursive: true, force: true });
112706
112827
  } catch (error) {
112707
112828
  throw new ContextError(ExitCode.WorkspaceStateError, `Local skill installation failed: ${plan.target}`, {
112708
112829
  category: ErrorCategory.WorkspaceStateInvalid,
@@ -112713,13 +112834,13 @@ ${frontmatter2}${parts[3]}${content3.slice(parts[0].length)}`);
112713
112834
  }
112714
112835
  for (const command3 of commandPlans) {
112715
112836
  await mkdir36(dirname46(command3.target), { recursive: true });
112716
- const staging = await mkdtemp5(join112(dirname46(command3.target), ".context-install-"));
112717
- await writeFile28(join112(staging, "command"), command3.content);
112718
- await writeFile28(join112(staging, "marker"), JSON.stringify({ owner: "context-plugin-local", digest: command3.hash }) + `
112837
+ const staging = await mkdtemp5(join113(dirname46(command3.target), ".context-install-"));
112838
+ await writeFile28(join113(staging, "command"), command3.content);
112839
+ await writeFile28(join113(staging, "marker"), JSON.stringify({ owner: "context-plugin-local", digest: command3.hash }) + `
112719
112840
  `);
112720
- await rename9(join112(staging, "command"), command3.target);
112721
- await rename9(join112(staging, "marker"), command3.marker);
112722
- await rm24(staging, { recursive: true, force: true });
112841
+ await rename9(join113(staging, "command"), command3.target);
112842
+ await rename9(join113(staging, "marker"), command3.marker);
112843
+ await rm25(staging, { recursive: true, force: true });
112723
112844
  }
112724
112845
  }
112725
112846
  return formatFeedback({
@@ -112742,10 +112863,10 @@ init_cliFeedback();
112742
112863
  init_errors3();
112743
112864
  init_exitCode();
112744
112865
  import { lstat as lstat15 } from "node:fs/promises";
112745
- import { join as join113, resolve as resolve41 } from "node:path";
112866
+ import { join as join114, resolve as resolve41 } from "node:path";
112746
112867
  var HOST_DIRS = { claude: ".claude", cursor: ".cursor", codex: ".agents" };
112747
112868
  async function detectLocalAgent(agent, repo) {
112748
- const path3 = join113(repo, HOST_DIRS[agent]);
112869
+ const path3 = join114(repo, HOST_DIRS[agent]);
112749
112870
  try {
112750
112871
  const entry = await lstat15(path3);
112751
112872
  if (!entry.isDirectory() || entry.isSymbolicLink()) {
@@ -112770,7 +112891,7 @@ async function installAutoLocalSkills(root2, path3, dryRun, detect = detectLocal
112770
112891
  if (selectedAgent ? selectedAgent === "all" || agent === selectedAgent : await detect(agent, repo))
112771
112892
  agents.push(agent);
112772
112893
  }
112773
- const targets = agents.length ? agents.map((agent) => ({ agent, path: join113(repo, HOST_DIRS[agent]) })) : [{ path: join113(repo, ".agents") }];
112894
+ const targets = agents.length ? agents.map((agent) => ({ agent, path: join114(repo, HOST_DIRS[agent]) })) : [{ path: join114(repo, ".agents") }];
112774
112895
  const previews = [];
112775
112896
  for (const target of targets)
112776
112897
  previews.push(await installLocalSkills(root2, target.path, true, target.agent));
@@ -112803,7 +112924,7 @@ init_cliFeedback();
112803
112924
  init_errors3();
112804
112925
  init_exitCode();
112805
112926
  import { existsSync as existsSync35 } from "node:fs";
112806
- import { dirname as dirname48, join as join115, resolve as resolve42 } from "node:path";
112927
+ import { dirname as dirname48, join as join116, resolve as resolve42 } from "node:path";
112807
112928
  import { fileURLToPath as fileURLToPath10 } from "node:url";
112808
112929
 
112809
112930
  // src/project/pluginInstallTargets.ts
@@ -112812,9 +112933,9 @@ init_errors3();
112812
112933
  init_exitCode();
112813
112934
  import { execFile as execFile13 } from "node:child_process";
112814
112935
  import { existsSync as existsSync34 } from "node:fs";
112815
- import { cp as cp3, mkdir as mkdir37, readdir as readdir30, readFile as readFile95, rename as rename10, rm as rm25, writeFile as writeFile29 } from "node:fs/promises";
112936
+ import { cp as cp3, mkdir as mkdir37, readdir as readdir31, readFile as readFile95, rename as rename10, rm as rm26, writeFile as writeFile29 } from "node:fs/promises";
112816
112937
  import { homedir as homedir2 } from "node:os";
112817
- import { dirname as dirname47, join as join114 } from "node:path";
112938
+ import { dirname as dirname47, join as join115 } from "node:path";
112818
112939
  import { promisify as promisify13 } from "node:util";
112819
112940
  var execFileAsync7 = promisify13(execFile13);
112820
112941
  var MARKETPLACE_NAME = "c4a";
@@ -112868,23 +112989,23 @@ async function claudePluginInstalled(pluginId) {
112868
112989
  }
112869
112990
  }
112870
112991
  function codexHome() {
112871
- return process.env.CODEX_HOME?.trim() || join114(homedir2(), ".codex");
112992
+ return process.env.CODEX_HOME?.trim() || join115(homedir2(), ".codex");
112872
112993
  }
112873
112994
  function claudePluginCacheRoot() {
112874
112995
  const explicitRoot = process.env[CLAUDE_PLUGIN_CACHE_ROOT_ENV2]?.trim();
112875
112996
  if (explicitRoot)
112876
112997
  return explicitRoot;
112877
112998
  const home = process.env[CLAUDE_PLUGIN_CACHE_HOME_ENV2]?.trim() || homedir2();
112878
- return join114(home, ".claude", "plugins", "cache");
112999
+ return join115(home, ".claude", "plugins", "cache");
112879
113000
  }
112880
113001
  function sharedSkillsRoot() {
112881
- return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() || join114(homedir2(), ".agents", "skills");
113002
+ return process.env[SHARED_SKILLS_ROOT_ENV]?.trim() || join115(homedir2(), ".agents", "skills");
112882
113003
  }
112883
113004
  function claudeSkillsRoot() {
112884
- return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() || join114(homedir2(), ".claude", "skills");
113005
+ return process.env[CLAUDE_SKILLS_ROOT_ENV]?.trim() || join115(homedir2(), ".claude", "skills");
112885
113006
  }
112886
113007
  function cursorPluginRoot() {
112887
- return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() || join114(homedir2(), ".cursor", "plugins", "local", PLUGIN_NAME);
113008
+ return process.env[CURSOR_PLUGIN_ROOT_ENV]?.trim() || join115(homedir2(), ".cursor", "plugins", "local", PLUGIN_NAME);
112888
113009
  }
112889
113010
  function blockHeader(line) {
112890
113011
  const match = line.match(/^\s*\[([^\]]+)\]\s*$/u);
@@ -112935,7 +113056,7 @@ function pruneLegacyCodexConfigContent(content3) {
112935
113056
  `), removed };
112936
113057
  }
112937
113058
  async function pruneLegacyCodexConfig(dryRun, steps) {
112938
- const configPath = join114(codexHome(), "config.toml");
113059
+ const configPath = join115(codexHome(), "config.toml");
112939
113060
  const current2 = await readFile95(configPath, "utf8").catch(() => "");
112940
113061
  if (!current2)
112941
113062
  return;
@@ -112952,10 +113073,10 @@ async function pruneLegacyCodexConfig(dryRun, steps) {
112952
113073
  }
112953
113074
  }
112954
113075
  async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, steps) {
112955
- const cacheRoot = join114(codexHome(), "plugins", "cache", MARKETPLACE_NAME, pluginName);
113076
+ const cacheRoot = join115(codexHome(), "plugins", "cache", MARKETPLACE_NAME, pluginName);
112956
113077
  if (!existsSync34(cacheRoot))
112957
113078
  return;
112958
- const versions = (await readdir30(cacheRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== keepVersion).map((entry) => entry.name).sort();
113079
+ const versions = (await readdir31(cacheRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== keepVersion).map((entry) => entry.name).sort();
112959
113080
  if (versions.length === 0)
112960
113081
  return;
112961
113082
  steps.push({
@@ -112964,7 +113085,7 @@ async function pruneCodexPluginCacheForName(pluginName, keepVersion, dryRun, ste
112964
113085
  status: dryRun ? "planned" : "ran"
112965
113086
  });
112966
113087
  if (!dryRun)
112967
- await Promise.all(versions.map((version3) => rm25(join114(cacheRoot, version3), { recursive: true, force: true })));
113088
+ await Promise.all(versions.map((version3) => rm26(join115(cacheRoot, version3), { recursive: true, force: true })));
112968
113089
  }
112969
113090
  async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
112970
113091
  await pruneCodexPluginCacheForName(PLUGIN_NAME, currentVersion, dryRun, steps);
@@ -112974,7 +113095,7 @@ async function pruneCodexPluginCache(currentVersion, dryRun, steps) {
112974
113095
  }
112975
113096
  async function isEmptyDir2(dir) {
112976
113097
  try {
112977
- return (await readdir30(dir)).length === 0;
113098
+ return (await readdir31(dir)).length === 0;
112978
113099
  } catch {
112979
113100
  return false;
112980
113101
  }
@@ -112984,31 +113105,31 @@ async function pruneClaudeOrphanContextCache(dryRun, steps) {
112984
113105
  if (!existsSync34(cacheRoot))
112985
113106
  return;
112986
113107
  const removed = [];
112987
- const marketplaces = await readdir30(cacheRoot, { withFileTypes: true }).catch(() => []);
113108
+ const marketplaces = await readdir31(cacheRoot, { withFileTypes: true }).catch(() => []);
112988
113109
  for (const marketplace of marketplaces) {
112989
113110
  if (!marketplace.isDirectory())
112990
113111
  continue;
112991
- const pluginDir = join114(cacheRoot, marketplace.name, PLUGIN_NAME);
113112
+ const pluginDir = join115(cacheRoot, marketplace.name, PLUGIN_NAME);
112992
113113
  if (!existsSync34(pluginDir))
112993
113114
  continue;
112994
- const versions = await readdir30(pluginDir, { withFileTypes: true }).catch(() => []);
113115
+ const versions = await readdir31(pluginDir, { withFileTypes: true }).catch(() => []);
112995
113116
  for (const version3 of versions) {
112996
113117
  if (!version3.isDirectory())
112997
113118
  continue;
112998
- const versionDir = join114(pluginDir, version3.name);
112999
- if (!existsSync34(join114(versionDir, ORPHAN_MARKER2)))
113119
+ const versionDir = join115(pluginDir, version3.name);
113120
+ if (!existsSync34(join115(versionDir, ORPHAN_MARKER2)))
113000
113121
  continue;
113001
113122
  removed.push(`${marketplace.name}/${PLUGIN_NAME}/${version3.name}`);
113002
113123
  if (!dryRun) {
113003
- await rm25(versionDir, { recursive: true, force: true });
113124
+ await rm26(versionDir, { recursive: true, force: true });
113004
113125
  }
113005
113126
  }
113006
113127
  if (!dryRun && await isEmptyDir2(pluginDir)) {
113007
- await rm25(pluginDir, { recursive: true, force: true });
113128
+ await rm26(pluginDir, { recursive: true, force: true });
113008
113129
  }
113009
- const marketplaceDir = join114(cacheRoot, marketplace.name);
113130
+ const marketplaceDir = join115(cacheRoot, marketplace.name);
113010
113131
  if (!dryRun && await isEmptyDir2(marketplaceDir)) {
113011
- await rm25(marketplaceDir, { recursive: true, force: true });
113132
+ await rm26(marketplaceDir, { recursive: true, force: true });
113012
113133
  }
113013
113134
  }
113014
113135
  if (removed.length > 0) {
@@ -113027,12 +113148,12 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
113027
113148
  return;
113028
113149
  const removed = [];
113029
113150
  for (const pluginName of LEGACY_PLUGIN_NAMES) {
113030
- const pluginDir = join114(cacheRoot, MARKETPLACE_NAME, pluginName);
113151
+ const pluginDir = join115(cacheRoot, MARKETPLACE_NAME, pluginName);
113031
113152
  if (!existsSync34(pluginDir))
113032
113153
  continue;
113033
113154
  removed.push(`${MARKETPLACE_NAME}/${pluginName}`);
113034
113155
  if (!dryRun) {
113035
- await rm25(pluginDir, { recursive: true, force: true });
113156
+ await rm26(pluginDir, { recursive: true, force: true });
113036
113157
  }
113037
113158
  }
113038
113159
  if (removed.length > 0) {
@@ -113044,12 +113165,12 @@ async function pruneClaudeLegacyPluginCache(dryRun, steps) {
113044
113165
  }
113045
113166
  }
113046
113167
  async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
113047
- const manifest = await readFile95(join114(root2, "claude", ".claude-plugin", "plugin.json"), "utf8").then((content3) => JSON.parse(content3)).catch(() => null);
113168
+ const manifest = await readFile95(join115(root2, "claude", ".claude-plugin", "plugin.json"), "utf8").then((content3) => JSON.parse(content3)).catch(() => null);
113048
113169
  const currentVersion = typeof manifest?.version === "string" ? manifest.version : null;
113049
113170
  if (!currentVersion)
113050
113171
  return;
113051
- const pluginDir = join114(claudePluginCacheRoot(), MARKETPLACE_NAME, PLUGIN_NAME);
113052
- const staleVersions = (await readdir30(pluginDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== currentVersion).map((entry) => entry.name);
113172
+ const pluginDir = join115(claudePluginCacheRoot(), MARKETPLACE_NAME, PLUGIN_NAME);
113173
+ const staleVersions = (await readdir31(pluginDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory() && entry.name !== currentVersion).map((entry) => entry.name);
113053
113174
  if (staleVersions.length === 0)
113054
113175
  return;
113055
113176
  steps.push({
@@ -113058,7 +113179,7 @@ async function pruneClaudeSupersededContextCache(root2, dryRun, steps) {
113058
113179
  status: dryRun ? "planned" : "ran"
113059
113180
  });
113060
113181
  if (!dryRun) {
113061
- await Promise.all(staleVersions.map((version3) => rm25(join114(pluginDir, version3), { recursive: true, force: true })));
113182
+ await Promise.all(staleVersions.map((version3) => rm26(join115(pluginDir, version3), { recursive: true, force: true })));
113062
113183
  }
113063
113184
  }
113064
113185
  function enableCodexPluginConfig(content3) {
@@ -113122,7 +113243,7 @@ source = ${JSON.stringify(root2)}
113122
113243
  `;
113123
113244
  }
113124
113245
  async function ensureCodexPluginEnabled() {
113125
- const configPath = join114(codexHome(), "config.toml");
113246
+ const configPath = join115(codexHome(), "config.toml");
113126
113247
  await mkdir37(dirname47(configPath), { recursive: true });
113127
113248
  const current2 = await readFile95(configPath, "utf8").catch(() => "");
113128
113249
  const next2 = enableCodexPluginConfig(current2);
@@ -113131,7 +113252,7 @@ async function ensureCodexPluginEnabled() {
113131
113252
  }
113132
113253
  }
113133
113254
  async function ensureCodexLocalMarketplace(root2) {
113134
- const configPath = join114(codexHome(), "config.toml");
113255
+ const configPath = join115(codexHome(), "config.toml");
113135
113256
  await mkdir37(dirname47(configPath), { recursive: true });
113136
113257
  const current2 = await readFile95(configPath, "utf8").catch(() => "");
113137
113258
  const next2 = upsertCodexLocalMarketplaceConfig(current2, root2);
@@ -113140,7 +113261,7 @@ async function ensureCodexLocalMarketplace(root2) {
113140
113261
  }
113141
113262
  }
113142
113263
  async function codexPluginVersion(root2) {
113143
- const manifestPath = join114(root2, "codex", ".codex-plugin", "plugin.json");
113264
+ const manifestPath = join115(root2, "codex", ".codex-plugin", "plugin.json");
113144
113265
  const manifest = JSON.parse(await readFile95(manifestPath, "utf8"));
113145
113266
  if (typeof manifest.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(manifest.version)) {
113146
113267
  throw new Error(`Codex plugin manifest has an invalid version: ${manifestPath}`);
@@ -113148,13 +113269,13 @@ async function codexPluginVersion(root2) {
113148
113269
  return manifest.version;
113149
113270
  }
113150
113271
  function codexPluginCacheDir(version3) {
113151
- return join114(codexHome(), "plugins", "cache", MARKETPLACE_NAME, PLUGIN_NAME, version3);
113272
+ return join115(codexHome(), "plugins", "cache", MARKETPLACE_NAME, PLUGIN_NAME, version3);
113152
113273
  }
113153
113274
  async function replaceDirectoryFromSource(source2, target) {
113154
113275
  await mkdir37(dirname47(target), { recursive: true });
113155
113276
  const temporary = `${target}.tmp-${process.pid}-${Date.now()}`;
113156
113277
  const previous3 = `${target}.previous-${process.pid}-${Date.now()}`;
113157
- await rm25(temporary, { recursive: true, force: true });
113278
+ await rm26(temporary, { recursive: true, force: true });
113158
113279
  await cp3(source2, temporary, { recursive: true, force: true });
113159
113280
  const hadPrevious = existsSync34(target);
113160
113281
  try {
@@ -113162,16 +113283,16 @@ async function replaceDirectoryFromSource(source2, target) {
113162
113283
  await rename10(target, previous3);
113163
113284
  await rename10(temporary, target);
113164
113285
  if (hadPrevious)
113165
- await rm25(previous3, { recursive: true, force: true });
113286
+ await rm26(previous3, { recursive: true, force: true });
113166
113287
  } catch (error) {
113167
- await rm25(temporary, { recursive: true, force: true });
113288
+ await rm26(temporary, { recursive: true, force: true });
113168
113289
  if (hadPrevious && !existsSync34(target) && existsSync34(previous3))
113169
113290
  await rename10(previous3, target);
113170
113291
  throw error;
113171
113292
  }
113172
113293
  }
113173
113294
  async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
113174
- const source2 = join114(root2, "codex");
113295
+ const source2 = join115(root2, "codex");
113175
113296
  const target = codexPluginCacheDir(version3);
113176
113297
  steps.push({
113177
113298
  agent: "codex",
@@ -113183,13 +113304,13 @@ async function materializeCodexPluginCache(root2, version3, dryRun, steps) {
113183
113304
  await replaceDirectoryFromSource(source2, target);
113184
113305
  }
113185
113306
  async function bundledProviderSkillNames(root2) {
113186
- const skillsRoot = join114(root2, "skills");
113187
- const entries2 = await readdir30(skillsRoot, { withFileTypes: true });
113307
+ const skillsRoot = join115(root2, "skills");
113308
+ const entries2 = await readdir31(skillsRoot, { withFileTypes: true });
113188
113309
  const names = [];
113189
113310
  for (const entry of entries2) {
113190
113311
  if (!entry.isDirectory() || entry.name === "context")
113191
113312
  continue;
113192
- const skillPath = join114(skillsRoot, entry.name, "SKILL.md");
113313
+ const skillPath = join115(skillsRoot, entry.name, "SKILL.md");
113193
113314
  if (!existsSync34(skillPath))
113194
113315
  continue;
113195
113316
  const skill = await readFile95(skillPath, "utf8");
@@ -113204,10 +113325,10 @@ async function bundledProviderSkillNames(root2) {
113204
113325
  return names;
113205
113326
  }
113206
113327
  async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps) {
113207
- const sourceRoot2 = join114(root2, "skills");
113328
+ const sourceRoot2 = join115(root2, "skills");
113208
113329
  for (const name3 of await bundledProviderSkillNames(root2)) {
113209
- const source2 = join114(sourceRoot2, name3);
113210
- const target = join114(targetRoot, name3);
113330
+ const source2 = join115(sourceRoot2, name3);
113331
+ const target = join115(targetRoot, name3);
113211
113332
  steps.push({
113212
113333
  agent,
113213
113334
  command: `materialize lifecycle Provider skill: ${shellQuote8(source2)} -> ${shellQuote8(target)}`,
@@ -113218,7 +113339,7 @@ async function materializeProviderSkills(root2, targetRoot, agent, dryRun, steps
113218
113339
  }
113219
113340
  }
113220
113341
  async function installCursor(root2, dryRun, steps) {
113221
- const source2 = join114(root2, "cursor");
113342
+ const source2 = join115(root2, "cursor");
113222
113343
  const target = cursorPluginRoot();
113223
113344
  steps.push({
113224
113345
  agent: "cursor",
@@ -113273,12 +113394,12 @@ async function installCodex(root2, dryRun, steps) {
113273
113394
  steps.push({ agent: "codex", command: commandLine("codex", addArgs), status: dryRun ? "planned" : "ran" });
113274
113395
  steps.push({
113275
113396
  agent: "codex",
113276
- command: `ensure ${shellQuote8(join114(codexHome(), "config.toml"))} registers local marketplace ${shellQuote8(MARKETPLACE_NAME)}`,
113397
+ command: `ensure ${shellQuote8(join115(codexHome(), "config.toml"))} registers local marketplace ${shellQuote8(MARKETPLACE_NAME)}`,
113277
113398
  status: dryRun ? "planned" : "ran"
113278
113399
  });
113279
113400
  steps.push({
113280
113401
  agent: "codex",
113281
- command: `ensure ${shellQuote8(join114(codexHome(), "config.toml"))} enables ${shellQuote8(PLUGIN_ID)}`,
113402
+ command: `ensure ${shellQuote8(join115(codexHome(), "config.toml"))} enables ${shellQuote8(PLUGIN_ID)}`,
113282
113403
  status: dryRun ? "planned" : "ran"
113283
113404
  });
113284
113405
  if (dryRun) {
@@ -113333,13 +113454,13 @@ function pluginRootCandidates() {
113333
113454
  return [resolve42(envRoot)];
113334
113455
  const candidates = [];
113335
113456
  for (const dir of packageCandidateDirs()) {
113336
- candidates.push(join115(dir, "plugins"));
113337
- candidates.push(join115(dir, "dist", "plugins"));
113457
+ candidates.push(join116(dir, "plugins"));
113458
+ candidates.push(join116(dir, "dist", "plugins"));
113338
113459
  }
113339
113460
  return [...new Set(candidates)];
113340
113461
  }
113341
113462
  function isInstallablePluginRoot(root2) {
113342
- return existsSync35(join115(root2, ".claude-plugin", "marketplace.json")) && existsSync35(join115(root2, ".agents", "plugins", "marketplace.json")) && existsSync35(join115(root2, "claude", ".claude-plugin", "plugin.json")) && existsSync35(join115(root2, "codex", ".codex-plugin", "plugin.json")) && existsSync35(join115(root2, "cursor", ".cursor-plugin", "plugin.json")) && existsSync35(join115(root2, "skills"));
113463
+ return existsSync35(join116(root2, ".claude-plugin", "marketplace.json")) && existsSync35(join116(root2, ".agents", "plugins", "marketplace.json")) && existsSync35(join116(root2, "claude", ".claude-plugin", "plugin.json")) && existsSync35(join116(root2, "codex", ".codex-plugin", "plugin.json")) && existsSync35(join116(root2, "cursor", ".cursor-plugin", "plugin.json")) && existsSync35(join116(root2, "skills"));
113343
113464
  }
113344
113465
  function resolveBundledPluginsRoot() {
113345
113466
  const candidates = pluginRootCandidates();
@@ -113586,10 +113707,10 @@ function readQuickstartPath() {
113586
113707
  try {
113587
113708
  let dir = dirname49(fileURLToPath11(import.meta.url));
113588
113709
  for (let i2 = 0;i2 < 8; i2++) {
113589
- const candidate = join116(dir, "docs", "quickstart.md");
113710
+ const candidate = join117(dir, "docs", "quickstart.md");
113590
113711
  if (existsSync36(candidate))
113591
113712
  return candidate;
113592
- const pkg = join116(dir, "package.json");
113713
+ const pkg = join117(dir, "package.json");
113593
113714
  if (existsSync36(pkg))
113594
113715
  return candidate;
113595
113716
  const parent = dirname49(dir);
@@ -113598,7 +113719,7 @@ function readQuickstartPath() {
113598
113719
  dir = parent;
113599
113720
  }
113600
113721
  } catch {}
113601
- return join116(dirname49(fileURLToPath11(import.meta.url)), "docs", "quickstart.md");
113722
+ return join117(dirname49(fileURLToPath11(import.meta.url)), "docs", "quickstart.md");
113602
113723
  }
113603
113724
  var GREEN = "\x1B[32m";
113604
113725
  var RESET = "\x1B[0m";