@c4a/context-cli 0.7.26 → 0.7.30

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.
Files changed (29) hide show
  1. package/README.md +12 -0
  2. package/README.zh-CN.md +8 -0
  3. package/cli.js +148 -51
  4. package/indexers/contracts/profile-contract.json +245 -245
  5. package/indexers/release-manifest.json +1 -1
  6. package/package.json +12 -12
  7. package/parserEntryWorker.js +90 -10
  8. package/plugins/README.md +1 -1
  9. package/plugins/README_CN.md +1 -1
  10. package/plugins/VERSION +1 -1
  11. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  12. package/plugins/claude/commands/context-inspect-search.md +98 -25
  13. package/plugins/claude/skills/context-inspect-search/SKILL.md +98 -25
  14. package/plugins/codex/.codex-plugin/plugin.json +2 -2
  15. package/plugins/codex/skills/context-inspect-search/SKILL.md +98 -25
  16. package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
  17. package/plugins/cursor/commands/c4a-context-inspect-search.md +98 -25
  18. package/plugins/cursor/skills/context-inspect-search/SKILL.md +98 -25
  19. package/plugins/skills/context-inspect-search/SKILL.md +98 -25
  20. package/providers/context/graphs/indexer.yaml +4 -0
  21. package/providers/context/manifest.json +25 -16
  22. package/providers/context/provider.yaml +1 -1
  23. package/providers/context/resources/dialogue/document-capture.md +8 -0
  24. package/providers/context/resources/manuals/guides/lark-resources.md +17 -1
  25. package/providers/context/resources/procedures/document-capture.md +88 -14
  26. package/providers/context/resources/procedures/document-code-investigation.md +45 -0
  27. package/providers/context/resources/procedures/repository-source-recovery.md +8 -1
  28. package/providers/context/resources/procedures/source-capture-detailed.md +6 -0
  29. package/providers/context/skills/work-production-stage/SKILL.md +4 -0
package/README.md CHANGED
@@ -283,6 +283,18 @@ MIT.
283
283
 
284
284
  ## Updating an existing workspace
285
285
 
286
+ ### Lark read identity
287
+
288
+ `CONTEXT_LARK_IDENTITY=user|bot` selects the identity for Lark acquisition
289
+ (default: `user`). Export it in each shell executing Context commands. In Bot
290
+ mode, a credential/scope/access failure retries once as the current user; the
291
+ rest of that capture keeps the fallback identity for pagination and resources.
292
+ Network, rate-limit and parsing errors do not trigger a user authorization
293
+ attempt. This does not change source-read consent or the audience of published
294
+ knowledge. Host-managed OAuth must use the host's authorization flow.
295
+
296
+ ### Workspace updates
297
+
286
298
  Start with `context entry --format json` and read the returned update guide when
287
299
  adding material or changing published knowledge. Entry and status only observe;
288
300
  they do not resume an earlier managed task before the new request is understood.
package/README.zh-CN.md CHANGED
@@ -47,6 +47,14 @@ Provider Skill 投影到 Claude、Codex 和 Cursor 的用户技能目录。
47
47
  方法并停止。它不会为每次调用增加安装预检,也不会把普通工作流中的 `not found`
48
48
  诊断误判成可执行文件缺失。
49
49
 
50
+ ## 飞书读取身份
51
+
52
+ `CONTEXT_LARK_IDENTITY=user|bot` 控制飞书来源采集,默认 `user`。每次执行 Context
53
+ 命令的 shell 都需传入该环境变量;仅在提示词中写配置并不会自动导出环境变量。
54
+ `bot` 模式先使用应用身份,遇到凭证、scope 或访问权限错误时回退当前用户一次,
55
+ 本次采集后续分页、引用与资源下载沿用回退身份。网络、限流和解析错误不切换身份。
56
+ 该配置不扩大来源授权或知识产物可见范围;托管运行时的用户授权遵循宿主流程。
57
+
50
58
  ## 一个 Agent 入口如何驱动整套工作流
51
59
 
52
60
  ```text
package/cli.js CHANGED
@@ -76780,22 +76780,14 @@ var init_indexerSourceBoundary = __esm(() => {
76780
76780
 
76781
76781
  // src/project/indexerParserSourceMaterialization.ts
76782
76782
  import { execFile as execFile3 } from "node:child_process";
76783
- import { lstat as lstat4, readFile as readFile18 } from "node:fs/promises";
76783
+ import { lstat as lstat4, readFile as readFile18, stat as stat6 } from "node:fs/promises";
76784
76784
  import { promisify as promisify3 } from "node:util";
76785
76785
  import { basename as basename4, extname as extname8, join as join17 } from "node:path";
76786
76786
  function sourceRefName(sourceRef) {
76787
76787
  return sourceRef.startsWith("repo:") ? sourceRef.slice("repo:".length) : null;
76788
76788
  }
76789
76789
  async function trackedPaths(root, ref2) {
76790
- try {
76791
- await execFileAsync2("git", ["diff", "--quiet", ref2, "--", "."], { cwd: root });
76792
- } catch (error) {
76793
- const exitCode = error !== null && typeof error === "object" && "code" in error ? error.code : null;
76794
- if (exitCode === 1) {
76795
- throw new TypeError(`materialized repository source differs from its pinned ref: ${root}`);
76796
- }
76797
- throw error;
76798
- }
76790
+ await assertPinnedSource(root, ref2);
76799
76791
  const { stdout } = await execFileAsync2("git", ["ls-files", "-z", "--", "."], {
76800
76792
  cwd: root,
76801
76793
  encoding: "buffer",
@@ -76804,6 +76796,20 @@ async function trackedPaths(root, ref2) {
76804
76796
  return Buffer.from(stdout).toString("utf8").split("\x00").filter(Boolean).sort();
76805
76797
  }
76806
76798
  async function assertPinnedSource(root, ref2) {
76799
+ const sourceStat = await stat6(root).catch((error) => {
76800
+ if (error.code === "ENOENT" || error.code === "ENOTDIR")
76801
+ return;
76802
+ throw error;
76803
+ });
76804
+ if (!sourceStat?.isDirectory()) {
76805
+ throw new ContextError(ExitCode.WorkspaceStateError, `Repository source directory is unavailable: ${root}. Inspect recovery with context source recovery-plan --format json.`, {
76806
+ category: ErrorCategory.WorkspaceStateInvalid,
76807
+ reason_code: "repository-source-directory-unavailable",
76808
+ cwd: root,
76809
+ next_action: { command: "context source recovery-plan --format json" },
76810
+ input_schema: { type: "object", properties: {}, additionalProperties: false }
76811
+ });
76812
+ }
76807
76813
  try {
76808
76814
  await execFileAsync2("git", ["diff", "--quiet", ref2, "--", "."], { cwd: root });
76809
76815
  } catch (error) {
@@ -76811,6 +76817,15 @@ async function assertPinnedSource(root, ref2) {
76811
76817
  if (exitCode === 1) {
76812
76818
  throw new TypeError(`materialized repository source differs from its pinned ref: ${root}`);
76813
76819
  }
76820
+ if (exitCode === "ENOENT") {
76821
+ throw new ContextError(ExitCode.ExternalToolError, `Git could not start for repository source ${root}. Check git --version and the source directory in the same host environment.`, {
76822
+ category: ErrorCategory.ExternalToolFailed,
76823
+ reason_code: "repository-git-spawn-unavailable",
76824
+ cwd: root,
76825
+ next_action: { command: "git --version" },
76826
+ input_schema: { type: "object", properties: {}, additionalProperties: false }
76827
+ });
76828
+ }
76814
76829
  throw error;
76815
76830
  }
76816
76831
  }
@@ -76874,6 +76889,9 @@ var init_indexerParserSourceMaterialization = __esm(() => {
76874
76889
  init_src2();
76875
76890
  init_indexerReadScopeAuthorization();
76876
76891
  init_indexerSourceBoundary();
76892
+ init_errors3();
76893
+ init_exitCode();
76894
+ init_cliFeedback();
76877
76895
  execFileAsync2 = promisify3(execFile3);
76878
76896
  });
76879
76897
 
@@ -76882,7 +76900,7 @@ var exports_managedDocumentSnapshot = {};
76882
76900
  __export(exports_managedDocumentSnapshot, {
76883
76901
  readManagedDocumentSnapshot: () => readManagedDocumentSnapshot
76884
76902
  });
76885
- import { readFile as readFile19, stat as stat6 } from "node:fs/promises";
76903
+ import { readFile as readFile19, stat as stat7 } from "node:fs/promises";
76886
76904
  import { basename as basename5, dirname as dirname15 } from "node:path";
76887
76905
  async function readManagedDocumentSnapshot(projectRoot, type, name2) {
76888
76906
  const absolutePath = await assertManagedDocumentPath(projectRoot, type, name2);
@@ -76891,7 +76909,7 @@ async function readManagedDocumentSnapshot(projectRoot, type, name2) {
76891
76909
  const manifest = createDocumentSnapshotManifest({
76892
76910
  sourceType: type,
76893
76911
  sourceName: name2,
76894
- capturedAt: (await stat6(absolutePath)).mtime.toISOString(),
76912
+ capturedAt: (await stat7(absolutePath)).mtime.toISOString(),
76895
76913
  files: [{
76896
76914
  path: file,
76897
76915
  bytes: markdown,
@@ -79423,7 +79441,7 @@ __export(exports_productionStageStore, {
79423
79441
  PRODUCTION_STAGES_ROOT: () => PRODUCTION_STAGES_ROOT
79424
79442
  });
79425
79443
  import { constants as constants4 } from "node:fs";
79426
- import { readFile as readFile28, access as access2, stat as stat7 } from "node:fs/promises";
79444
+ import { readFile as readFile28, access as access2, stat as stat8 } from "node:fs/promises";
79427
79445
  import { join as join31 } from "node:path";
79428
79446
  function productionStageDirectory(id3) {
79429
79447
  productionAgentDirectory(id3);
@@ -79442,7 +79460,7 @@ async function readOptional(root, path2) {
79442
79460
  async function hasReadableProjection(root, path2) {
79443
79461
  const absolute = await safeProjectTarget(root, path2);
79444
79462
  try {
79445
- if (!(await stat7(absolute)).isFile())
79463
+ if (!(await stat8(absolute)).isFile())
79446
79464
  throw new TypeError(`Stage material must be a regular file: ${path2}`);
79447
79465
  await access2(absolute, constants4.R_OK);
79448
79466
  return true;
@@ -81693,19 +81711,19 @@ function failure(reason, message, next) {
81693
81711
  }
81694
81712
  async function entries(root, path2) {
81695
81713
  await safeProjectTarget(root, path2);
81696
- let stat8;
81714
+ let stat9;
81697
81715
  try {
81698
- stat8 = await lstat7(join43(root, path2));
81716
+ stat9 = await lstat7(join43(root, path2));
81699
81717
  } catch (error) {
81700
81718
  if (error.code === "ENOENT")
81701
81719
  return [];
81702
81720
  throw error;
81703
81721
  }
81704
- if (stat8.isSymbolicLink())
81722
+ if (stat9.isSymbolicLink())
81705
81723
  failure("preparation-symlink", `Refusing to clean linked task state: ${path2}`, "Inspect and relocate the link without deleting its target, then preview again.");
81706
- if (stat8.isFile())
81724
+ if (stat9.isFile())
81707
81725
  return [path2];
81708
- if (!stat8.isDirectory())
81726
+ if (!stat9.isDirectory())
81709
81727
  failure("preparation-file-type", `Unsupported task-state file: ${path2}`, "Inspect this file before preparing the workspace.");
81710
81728
  const result = [];
81711
81729
  for (const name3 of (await readdir10(join43(root, path2))).sort())
@@ -86351,7 +86369,7 @@ var init_packageSiteExtensions = __esm(() => {
86351
86369
  // src/project/packageSite.ts
86352
86370
  import { createHash as createHash17 } from "node:crypto";
86353
86371
  import { spawn as spawn3 } from "node:child_process";
86354
- import { mkdir as mkdir23, mkdtemp as mkdtemp2, readFile as readFile52, writeFile as writeFile19, rm as rm12, symlink as symlink2, cp, access as access5, stat as stat8 } from "node:fs/promises";
86372
+ import { mkdir as mkdir23, mkdtemp as mkdtemp2, readFile as readFile52, writeFile as writeFile19, rm as rm12, symlink as symlink2, cp, access as access5, stat as stat9 } from "node:fs/promises";
86355
86373
  import { createRequire as createRequire5 } from "node:module";
86356
86374
  import { dirname as dirname28, join as join64, posix as posix6 } from "node:path";
86357
86375
  function sitePagePath(identity) {
@@ -86675,7 +86693,7 @@ title: LLM Docs
86675
86693
  const placeholder = [".css", ".js", ".mjs", ".cjs"].includes(extension2) ? `/* Intentionally empty. */
86676
86694
  ` : [".html", ".htm"].includes(extension2) ? `<!-- Intentionally empty. -->
86677
86695
  ` : null;
86678
- if (placeholder !== null && (await stat8(file.absPath)).size === 0) {
86696
+ if (placeholder !== null && (await stat9(file.absPath)).size === 0) {
86679
86697
  await writeFile19(file.absPath, placeholder);
86680
86698
  }
86681
86699
  }
@@ -89996,7 +90014,7 @@ var init_sourceInputMutation = __esm(() => {
89996
90014
 
89997
90015
  // src/project/documentCapture.ts
89998
90016
  import { createHash as createHash21 } from "node:crypto";
89999
- import { mkdir as mkdir28, readdir as readdir18, readFile as readFile63, rm as rm17, stat as stat9, writeFile as writeFile23 } from "node:fs/promises";
90017
+ import { mkdir as mkdir28, readdir as readdir18, readFile as readFile63, rm as rm17, stat as stat10, writeFile as writeFile23 } from "node:fs/promises";
90000
90018
  import { basename as basename8, dirname as dirname34, extname as extname12, join as join75, relative as relative22, resolve as resolve24 } from "node:path";
90001
90019
  function toPosixPath7(path2) {
90002
90020
  return path2.split(/[\\/]+/u).filter((part) => part.length > 0).join("/");
@@ -90101,7 +90119,7 @@ function assignFlatSnapshotPaths(files, module) {
90101
90119
  });
90102
90120
  }
90103
90121
  async function walkMarkdownFiles(input) {
90104
- const rootStat = await stat9(input.localRoot);
90122
+ const rootStat = await stat10(input.localRoot);
90105
90123
  if (rootStat.isFile()) {
90106
90124
  const sourcePath = normalizeSnapshotRelativePath(basename8(input.localRoot));
90107
90125
  return {
@@ -98636,6 +98654,72 @@ var init_larkMarkdownImages = __esm(() => {
98636
98654
  init_markdownLinks();
98637
98655
  });
98638
98656
 
98657
+ // src/lib/larkReadIdentity.ts
98658
+ function resolveLarkReadIdentity() {
98659
+ const value = process.env.CONTEXT_LARK_IDENTITY?.trim() || "user";
98660
+ if (value === "user" || value === "bot")
98661
+ return value;
98662
+ throw new ContextError(ExitCode.UserError, "CONTEXT_LARK_IDENTITY must be user or bot", {
98663
+ reason_code: "lark.invalid-read-identity",
98664
+ valid_values: ["user", "bot"],
98665
+ next: "Set CONTEXT_LARK_IDENTITY to user or bot and rerun the same command"
98666
+ });
98667
+ }
98668
+ function credentialFailure(result) {
98669
+ for (const output of [result.stdout, result.stderr]) {
98670
+ const start2 = output.indexOf("{");
98671
+ if (start2 < 0)
98672
+ continue;
98673
+ try {
98674
+ const parsed = JSON.parse(output.slice(start2));
98675
+ if (!parsed || typeof parsed !== "object" || !("error" in parsed))
98676
+ continue;
98677
+ if (result.exitCode === 0 && (!("ok" in parsed) || parsed.ok !== false))
98678
+ continue;
98679
+ const error = parsed.error;
98680
+ if (!error || typeof error !== "object")
98681
+ continue;
98682
+ const type = "type" in error ? error.type : undefined;
98683
+ const subtype = "subtype" in error ? error.subtype : undefined;
98684
+ if (type === "authorization" || type === "authentication" || ["missing_scope", "permission_denied", "access_denied", "need_user_authorization"].includes(String(subtype))) {
98685
+ return true;
98686
+ }
98687
+ } catch {}
98688
+ }
98689
+ return result.exitCode !== 0 && /reason=(?:bot_unavailable|not_authorized)\b|\b(?:missing_scope|permission_denied|access_denied)\b/iu.test(result.stderr);
98690
+ }
98691
+ function createLarkReadSession(runner2, initial, fallback = initial === "bot") {
98692
+ let identity2 = initial;
98693
+ let usedFallback = false;
98694
+ const run = async (args, options) => {
98695
+ const explicit = args.indexOf("--as");
98696
+ const requested = explicit >= 0 ? args[explicit + 1] : identity2;
98697
+ const selected = usedFallback ? "user" : requested === "bot" ? "bot" : "user";
98698
+ const withIdentity = [...args];
98699
+ if (explicit >= 0)
98700
+ withIdentity[explicit + 1] = selected;
98701
+ else
98702
+ withIdentity.push("--as", selected);
98703
+ const result = await runner2(withIdentity, options);
98704
+ identity2 = selected;
98705
+ if (identity2 !== "bot" || !fallback || usedFallback || args.includes("--help") || !credentialFailure(result))
98706
+ return result;
98707
+ usedFallback = true;
98708
+ identity2 = "user";
98709
+ withIdentity[withIdentity.indexOf("--as") + 1] = "user";
98710
+ return runner2(withIdentity, options);
98711
+ };
98712
+ return { run, get identity() {
98713
+ return identity2;
98714
+ }, get usedFallback() {
98715
+ return usedFallback;
98716
+ } };
98717
+ }
98718
+ var init_larkReadIdentity = __esm(() => {
98719
+ init_errors3();
98720
+ init_exitCode();
98721
+ });
98722
+
98639
98723
  // src/lib/feishu.ts
98640
98724
  import { spawn as spawn4 } from "node:child_process";
98641
98725
  async function detectDocsFetchCapabilities(runner2) {
@@ -98652,14 +98736,19 @@ ${result.stderr}`;
98652
98736
  supportsDocFormat: result.exitCode === 0 && /\s--doc-format\b/u.test(help)
98653
98737
  };
98654
98738
  }
98655
- function resolveDocsFetchPlan(requested, runner2) {
98739
+ function resolveDocsFetchPlan(requested, runner2, cacheKey = runner2, identity2 = "user") {
98656
98740
  if (requested === "v1" || requested === "v2") {
98657
98741
  return Promise.resolve({ apiVersion: requested, docFormat: "xml" });
98658
98742
  }
98659
- let capabilitiesPromise = docsFetchCapabilitiesCache.get(runner2);
98743
+ let cached = docsFetchCapabilitiesCache.get(cacheKey);
98744
+ if (!cached) {
98745
+ cached = new Map;
98746
+ docsFetchCapabilitiesCache.set(cacheKey, cached);
98747
+ }
98748
+ let capabilitiesPromise = cached.get(identity2);
98660
98749
  if (capabilitiesPromise === undefined) {
98661
98750
  capabilitiesPromise = detectDocsFetchCapabilities(runner2);
98662
- docsFetchCapabilitiesCache.set(runner2, capabilitiesPromise);
98751
+ cached.set(identity2, capabilitiesPromise);
98663
98752
  }
98664
98753
  return capabilitiesPromise.then((capabilities) => {
98665
98754
  if (!capabilities.supportsDocFormat) {
@@ -98956,20 +99045,25 @@ function hasEmptySubPageList(projection) {
98956
99045
  return projection.fidelity.issues.some((issue) => issue.code === LARK_EMPTY_SUB_PAGE_LIST_CODE);
98957
99046
  }
98958
99047
  async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
98959
- const docsFetchPlan = input.prefetched ? { apiVersion: "v2", docFormat: "xml" } : await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2);
99048
+ const preference = input.prefetched?.identity ?? input.identity ?? resolveLarkReadIdentity();
99049
+ const cacheKey = runner2;
99050
+ const session = createLarkReadSession(runner2, input.prefetched?.identity ?? (preference === "bot" ? "bot" : "user"), !input.prefetched && preference === "bot");
99051
+ runner2 = session.run;
99052
+ const docsFetchPlan = input.prefetched ? { apiVersion: "v2", docFormat: "xml" } : await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2, cacheKey, preference);
98960
99053
  const identityFetch = input.prefetched ? {
98961
99054
  fetched: await fetchDocsResponse(input, docsFetchPlan, runner2, input.prefetched.identity),
98962
99055
  identity: input.prefetched.identity,
98963
99056
  fallback: false
98964
- } : await fetchWithIdentity(input, docsFetchPlan, runner2);
99057
+ } : await fetchWithIdentity({ ...input, identity: preference }, docsFetchPlan, runner2);
98965
99058
  let fetched = identityFetch.fetched;
98966
- const accessIdentity = identityFetch.identity;
99059
+ let accessIdentity = input.prefetched?.identity ?? session.identity;
98967
99060
  let projection;
98968
99061
  for (let attempt = 0;attempt < MAX_STRUCTURAL_FETCH_ATTEMPTS && fetched.contentFormat === "xml"; attempt++) {
98969
99062
  projection = projectLarkDocxXml({ xml: fetched.body, sourceUrl: input.url });
98970
99063
  if (input.prefetched || !hasEmptySubPageList(projection) || attempt === MAX_STRUCTURAL_FETCH_ATTEMPTS - 1)
98971
99064
  break;
98972
99065
  fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
99066
+ accessIdentity = session.identity;
98973
99067
  }
98974
99068
  let body2 = fetched.body;
98975
99069
  let title2 = fetched.title;
@@ -99055,7 +99149,7 @@ ${body2}`,
99055
99149
  fidelity,
99056
99150
  resourceMaterialization,
99057
99151
  accessIdentity,
99058
- identityFallback: identityFetch.fallback
99152
+ identityFallback: identityFetch.fallback || session.usedFallback
99059
99153
  };
99060
99154
  }
99061
99155
  return {
@@ -99066,7 +99160,7 @@ ${body2}`,
99066
99160
  fidelity,
99067
99161
  resourceMaterialization,
99068
99162
  accessIdentity,
99069
- identityFallback: identityFetch.fallback
99163
+ identityFallback: identityFetch.fallback || session.usedFallback
99070
99164
  };
99071
99165
  }
99072
99166
  var LARK_BIN = "lark-cli", MAX_FETCH_PAGES = 50, MAX_STRUCTURAL_FETCH_ATTEMPTS = 2, LarkCliNotInstalledError, LarkCliError, defaultRunner = (args, options) => new Promise((resolve8, reject) => {
@@ -99098,6 +99192,7 @@ var init_feishu = __esm(() => {
99098
99192
  init_larkResourceMaterialization();
99099
99193
  init_larkCaptureReport();
99100
99194
  init_larkMarkdownImages();
99195
+ init_larkReadIdentity();
99101
99196
  LarkCliNotInstalledError = class LarkCliNotInstalledError extends Error {
99102
99197
  constructor() {
99103
99198
  super(`${LARK_BIN} not installed — see https://github.com/larksuite/cli for install instructions`);
@@ -99258,7 +99353,7 @@ function larkErrorRecovery(error, sourceName) {
99258
99353
  if (/auth|login|permission|forbidden|unauthori[sz]ed|scope/iu.test(message)) {
99259
99354
  return {
99260
99355
  reasonCode: "external.authorization-required",
99261
- next: "Run lark-cli auth login with an account that can read the document, then rerun capture"
99356
+ 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"
99262
99357
  };
99263
99358
  }
99264
99359
  return {
@@ -99403,6 +99498,8 @@ async function runCaptureLarkPhaseUnlocked(input) {
99403
99498
  ...input.prefetched === undefined ? {} : { prefetched: input.prefetched }
99404
99499
  }, input.larkRunner);
99405
99500
  } catch (error) {
99501
+ if (error instanceof ContextError)
99502
+ throw error;
99406
99503
  const failure2 = normalizeLarkError(error, resolved.sourceName);
99407
99504
  if (failure2.detail?.reason_code === "external.authorization-required" || failure2.detail?.reason_code === "external.tool-failed" && error instanceof LarkCliError && error.exitCode !== 0) {
99408
99505
  throw new ContextError(failure2.code, failure2.message, { ...failure2.detail, reason_code: DOCUMENT_ACQUISITION_UNAVAILABLE, acquisition_cause: failure2.detail?.reason_code });
@@ -99576,7 +99673,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
99576
99673
  resource_materialization: fetched.resourceMaterialization,
99577
99674
  diagnostics: [
99578
99675
  ...recovery === undefined ? [] : [recovery.diagnostic],
99579
- ...fetched.identityFallback ? [`info: lark.capture.identity-fallback: user credentials unavailable; captured with ${fetched.accessIdentity} identity`] : [],
99676
+ ...fetched.identityFallback ? [`info: lark.capture.identity-fallback: capture used an identity fallback; document identity: ${fetched.accessIdentity}; embedded reads may use the fallback identity`] : [],
99580
99677
  ...fetched.fidelity.issues.map((issue) => `${issue.severity}: ${issue.code}: ${issue.block_type} × ${issue.count}: ${issue.reason}`),
99581
99678
  ...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}`)
99582
99679
  ],
@@ -99938,15 +100035,15 @@ import { join as join100 } from "node:path";
99938
100035
  import { createHash as createHash30, randomUUID as randomUUID7 } from "node:crypto";
99939
100036
  async function inspectWriterLock(root2) {
99940
100037
  const path3 = await safeProjectTarget(root2, lockRelative);
99941
- let stat10;
100038
+ let stat11;
99942
100039
  try {
99943
- stat10 = await lstat10(path3);
100040
+ stat11 = await lstat10(path3);
99944
100041
  } catch (error) {
99945
100042
  if (error.code === "ENOENT")
99946
100043
  return null;
99947
100044
  throw error;
99948
100045
  }
99949
- if (!stat10.isDirectory() || stat10.isSymbolicLink())
100046
+ if (!stat11.isDirectory() || stat11.isSymbolicLink())
99950
100047
  throw new Error("Writer lock must be a real directory.");
99951
100048
  const ownerPath = await safeProjectTarget(root2, `${lockRelative}/owner.json`);
99952
100049
  const bytes = await readFile82(ownerPath, "utf8");
@@ -99961,7 +100058,7 @@ async function inspectWriterLock(root2) {
99961
100058
  const code3 = error.code;
99962
100059
  processState = code3 === "ESRCH" ? "not-running" : code3 === "EPERM" ? "running" : "unknown";
99963
100060
  }
99964
- const digest6 = `sha256:${createHash30("sha256").update(`${stat10.dev}:${stat10.ino}:${stat10.birthtimeMs}:${bytes}`).digest("hex")}`;
100061
+ const digest6 = `sha256:${createHash30("sha256").update(`${stat11.dev}:${stat11.ino}:${stat11.birthtimeMs}:${bytes}`).digest("hex")}`;
99965
100062
  return { ...owner, process_state: processState, digest: digest6, path: lockRelative };
99966
100063
  }
99967
100064
  async function recoverWriterLock(input) {
@@ -100033,22 +100130,22 @@ async function recoveryJournals(root2) {
100033
100130
  const entries2 = [];
100034
100131
  async function visit4(path3, depth) {
100035
100132
  await safeProjectTarget(root2, path3);
100036
- let stat10;
100133
+ let stat11;
100037
100134
  try {
100038
- stat10 = await lstat11(join101(root2, path3));
100135
+ stat11 = await lstat11(join101(root2, path3));
100039
100136
  } catch (error) {
100040
100137
  if (error.code === "ENOENT")
100041
100138
  return;
100042
100139
  throw error;
100043
100140
  }
100044
- if (stat10.isSymbolicLink())
100141
+ if (stat11.isSymbolicLink())
100045
100142
  throw new TypeError("Recovery does not follow transaction symlinks.");
100046
- if (stat10.isDirectory()) {
100143
+ if (stat11.isDirectory()) {
100047
100144
  if (depth > 3)
100048
100145
  throw new TypeError("Unexpected transaction directory depth; preserve it for diagnosis.");
100049
100146
  for (const name3 of (await readdir25(join101(root2, path3))).sort())
100050
100147
  await visit4(`${path3}/${name3}`, depth + 1);
100051
- } else if (stat10.isFile())
100148
+ } else if (stat11.isFile())
100052
100149
  entries2.push({ path: path3, digest: indexerProtocolDigest(await recoveryText(root2, path3)) });
100053
100150
  }
100054
100151
  await visit4(TRANSACTIONS_PATH, 0);
@@ -112531,7 +112628,7 @@ function conflict(path3) {
112531
112628
  next: "Choose another --local directory, or move conflicting/customized skills aside before retrying."
112532
112629
  });
112533
112630
  }
112534
- async function stat10(path3) {
112631
+ async function stat11(path3) {
112535
112632
  try {
112536
112633
  return await lstat14(path3);
112537
112634
  } catch (error) {
@@ -112541,7 +112638,7 @@ async function stat10(path3) {
112541
112638
  }
112542
112639
  }
112543
112640
  async function checkParents(path3) {
112544
- const entry = await stat10(path3);
112641
+ const entry = await stat11(path3);
112545
112642
  if (entry && (!entry.isDirectory() || entry.isSymbolicLink()))
112546
112643
  conflict(path3);
112547
112644
  const parent = dirname46(path3);
@@ -112600,11 +112697,11 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112600
112697
  const target = join112(commandsRoot, entry.name);
112601
112698
  const marker = join112(commandsRoot, `.context-${entry.name}.json`);
112602
112699
  for (const file of [target, marker]) {
112603
- const existing = await stat10(file);
112700
+ const existing = await stat11(file);
112604
112701
  if (existing && (!existing.isFile() || existing.isSymbolicLink()))
112605
112702
  conflict(file);
112606
112703
  }
112607
- if (await stat10(target)) {
112704
+ if (await stat11(target)) {
112608
112705
  let previous3;
112609
112706
  try {
112610
112707
  previous3 = JSON.parse(await readFile94(marker, "utf8"));
@@ -112614,7 +112711,7 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112614
112711
  const current2 = createHash34("sha256").update(await readFile94(target)).digest("hex");
112615
112712
  if (previous3?.owner !== "context-plugin-local" || previous3.digest !== current2)
112616
112713
  conflict(target);
112617
- } else if (await stat10(marker))
112714
+ } else if (await stat11(marker))
112618
112715
  conflict(marker);
112619
112716
  commandPlans.push({ name: entry.name, target, marker, content: content3, hash: createHash34("sha256").update(content3).digest("hex") });
112620
112717
  }
@@ -112623,21 +112720,21 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
112623
112720
  }
112624
112721
  const plans = [];
112625
112722
  for (const entry of (await readdir29(sourceRoot2, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
112626
- if (!entry.isDirectory() || !await stat10(join112(sourceRoot2, entry.name, "SKILL.md")))
112723
+ if (!entry.isDirectory() || !await stat11(join112(sourceRoot2, entry.name, "SKILL.md")))
112627
112724
  continue;
112628
112725
  const source2 = join112(sourceRoot2, entry.name);
112629
112726
  const target = join112(skillsRoot, entry.name);
112630
112727
  if (commandEntries.has(entry.name)) {
112631
- if (await stat10(target))
112728
+ if (await stat11(target))
112632
112729
  conflict(`${target} (use a fresh host directory to avoid duplicate command/skill entries)`);
112633
112730
  continue;
112634
112731
  }
112635
- const existing = await stat10(target);
112732
+ const existing = await stat11(target);
112636
112733
  if (existing) {
112637
112734
  if (!existing.isDirectory() || existing.isSymbolicLink())
112638
112735
  conflict(target);
112639
112736
  const markerPath = join112(target, MARKER);
112640
- if (!(await stat10(markerPath))?.isFile())
112737
+ if (!(await stat11(markerPath))?.isFile())
112641
112738
  conflict(target);
112642
112739
  let marker;
112643
112740
  try {