opencode-acp 1.14.20-pr.286.33 → 1.14.20

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/README.md CHANGED
@@ -235,10 +235,13 @@ ACP provides an `/acp` slash command (also accepts `/dcp` for backward compatibi
235
235
 
236
236
  | Command | Description |
237
237
  | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
238
- | `/acp` | Show compression status (same as `/acp stats`). Use `/acp help` for the command list |
238
+ | `/acp` | Shows available ACP commands |
239
239
  | `/acp context` | Token usage breakdown by category (system, user, assistant, tools, etc.) and how much has been saved through pruning |
240
- | `/acp stats` | Compression status: blocks, context usage, ranges (same report as the `acp_status` tool) |
241
- | `/acp export` | Export active compression blocks to a markdown file. Options: `--output <path>`, `--tier t1,t2,t3`, `--stdout`, `--append` |
240
+ | `/acp stats` | Cumulative pruning statistics across all sessions |
241
+ | `/acp manual [on\|off]` | Toggle manual mode. When on, the AI will not autonomously use context management tools |
242
+ | `/acp compress [focus]` | Trigger a single compress tool execution. Optional focus text directs what content to compress, following the active `compress.mode` |
243
+ | `/acp decompress <n>` | Restore a specific active compression by ID. Running without an argument shows available compression IDs, token sizes, and topics |
244
+ | `/acp recompress <n>` | Re-apply a user-decompressed compression by ID. Running without an argument shows recompressible IDs, token sizes, and topics |
242
245
 
243
246
  ---
244
247
 
package/README.zh-CN.md CHANGED
@@ -190,10 +190,13 @@ ACP 提供 `/acp` 斜杠命令(为向后兼容也接受 `/dcp`):
190
190
 
191
191
  | 命令 | 说明 |
192
192
  | ----------------------- | ------------------------------------------------------------------------------------------------------------------- |
193
- | `/acp` | 显示压缩状态(同 `/acp stats`);`/acp help` 查看命令列表 |
193
+ | `/acp` | 显示可用的 ACP 命令 |
194
194
  | `/acp context` | 按类别(system、user、assistant、tools 等)显示 token 用量明细,以及通过剪枝节省的量 |
195
- | `/acp stats` | 压缩状态:压缩块、上下文用量、推荐范围(与 `acp_status` 工具同一报告) |
196
- | `/acp export` | 导出活动压缩块到 markdown 文件。选项:`--output <path>`、`--tier t1,t2,t3`、`--stdout`、`--append` |
195
+ | `/acp stats` | 跨所有会话的累计剪枝统计 |
196
+ | `/acp manual [on\|off]` | 切换手动模式。开启后,AI 不会自动使用上下文管理工具 |
197
+ | `/acp compress [focus]` | 触发一次 `compress` 工具执行。可选的焦点文本指示要压缩的内容,遵循当前 `compress.mode` |
198
+ | `/acp decompress <n>` | 按 ID 恢复特定的活动压缩。不带参数运行时显示可用的压缩 ID、token 大小和主题 |
199
+ | `/acp recompress <n>` | 按 ID 重新应用用户解压的压缩。不带参数运行时显示可重新压缩的 ID、token 大小和主题 |
197
200
 
198
201
  ---
199
202
 
package/dist/index.js CHANGED
@@ -2900,14 +2900,14 @@ function applyCompressionState(state, input, selection, anchorMessageId, blockId
2900
2900
  block.directMessageIds = [...newlyCompressedMessageIds];
2901
2901
  block.directToolIds = [...newlyCompressedToolIds];
2902
2902
  block.compressedTokens = compressedTokens;
2903
- let effectiveTokens2 = compressedTokens;
2903
+ let effectiveTokens = compressedTokens;
2904
2904
  for (const consumedBlockId of consumed) {
2905
2905
  const cb = messagesState.blocksById.get(consumedBlockId);
2906
2906
  if (cb && (cb.tier ?? 1) === targetTierForConsumption) {
2907
- effectiveTokens2 += cb.effectiveCompressedTokens ?? cb.compressedTokens;
2907
+ effectiveTokens += cb.effectiveCompressedTokens ?? cb.compressedTokens;
2908
2908
  }
2909
2909
  }
2910
- block.effectiveCompressedTokens = effectiveTokens2;
2910
+ block.effectiveCompressedTokens = effectiveTokens;
2911
2911
  state.stats.pruneTokenCounter += compressedTokens;
2912
2912
  state.stats.totalPruneTokens += state.stats.pruneTokenCounter;
2913
2913
  state.stats.pruneTokenCounter = 0;
@@ -8347,9 +8347,9 @@ function createDecompressTool(factoryCtx) {
8347
8347
  }
8348
8348
  const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
8349
8349
  const lines2 = blockMessages.map(extractMessageText2);
8350
- const { writeFile: writeFile4 } = await import("fs/promises");
8350
+ const { writeFile: writeFile3 } = await import("fs/promises");
8351
8351
  const fileContent = lines2.length > 0 ? lines2.join("\n\n---\n\n") : targets[0]?.blocks[0]?.summary ?? "(no content available)";
8352
- await writeFile4(targetPath, fileContent, "utf-8");
8352
+ await writeFile3(targetPath, fileContent, "utf-8");
8353
8353
  const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
8354
8354
  return `Block(s) ${displayIds2} content (${blockMessages.length} messages, ${fileContent.length} chars) written to ${targetPath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
8355
8355
  }
@@ -9025,7 +9025,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
9025
9025
  import { join as join3 } from "path";
9026
9026
  import { existsSync as existsSync3 } from "fs";
9027
9027
  import { homedir as homedir3 } from "os";
9028
- var LOG_VERSION = true ? "1.14.20-pr.286.33" : "dev";
9028
+ var LOG_VERSION = true ? "1.14.20" : "dev";
9029
9029
  var Logger = class {
9030
9030
  logDir;
9031
9031
  enabled;
@@ -10089,308 +10089,6 @@ ${report}`;
10089
10089
  );
10090
10090
  }
10091
10091
 
10092
- // lib/commands/export.ts
10093
- import * as fs2 from "fs/promises";
10094
- import { existsSync as existsSync5 } from "fs";
10095
- import { dirname as dirname3, isAbsolute, join as join5, resolve } from "path";
10096
- var ALL_TIERS = [1, 2, 3];
10097
- var TIER_NAMES = {
10098
- 1: "Tier 1 \u2014 Capture",
10099
- 2: "Tier 2 \u2014 Distilled",
10100
- 3: "Tier 3 \u2014 Condensed"
10101
- };
10102
- function parseExportArgs(rawArgs) {
10103
- const options = {
10104
- outputPath: "",
10105
- // "" sentinel => default path resolved later
10106
- tiers: /* @__PURE__ */ new Set(),
10107
- includeMetadata: true,
10108
- append: false
10109
- };
10110
- const tokens = tokenize2(rawArgs);
10111
- let i = 0;
10112
- while (i < tokens.length) {
10113
- const tok = tokens[i];
10114
- const [flag, inlineValue] = splitFlag(tok);
10115
- switch (flag) {
10116
- case "--output":
10117
- case "-o": {
10118
- const value = inlineValue ?? tokens[i + 1];
10119
- if (value === void 0) {
10120
- throw new Error("--output requires a path (use `-` for stdout)");
10121
- }
10122
- options.outputPath = value;
10123
- i += inlineValue !== void 0 ? 1 : 2;
10124
- break;
10125
- }
10126
- case "--tier":
10127
- case "-t": {
10128
- const value = inlineValue ?? tokens[i + 1];
10129
- if (value === void 0) {
10130
- throw new Error("--tier requires a value (e.g. t2,t3 or all)");
10131
- }
10132
- options.tiers = parseTiers(value);
10133
- i += inlineValue !== void 0 ? 1 : 2;
10134
- break;
10135
- }
10136
- case "--no-metadata":
10137
- if (inlineValue !== void 0) {
10138
- throw new Error("--no-metadata does not take a value");
10139
- }
10140
- options.includeMetadata = false;
10141
- i += 1;
10142
- break;
10143
- case "--metadata":
10144
- if (inlineValue !== void 0) {
10145
- throw new Error("--metadata does not take a value");
10146
- }
10147
- options.includeMetadata = true;
10148
- i += 1;
10149
- break;
10150
- case "--append":
10151
- if (inlineValue !== void 0) {
10152
- throw new Error("--append does not take a value");
10153
- }
10154
- options.append = true;
10155
- i += 1;
10156
- break;
10157
- case "--stdout":
10158
- if (inlineValue !== void 0) {
10159
- throw new Error("--stdout does not take a value");
10160
- }
10161
- options.outputPath = "-";
10162
- i += 1;
10163
- break;
10164
- default:
10165
- throw new Error(
10166
- `Unknown flag: ${tok}. Supported: --output, --tier, --no-metadata, --append, --stdout.`
10167
- );
10168
- }
10169
- }
10170
- return options;
10171
- }
10172
- function tokenize2(input) {
10173
- const tokens = [];
10174
- const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
10175
- let m;
10176
- while ((m = re.exec(input)) !== null) {
10177
- tokens.push(m[1] ?? m[2] ?? m[3]);
10178
- }
10179
- return tokens;
10180
- }
10181
- function splitFlag(tok) {
10182
- const eq = tok.indexOf("=");
10183
- if (eq === -1) return [tok, void 0];
10184
- return [tok.slice(0, eq), tok.slice(eq + 1)];
10185
- }
10186
- function parseTiers(raw) {
10187
- const lower = raw.trim().toLowerCase();
10188
- if (lower === "all") return new Set(ALL_TIERS);
10189
- const parts = lower.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
10190
- const result = /* @__PURE__ */ new Set();
10191
- for (const part of parts) {
10192
- const digits = part.replace(/^t/i, "");
10193
- const n = Number(digits);
10194
- if (!Number.isInteger(n) || n < 1 || n > 3) {
10195
- throw new Error(
10196
- `Invalid tier "${part}". Expected t1, t2, t3, a combination (t2,t3), or "all".`
10197
- );
10198
- }
10199
- result.add(n);
10200
- }
10201
- if (result.size === 0) {
10202
- throw new Error(`Invalid tier "${raw}". Expected t1, t2, t3, or "all".`);
10203
- }
10204
- return result;
10205
- }
10206
- function resolveDefaultOutputPath(sessionId, cwd) {
10207
- const short = (sessionId || "session").slice(0, 8);
10208
- return join5(cwd, ".opencode", `acp-export-${short}.md`);
10209
- }
10210
- function formatTokens2(n) {
10211
- if (!Number.isFinite(n) || n <= 0) return "0";
10212
- return n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
10213
- }
10214
- function effectiveTokens(block) {
10215
- return block.effectiveCompressedTokens ?? block.compressedTokens ?? 0;
10216
- }
10217
- function collectActiveBlocks(state) {
10218
- const msgState = state.prune.messages;
10219
- const blocks = [];
10220
- for (const id of msgState.activeBlockIds) {
10221
- const block = msgState.blocksById.get(id);
10222
- if (block && block.active) {
10223
- blocks.push(block);
10224
- }
10225
- }
10226
- return blocks;
10227
- }
10228
- function filterByTier(blocks, tiers) {
10229
- if (tiers.size === 0) return blocks;
10230
- return blocks.filter((b) => tiers.has(b.tier ?? 1));
10231
- }
10232
- function tokenSpans(blocks) {
10233
- return {
10234
- effective: blocks.reduce((s, b) => s + effectiveTokens(b), 0),
10235
- summary: blocks.reduce((s, b) => s + (b.summaryTokens || 0), 0)
10236
- };
10237
- }
10238
- function tierCounts(blocks) {
10239
- const counts = {};
10240
- for (const b of blocks) {
10241
- const t = b.tier ?? 1;
10242
- counts[t] = (counts[t] || 0) + 1;
10243
- }
10244
- return ALL_TIERS.filter((t) => counts[t]).map((t) => `T${t}: ${counts[t]}`).join(", ");
10245
- }
10246
- function renderExportMarkdown(params) {
10247
- const { sessionId, generatedAt, blocks, tiers, includeMetadata } = params;
10248
- const out = [];
10249
- const tierFilterLabel = tiers.size === 0 ? "all" : ALL_TIERS.filter((t) => tiers.has(t)).map((t) => `T${t}`).join(", ");
10250
- out.push("# ACP Session Export");
10251
- out.push("");
10252
- out.push(`- **Session**: \`${(sessionId || "unknown").slice(0, 16)}\``);
10253
- out.push(`- **Generated**: ${generatedAt.toISOString()}`);
10254
- out.push(`- **Blocks exported**: ${blocks.length}`);
10255
- const breakdown = tierCounts(blocks);
10256
- if (breakdown) {
10257
- out.push(` - ${breakdown}`);
10258
- }
10259
- out.push(`- **Tiers**: ${tierFilterLabel}`);
10260
- const span = tokenSpans(blocks);
10261
- if (blocks.length > 0) {
10262
- out.push(
10263
- `- **Coverage**: ${formatTokens2(span.effective)} original \u2192 ${formatTokens2(span.summary)} summary`
10264
- );
10265
- }
10266
- out.push("");
10267
- out.push("---");
10268
- out.push("");
10269
- if (blocks.length === 0) {
10270
- out.push("_No active compression blocks match the selected tiers._");
10271
- out.push("");
10272
- out.push(
10273
- "Tip: run `/acp export --tier all` to include every active tier, or trigger a compression first."
10274
- );
10275
- return out.join("\n");
10276
- }
10277
- for (const tier of [3, 2, 1]) {
10278
- const tierBlocks = blocks.filter((b) => (b.tier ?? 1) === tier).sort((a, b) => a.blockId - b.blockId);
10279
- if (tierBlocks.length === 0) continue;
10280
- out.push(`## ${TIER_NAMES[tier]}`);
10281
- out.push("");
10282
- for (const block of tierBlocks) {
10283
- const topic = block.topic || block.batchTopic || "(no topic)";
10284
- out.push(`### b${block.blockId} \u2014 ${topic}`);
10285
- out.push("");
10286
- if (includeMetadata) {
10287
- const mode = block.mode ?? "range";
10288
- const msgCount = block.effectiveMessageIds?.length ?? 0;
10289
- const created = new Date(block.createdAt).toISOString();
10290
- const age = block.survivedCount ?? 0;
10291
- out.push(`- **Tier**: T${tier} \xB7 **Mode**: ${mode}`);
10292
- out.push(`- **Messages**: ${msgCount} (effective)`);
10293
- out.push(
10294
- `- **Tokens**: ${formatTokens2(effectiveTokens(block))} \u2192 ${formatTokens2(block.summaryTokens || 0)} summary`
10295
- );
10296
- out.push(`- **Created**: ${created}`);
10297
- out.push(`- **Age**: survived ${age} transform${age === 1 ? "" : "s"}`);
10298
- out.push("");
10299
- }
10300
- const summary = (block.summary || "").trim() || "_(empty summary)_";
10301
- out.push(summary);
10302
- out.push("");
10303
- out.push("---");
10304
- out.push("");
10305
- }
10306
- }
10307
- return out.join("\n");
10308
- }
10309
- async function handleExportCommand(ctx, args) {
10310
- const { client, state, logger, sessionId } = ctx;
10311
- let options;
10312
- try {
10313
- options = parseExportArgs(args);
10314
- } catch (err) {
10315
- const msg = err?.message ?? String(err);
10316
- logger.warn("export: argument parse failed", { error: msg, args });
10317
- await sendExportNotice(client, sessionId, ctx, `[ACP Export] ${msg}`);
10318
- return;
10319
- }
10320
- const tiers = options.tiers;
10321
- const allActive = collectActiveBlocks(state);
10322
- const blocks = filterByTier(allActive, tiers);
10323
- const generatedAt = /* @__PURE__ */ new Date();
10324
- const markdown = renderExportMarkdown({
10325
- sessionId,
10326
- generatedAt,
10327
- blocks,
10328
- tiers,
10329
- includeMetadata: options.includeMetadata
10330
- });
10331
- if (options.outputPath === "-") {
10332
- await sendExportNotice(client, sessionId, ctx, markdown);
10333
- return;
10334
- }
10335
- const cwd = ctx.workingDirectory || process.cwd();
10336
- const targetPath = options.outputPath !== "" ? isAbsolute(options.outputPath) ? options.outputPath : resolve(cwd, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd);
10337
- try {
10338
- const dir = dirname3(targetPath);
10339
- if (!existsSync5(dir)) {
10340
- await fs2.mkdir(dir, { recursive: true });
10341
- }
10342
- const flag = options.append ? "a" : "w";
10343
- if (options.append && existsSync5(targetPath)) {
10344
- await fs2.appendFile(targetPath, `
10345
-
10346
- ---
10347
-
10348
- ${markdown}`, "utf-8");
10349
- } else {
10350
- await fs2.writeFile(targetPath, markdown, { flag, encoding: "utf-8" });
10351
- }
10352
- } catch (err) {
10353
- const msg = err?.message ?? String(err);
10354
- logger.warn("export: file write failed", { path: targetPath, error: msg });
10355
- await sendExportNotice(
10356
- client,
10357
- sessionId,
10358
- ctx,
10359
- `[ACP Export] Failed to write \`${targetPath}\`: ${msg}`
10360
- );
10361
- return;
10362
- }
10363
- logger.info("export: wrote markdown", {
10364
- path: targetPath,
10365
- blocks: blocks.length,
10366
- tiers: tiers.size ? [...tiers].sort().join(",") : "all"
10367
- });
10368
- const summary = formatExportSummary(targetPath, blocks, allActive, generatedAt);
10369
- await sendExportNotice(client, sessionId, ctx, summary);
10370
- }
10371
- function formatExportSummary(path, exported, allActive, generatedAt) {
10372
- const lines = [];
10373
- lines.push("[ACP Export]");
10374
- lines.push(`Wrote ${exported.length} block${exported.length === 1 ? "" : "s"} to:`);
10375
- lines.push(` ${path}`);
10376
- if (exported.length === 0) {
10377
- lines.push("");
10378
- lines.push(
10379
- `No matching blocks (of ${allActive.length} active). Try \`/acp export --tier all\`.`
10380
- );
10381
- } else {
10382
- lines.push("");
10383
- lines.push(`Generated ${generatedAt.toISOString()}.`);
10384
- lines.push(
10385
- "Review, edit, and commit as a devlog / AGENTS.md supplement \u2014 export is user-driven, never auto-injected."
10386
- );
10387
- }
10388
- return lines.join("\n");
10389
- }
10390
- async function sendExportNotice(client, sessionId, ctx, text) {
10391
- await sendIgnoredMessage(client, sessionId, text, {}, ctx.logger);
10392
- }
10393
-
10394
10092
  // lib/messages/filter/registry.ts
10395
10093
  var registry3 = /* @__PURE__ */ new Map();
10396
10094
  function registerMessageFilter(filter) {
@@ -11046,20 +10744,6 @@ ${text}`);
11046
10744
  }
11047
10745
  };
11048
10746
  }
11049
- function buildHelpText() {
11050
- return [
11051
- "[ACP] Available commands:",
11052
- "",
11053
- " /acp Show compression status (same as /acp stats)",
11054
- " /acp context Token usage breakdown (system, user, assistant, tools)",
11055
- " /acp stats Compression status: blocks, context usage, ranges",
11056
- " /acp export Export active compression blocks to markdown",
11057
- " Options: --output <path>, --tier t1,t2,t3, --stdout, --append",
11058
- " /acp help Show this help",
11059
- "",
11060
- "Also accepts /dcp for backward compatibility."
11061
- ].join("\n");
11062
- }
11063
10747
  function createCommandExecuteHandler(client, registry4, logger, config, workingDirectory, hostPermissions) {
11064
10748
  return async (input, output) => {
11065
10749
  if (!config.commands.enabled) {
@@ -11077,35 +10761,23 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
11077
10761
  config
11078
10762
  );
11079
10763
  syncCompressPermissionState(state, config, hostPermissions, messages);
10764
+ const effectivePermission = compressPermission(state, config);
10765
+ if (effectivePermission === "deny") {
10766
+ return;
10767
+ }
11080
10768
  const commandCtx = {
11081
10769
  client,
11082
10770
  state,
11083
10771
  config,
11084
10772
  logger,
11085
10773
  sessionId: input.sessionID,
11086
- messages,
11087
- workingDirectory
10774
+ messages
11088
10775
  };
11089
10776
  const sub = input.arguments?.trim().toLowerCase();
11090
- if (sub === "stats" || sub === "status" || sub === "") {
10777
+ if (sub === "stats" || sub === "status") {
11091
10778
  await handleStatsCommand(commandCtx);
11092
10779
  throw new Error("__DCP_CONTEXT_HANDLED__");
11093
10780
  }
11094
- if (sub === "export" || sub.startsWith("export ")) {
11095
- const exportArgs = input.arguments?.trim().slice("export".length).trim() || "";
11096
- await handleExportCommand(commandCtx, exportArgs);
11097
- throw new Error("__DCP_CONTEXT_HANDLED__");
11098
- }
11099
- if (sub === "help") {
11100
- await sendIgnoredMessage(
11101
- client,
11102
- input.sessionID,
11103
- buildHelpText(),
11104
- {},
11105
- logger
11106
- );
11107
- throw new Error("__DCP_CONTEXT_HANDLED__");
11108
- }
11109
10781
  await handleContextCommand(commandCtx);
11110
10782
  throw new Error("__DCP_CONTEXT_HANDLED__");
11111
10783
  }
@@ -11215,7 +10887,7 @@ function configureClientAuth(client) {
11215
10887
 
11216
10888
  // lib/update.ts
11217
10889
  import { readFile as readFile2, rm } from "fs/promises";
11218
- import { basename, dirname as dirname4, join as join6 } from "path";
10890
+ import { basename, dirname as dirname3, join as join5 } from "path";
11219
10891
  import { fileURLToPath } from "url";
11220
10892
  var PACKAGE_NAME = "opencode-acp";
11221
10893
  function startAutoUpdate(ctx, enabled) {
@@ -11240,7 +10912,7 @@ function startAutoUpdate(ctx, enabled) {
11240
10912
  async function checkAutoUpdate(signal) {
11241
10913
  const packageDir = await findPackageDir(PACKAGE_NAME);
11242
10914
  if (!packageDir) return { updated: false };
11243
- const pkg = await readPackageJson(join6(packageDir, "package.json"));
10915
+ const pkg = await readPackageJson(join5(packageDir, "package.json"));
11244
10916
  if (!pkg?.name || !pkg.version) return { updated: false };
11245
10917
  const latest = await fetchLatestVersion(pkg.name, signal);
11246
10918
  if (!latest || !isVersionNewer(latest, pkg.version)) return { updated: false };
@@ -11260,21 +10932,21 @@ async function checkAutoUpdate(signal) {
11260
10932
  return { updated: true, name: pkg.name, current: pkg.version, latest };
11261
10933
  }
11262
10934
  async function findPackageDir(name) {
11263
- let dir = dirname4(fileURLToPath(import.meta.url));
10935
+ let dir = dirname3(fileURLToPath(import.meta.url));
11264
10936
  for (; ; ) {
11265
- const pkg = await readPackageJson(join6(dir, "package.json"));
10937
+ const pkg = await readPackageJson(join5(dir, "package.json"));
11266
10938
  if (pkg?.name === name) return dir;
11267
- const parent = dirname4(dir);
10939
+ const parent = dirname3(dir);
11268
10940
  if (parent === dir) return void 0;
11269
10941
  dir = parent;
11270
10942
  }
11271
10943
  }
11272
10944
  async function updateRemoveDir(packageDir, name) {
11273
- const packageParent = dirname4(packageDir);
11274
- const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
10945
+ const packageParent = dirname3(packageDir);
10946
+ const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname3(packageParent) : packageParent;
11275
10947
  if (basename(nodeModulesDir) !== "node_modules") return void 0;
11276
- const wrapperDir = dirname4(nodeModulesDir);
11277
- const wrapperPkg = await readPackageJson(join6(wrapperDir, "package.json"));
10948
+ const wrapperDir = dirname3(nodeModulesDir);
10949
+ const wrapperPkg = await readPackageJson(join5(wrapperDir, "package.json"));
11278
10950
  const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
11279
10951
  if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
11280
10952
  return wrapperDir;
@@ -11282,7 +10954,7 @@ async function updateRemoveDir(packageDir, name) {
11282
10954
  function wrapperSpec(wrapperDir, name) {
11283
10955
  if (name.startsWith("@")) {
11284
10956
  const [scope, pkg] = name.split("/");
11285
- if (!scope || !pkg || basename(dirname4(wrapperDir)) !== scope) return void 0;
10957
+ if (!scope || !pkg || basename(dirname3(wrapperDir)) !== scope) return void 0;
11286
10958
  const prefix2 = `${pkg}@`;
11287
10959
  const base2 = basename(wrapperDir);
11288
10960
  return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;