@serviceme/devtools-core 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1311,7 +1311,7 @@ var GitHubAuthProvider = class {
1311
1311
  }
1312
1312
  };
1313
1313
  function sleep(ms) {
1314
- return new Promise((resolve11) => setTimeout(resolve11, ms));
1314
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
1315
1315
  }
1316
1316
 
1317
1317
  // src/auth/providers/MicrosoftAuthProvider.ts
@@ -1393,7 +1393,7 @@ function scheduleForceKill(child) {
1393
1393
  return timer;
1394
1394
  }
1395
1395
  async function runCommand(command, options = {}) {
1396
- return new Promise((resolve11, reject) => {
1396
+ return new Promise((resolve13, reject) => {
1397
1397
  const child = spawn(command, options.args ?? [], {
1398
1398
  cwd: options.cwd,
1399
1399
  env: options.env,
@@ -1440,7 +1440,7 @@ async function runCommand(command, options = {}) {
1440
1440
  }
1441
1441
  finish(() => {
1442
1442
  if (code === 0) {
1443
- resolve11({
1443
+ resolve13({
1444
1444
  stdout,
1445
1445
  stderr,
1446
1446
  code: 0
@@ -1772,8 +1772,8 @@ async function createDefaultCopilotHostCapabilities(options) {
1772
1772
  for (const kind of allPersonalLinkKinds) {
1773
1773
  if (kind !== "agent" && kind !== "skill") continue;
1774
1774
  const target = path3.join(home, ".copilot", kind === "agent" ? "agents" : "skills");
1775
- const stat15 = await fsp.stat(target).catch(() => null);
1776
- if (stat15?.isDirectory() === true) {
1775
+ const stat16 = await fsp.stat(target).catch(() => null);
1776
+ if (stat16?.isDirectory() === true) {
1777
1777
  personalTargets[kind] = target;
1778
1778
  }
1779
1779
  }
@@ -1826,8 +1826,8 @@ var CopilotLinkMaterializer = class {
1826
1826
  const previous = input.previousState.entries.find(
1827
1827
  (state) => state.identity === entry.identity
1828
1828
  );
1829
- const stat15 = await fsp2.lstat(linkPath).catch(() => null);
1830
- if (stat15 && !stat15.isSymbolicLink()) {
1829
+ const stat16 = await fsp2.lstat(linkPath).catch(() => null);
1830
+ if (stat16 && !stat16.isSymbolicLink()) {
1831
1831
  results.push({
1832
1832
  identity: entry.identity,
1833
1833
  status: previous ? "drifted" : "conflict",
@@ -1835,7 +1835,7 @@ var CopilotLinkMaterializer = class {
1835
1835
  });
1836
1836
  continue;
1837
1837
  }
1838
- if (stat15?.isSymbolicLink()) {
1838
+ if (stat16?.isSymbolicLink()) {
1839
1839
  const currentTarget = await fsp2.readlink(linkPath);
1840
1840
  if (currentTarget !== entry.sourcePath) {
1841
1841
  if (previous?.linkPath === this.toStateLinkPath(linkPath) && previous.sourcePath === entry.sourcePath) {
@@ -1874,8 +1874,8 @@ var CopilotLinkMaterializer = class {
1874
1874
  /** Adopt a matching legacy link without recreating it. */
1875
1875
  async adoptLegacyLink(input) {
1876
1876
  const linkPath = this.resolveLinkPath(input.workspaceDir, input.entry);
1877
- const stat15 = await fsp2.lstat(linkPath).catch(() => null);
1878
- if (stat15?.isSymbolicLink() && await fsp2.readlink(linkPath) === input.entry.sourcePath) {
1877
+ const stat16 = await fsp2.lstat(linkPath).catch(() => null);
1878
+ if (stat16?.isSymbolicLink() && await fsp2.readlink(linkPath) === input.entry.sourcePath) {
1879
1879
  return { identity: input.entry.identity, status: "adopted" };
1880
1880
  }
1881
1881
  return {
@@ -1893,8 +1893,8 @@ var CopilotLinkMaterializer = class {
1893
1893
  kindDir,
1894
1894
  input.entry.sourceIsFile ? path4.basename(input.entry.sourcePath) : input.entry.name
1895
1895
  );
1896
- const stat15 = await fsp2.lstat(legacyPath).catch(() => null);
1897
- if (stat15?.isSymbolicLink() && await fsp2.readlink(legacyPath) === input.entry.sourcePath) {
1896
+ const stat16 = await fsp2.lstat(legacyPath).catch(() => null);
1897
+ if (stat16?.isSymbolicLink() && await fsp2.readlink(legacyPath) === input.entry.sourcePath) {
1898
1898
  return {
1899
1899
  identity: input.entry.identity,
1900
1900
  status: "adopted",
@@ -1946,8 +1946,8 @@ var CopilotLinkMaterializer = class {
1946
1946
  return toPosix(linkPath);
1947
1947
  }
1948
1948
  const workspaceRoot = path4.dirname(cursor);
1949
- const relative4 = toPosix(path4.relative(workspaceRoot, linkPath));
1950
- return relative4 === "" ? toPosix(linkPath) : relative4;
1949
+ const relative5 = toPosix(path4.relative(workspaceRoot, linkPath));
1950
+ return relative5 === "" ? toPosix(linkPath) : relative5;
1951
1951
  }
1952
1952
  };
1953
1953
  function toPosix(value) {
@@ -1993,8 +1993,8 @@ async function resolveSource(entry, pluginDir, repoRoot) {
1993
1993
  candidates.push(path5.join(repoRoot, "agents", `${base}.agent.md`));
1994
1994
  }
1995
1995
  for (const candidate of candidates) {
1996
- const stat15 = await fs2.stat(candidate).catch(() => void 0);
1997
- if (stat15) return candidate;
1996
+ const stat16 = await fs2.stat(candidate).catch(() => void 0);
1997
+ if (stat16) return candidate;
1998
1998
  }
1999
1999
  return null;
2000
2000
  }
@@ -2007,8 +2007,8 @@ function normalizeRefPath(entry) {
2007
2007
  async function copyProjectionEntry(source, dest) {
2008
2008
  await fs2.mkdir(path5.dirname(dest), { recursive: true });
2009
2009
  await fs2.rm(dest, { force: true, recursive: true });
2010
- const stat15 = await fs2.stat(source);
2011
- if (stat15.isDirectory()) {
2010
+ const stat16 = await fs2.stat(source);
2011
+ if (stat16.isDirectory()) {
2012
2012
  await fs2.cp(source, dest, { recursive: true, dereference: true });
2013
2013
  } else {
2014
2014
  await fs2.copyFile(source, dest);
@@ -2065,13 +2065,15 @@ async function materializePluginProjection(input) {
2065
2065
  };
2066
2066
  await fs2.writeFile(
2067
2067
  inside("plugin.json"),
2068
- JSON.stringify(servedPluginManifest(raw), null, 2) + "\n"
2068
+ `${JSON.stringify(servedPluginManifest(raw), null, 2)}
2069
+ `
2069
2070
  );
2070
2071
  const pluginMarkerDir = inside(PLUGIN_MARKER_DIR);
2071
2072
  await fs2.mkdir(pluginMarkerDir, { recursive: true });
2072
2073
  await fs2.writeFile(
2073
2074
  path5.join(pluginMarkerDir, "plugin.json"),
2074
- JSON.stringify(servedPluginManifest(raw), null, 2) + "\n"
2075
+ `${JSON.stringify(servedPluginManifest(raw), null, 2)}
2076
+ `
2075
2077
  );
2076
2078
  const mcpJson = await fs2.stat(path5.join(pluginDir, "mcp.json")).catch(() => void 0);
2077
2079
  if (mcpJson?.isFile()) {
@@ -2083,8 +2085,8 @@ async function materializePluginProjection(input) {
2083
2085
  return result;
2084
2086
  }
2085
2087
  async function isMaterializedProjection(dir) {
2086
- const stat15 = await fs2.lstat(dir).catch(() => void 0);
2087
- if (!stat15?.isDirectory() || stat15.isSymbolicLink()) return false;
2088
+ const stat16 = await fs2.lstat(dir).catch(() => void 0);
2089
+ if (!stat16?.isDirectory() || stat16.isSymbolicLink()) return false;
2088
2090
  const root = path5.resolve(dir);
2089
2091
  const markerPath = path5.resolve(root, PLUGIN_MARKER_DIR, MARKER_FILE);
2090
2092
  if (!markerPath.startsWith(root + path5.sep)) return false;
@@ -2194,8 +2196,8 @@ var CopilotPluginRegistrar = class {
2194
2196
  merged.set(id, reg);
2195
2197
  }
2196
2198
  const legacyLink = path6.join(legacyDir, id);
2197
- const stat15 = await fs3.lstat(legacyLink).catch(() => void 0);
2198
- if (stat15?.isSymbolicLink()) {
2199
+ const stat16 = await fs3.lstat(legacyLink).catch(() => void 0);
2200
+ if (stat16?.isSymbolicLink()) {
2199
2201
  await this.writeRegistry(merged);
2200
2202
  await fs3.mkdir(dest, { recursive: true });
2201
2203
  await fs3.rm(this.linkPath(id), { force: true, recursive: true });
@@ -2345,6 +2347,7 @@ function buildCopilotCustomizationView(input) {
2345
2347
  throw new Error(`Unknown Copilot source ${definition.sourceId} for package ${definition.id}`);
2346
2348
  }
2347
2349
  }
2350
+ const disabledArtifactIds = new Set(input.disabledArtifactIds ?? []);
2348
2351
  const packages = input.installations.filter((installation) => installation.scope === input.scope).map((installation) => {
2349
2352
  const definition = requirePackage(input.packages, installation.packageId);
2350
2353
  const artifacts = installation.selectedArtifactIds.map((artifactId) => {
@@ -2368,6 +2371,11 @@ function buildCopilotCustomizationView(input) {
2368
2371
  return status === "action-required" || status === "blocked";
2369
2372
  })
2370
2373
  );
2374
+ const packageCount = packages.filter((pkg) => pkg.definition.wholePackage === true).length;
2375
+ const enabledArtifactCount = packages.reduce(
2376
+ (count, pkg) => count + pkg.artifacts.filter(({ artifact }) => !disabledArtifactIds.has(artifact.id)).length,
2377
+ 0
2378
+ );
2371
2379
  const sources = input.sources.map((source) => ({
2372
2380
  ...source,
2373
2381
  packages: packages.filter((pkg) => pkg.definition.sourceId === source.id)
@@ -2379,11 +2387,16 @@ function buildCopilotCustomizationView(input) {
2379
2387
  packages,
2380
2388
  attention,
2381
2389
  summary: {
2382
- packageCount: packages.length,
2383
- enabledArtifactCount: packages.reduce((count, pkg) => count + pkg.selectedArtifactCount, 0),
2390
+ packageCount,
2391
+ enabledArtifactCount,
2384
2392
  attentionCount: attention.length
2385
2393
  },
2386
- ...input.legacyCount ? { legacyMigration: { count: input.legacyCount, sourceLabel: "~/.agents" } } : {}
2394
+ ...input.legacyCount ? {
2395
+ legacyMigration: {
2396
+ count: input.legacyCount,
2397
+ sourceLabel: "~/.agents"
2398
+ }
2399
+ } : {}
2387
2400
  };
2388
2401
  }
2389
2402
  function requirePackage(packages, packageId) {
@@ -2407,6 +2420,33 @@ function requireState(statesByArtifactId, artifactId) {
2407
2420
  // src/copilot-content/disabled-content-store.ts
2408
2421
  import * as fsp3 from "fs/promises";
2409
2422
  import * as path7 from "path";
2423
+
2424
+ // src/copilot-content/types.ts
2425
+ function buildContentIdentity(identity) {
2426
+ return `${identity.repoId}::${identity.pluginId}::${identity.kind}:${identity.name}`;
2427
+ }
2428
+ function parseContentIdentity(value) {
2429
+ const segments = value.split("::");
2430
+ if (segments.length !== 3) return null;
2431
+ const repoId = segments[0] ?? "";
2432
+ const pluginId = segments[1] ?? "";
2433
+ const tail = (segments[2] ?? "").split(":");
2434
+ const kind = tail[0] ?? "";
2435
+ const name = tail.slice(1).join(":");
2436
+ if (!repoId || !pluginId || !kind || !name) return null;
2437
+ return { repoId, pluginId, kind, name };
2438
+ }
2439
+ function getWorkspaceManifestSources(manifest) {
2440
+ return manifest.version === 1 ? manifest.repositories.map((repository) => ({
2441
+ ...repository,
2442
+ type: "git"
2443
+ })) : manifest.sources;
2444
+ }
2445
+ function getWorkspaceManifestPluginSourceId(manifest, plugin) {
2446
+ return manifest.version === 1 ? plugin.repository : plugin.source;
2447
+ }
2448
+
2449
+ // src/copilot-content/disabled-content-store.ts
2410
2450
  function sameEntry(a, b) {
2411
2451
  if (a.scope !== b.scope || a.repoId !== b.repoId || a.name !== b.name || a.kind !== b.kind) {
2412
2452
  return false;
@@ -2416,6 +2456,15 @@ function sameEntry(a, b) {
2416
2456
  }
2417
2457
  return true;
2418
2458
  }
2459
+ function buildDisabledIdentityMatcher(workspaceDir, marks) {
2460
+ return (identity) => {
2461
+ const parsed = parseContentIdentity(identity);
2462
+ if (parsed === null) return false;
2463
+ return marks.some(
2464
+ (mark) => mark.repoId === parsed.repoId && mark.kind === parsed.kind && mark.name === parsed.name && (mark.scope === "user" || mark.workspaceDir === workspaceDir)
2465
+ );
2466
+ };
2467
+ }
2419
2468
  var DisabledContentStore = class {
2420
2469
  constructor(options = {}) {
2421
2470
  this.homeDir = options.homeDir ?? getServicemeHome();
@@ -2436,9 +2485,6 @@ var DisabledContentStore = class {
2436
2485
  throw error;
2437
2486
  }
2438
2487
  }
2439
- async has(matcher) {
2440
- return this.list().then((entries) => entries.some(matcher));
2441
- }
2442
2488
  async add(entry) {
2443
2489
  const entries = await this.list();
2444
2490
  if (entries.some((candidate) => sameEntry(candidate, entry))) {
@@ -2454,6 +2500,30 @@ var DisabledContentStore = class {
2454
2500
  }
2455
2501
  await this.write(next);
2456
2502
  }
2503
+ /**
2504
+ * Lifecycle cleanup: installing or uninstalling an artifact clears
2505
+ * its disable marks for that scope — a mark without its artifact is
2506
+ * stale garbage that keeps the home page showing a disabled row for
2507
+ * content the catalog already reports as gone. User scope matches
2508
+ * any workspace; workspace scope matches the exact workspace.
2509
+ */
2510
+ async removeForArtifact(input) {
2511
+ const entries = await this.list();
2512
+ const next = entries.filter((candidate) => {
2513
+ const sameArtifact = candidate.repoId === input.repoId && candidate.name === input.name && candidate.kind === input.kind;
2514
+ if (!sameArtifact || candidate.scope !== input.scope) {
2515
+ return true;
2516
+ }
2517
+ if (input.scope === "user") {
2518
+ return false;
2519
+ }
2520
+ return candidate.workspaceDir !== input.workspaceDir;
2521
+ });
2522
+ if (next.length === entries.length) {
2523
+ return;
2524
+ }
2525
+ await this.write(next);
2526
+ }
2457
2527
  async write(entries) {
2458
2528
  const statePath = await this.path();
2459
2529
  await fsp3.mkdir(path7.dirname(statePath), { recursive: true });
@@ -2638,9 +2708,9 @@ async function copyDirPreservingMode(sourceDir, targetDir) {
2638
2708
  continue;
2639
2709
  }
2640
2710
  if (!dirent.isFile()) continue;
2641
- const stat15 = await fsp4.stat(sourceChild);
2711
+ const stat16 = await fsp4.stat(sourceChild);
2642
2712
  await fsp4.copyFile(sourceChild, targetChild);
2643
- await fsp4.chmod(targetChild, stat15.mode & 511);
2713
+ await fsp4.chmod(targetChild, stat16.mode & 511);
2644
2714
  }
2645
2715
  }
2646
2716
 
@@ -2755,7 +2825,7 @@ async function defaultResolveGitExcludePath(workspaceDir) {
2755
2825
  );
2756
2826
  }
2757
2827
  async function resolveGitExcludeViaRevParse(workspaceDir) {
2758
- const result = await new Promise((resolve11, reject) => {
2828
+ const result = await new Promise((resolve13, reject) => {
2759
2829
  const child = spawn2("git", ["rev-parse", "--git-path", "info/exclude"], {
2760
2830
  cwd: workspaceDir
2761
2831
  });
@@ -2764,7 +2834,7 @@ async function resolveGitExcludeViaRevParse(workspaceDir) {
2764
2834
  stdout += chunk;
2765
2835
  });
2766
2836
  child.on("error", reject);
2767
- child.on("close", (code) => resolve11({ code: code ?? -1, stdout }));
2837
+ child.on("close", (code) => resolve13({ code: code ?? -1, stdout }));
2768
2838
  });
2769
2839
  if (result.code !== 0) {
2770
2840
  throw new Error(`git rev-parse --git-path failed with exit code ${result.code}`);
@@ -2851,17 +2921,26 @@ var catalogSourceSchema = z.object({
2851
2921
  revision: z.string().regex(CATALOG_REVISION_PATTERN, "invalid catalog revision"),
2852
2922
  digest: z.string().regex(SHA256_DIGEST_PATTERN, "invalid catalog digest")
2853
2923
  });
2924
+ var artifactTargetSchema = z.string().refine((value) => {
2925
+ const separator = value.indexOf(":");
2926
+ if (separator <= 0 || separator === value.length - 1) return false;
2927
+ const kind = value.slice(0, separator);
2928
+ const name = value.slice(separator + 1);
2929
+ return artifactKindSchema.safeParse(kind).success && !/[\\/\0]/.test(name) && name !== "." && name !== "..";
2930
+ }, "unsafe artifact target");
2854
2931
  var pluginSchema = z.object({
2855
2932
  repository: z.string().min(1),
2856
2933
  id: z.string().regex(PLUGIN_ID_PATTERN, "unsafe plugin id"),
2857
2934
  artifacts: z.record(z.string(), z.boolean()),
2858
- artifactIds: z.array(artifactIdSchema).optional()
2935
+ artifactIds: z.array(artifactIdSchema).optional(),
2936
+ disabledArtifacts: z.array(artifactTargetSchema).optional()
2859
2937
  });
2860
2938
  var pluginV2Schema = z.object({
2861
2939
  source: z.string().min(1),
2862
2940
  id: z.string().regex(PLUGIN_ID_PATTERN, "unsafe plugin id"),
2863
2941
  artifacts: z.record(z.string(), z.boolean()),
2864
- artifactIds: z.array(artifactIdSchema).optional()
2942
+ artifactIds: z.array(artifactIdSchema).optional(),
2943
+ disabledArtifacts: z.array(artifactTargetSchema).optional()
2865
2944
  });
2866
2945
  function validatePluginArtifacts(plugins, ctx) {
2867
2946
  for (const plugin of plugins) {
@@ -3072,6 +3151,34 @@ async function removeWorkspaceContentSelection(input) {
3072
3151
  await writeWorkspaceCopilotManifest(input.workspaceDir, next);
3073
3152
  return next;
3074
3153
  }
3154
+ async function setWorkspacePluginArtifactsDisabled(input) {
3155
+ const manifest = await loadWorkspaceCopilotManifest(input.workspaceDir).catch(() => void 0);
3156
+ if (!manifest) return void 0;
3157
+ const withDisabledTarget = (plugin) => {
3158
+ const current = new Set(plugin.disabledArtifacts ?? []);
3159
+ if (input.disabled) {
3160
+ current.add(input.target);
3161
+ } else {
3162
+ current.delete(input.target);
3163
+ }
3164
+ const { disabledArtifacts: _dropped, ...rest } = plugin;
3165
+ return current.size > 0 ? { ...rest, disabledArtifacts: [...current].sort() } : rest;
3166
+ };
3167
+ if (manifest.version === 2) {
3168
+ const plugins2 = manifest.plugins.map(
3169
+ (plugin) => plugin.source === input.repositoryId && plugin.id === input.pluginId ? withDisabledTarget({ ...plugin, repository: plugin.source }) : plugin
3170
+ );
3171
+ const next2 = { ...manifest, plugins: plugins2 };
3172
+ await writeWorkspaceCopilotManifest(input.workspaceDir, next2);
3173
+ return next2;
3174
+ }
3175
+ const plugins = manifest.plugins.map(
3176
+ (plugin) => plugin.repository === input.repositoryId && plugin.id === input.pluginId ? withDisabledTarget(plugin) : plugin
3177
+ );
3178
+ const next = { ...manifest, plugins };
3179
+ await writeWorkspaceCopilotManifest(input.workspaceDir, next);
3180
+ return next;
3181
+ }
3075
3182
 
3076
3183
  // src/copilot-content/workspace-state-store.ts
3077
3184
  import { createHash } from "crypto";
@@ -3287,8 +3394,8 @@ var PackageInstallationService = class {
3287
3394
  const dirents = await fsp9.readdir(kindDir, { withFileTypes: true }).catch(() => []);
3288
3395
  for (const dirent of dirents) {
3289
3396
  const legacyPath = path13.join(kindDir, dirent.name);
3290
- const stat15 = await fsp9.lstat(legacyPath).catch(() => null);
3291
- const isLink = stat15?.isSymbolicLink() === true;
3397
+ const stat16 = await fsp9.lstat(legacyPath).catch(() => null);
3398
+ const isLink = stat16?.isSymbolicLink() === true;
3292
3399
  const targetPath = isLink ? await fsp9.readlink(legacyPath).catch(() => "") : "";
3293
3400
  const resolved = isLink ? this.resolveLegacyTarget(legacyPath, targetPath) : "";
3294
3401
  const targetStat = resolved !== "" ? await fsp9.stat(resolved).catch(() => null) : null;
@@ -3314,8 +3421,8 @@ var PackageInstallationService = class {
3314
3421
  if (!entry?.eligible) {
3315
3422
  throw new Error(`Legacy entry ${artifactId} is not eligible for migration`);
3316
3423
  }
3317
- const stat15 = await fsp9.lstat(entry.sourcePath).catch(() => null);
3318
- if (!stat15?.isSymbolicLink()) {
3424
+ const stat16 = await fsp9.lstat(entry.sourcePath).catch(() => null);
3425
+ if (!stat16?.isSymbolicLink()) {
3319
3426
  throw new Error(`Legacy entry ${entry.name} is no longer a link`);
3320
3427
  }
3321
3428
  const currentTarget = this.resolveLegacyTarget(
@@ -3843,8 +3950,8 @@ var PersonalCopilotContentReconciler = class {
3843
3950
  const target = this.capabilities.personalTargets[previous.kind];
3844
3951
  if (target === void 0) return;
3845
3952
  const linkPath = path15.join(target, path15.basename(previous.linkPath));
3846
- const stat15 = await fsp11.lstat(linkPath).catch(() => null);
3847
- if (!stat15?.isSymbolicLink()) return;
3953
+ const stat16 = await fsp11.lstat(linkPath).catch(() => null);
3954
+ if (!stat16?.isSymbolicLink()) return;
3848
3955
  const currentTarget = await fsp11.readlink(linkPath).catch(() => null);
3849
3956
  if (currentTarget !== previous.sourcePath) return;
3850
3957
  await fsp11.rm(linkPath, { force: true });
@@ -3923,16 +4030,6 @@ import * as path17 from "path";
3923
4030
  import { createHash as createHash2 } from "crypto";
3924
4031
  import * as fsp12 from "fs/promises";
3925
4032
  import * as path16 from "path";
3926
-
3927
- // src/copilot-content/types.ts
3928
- function getWorkspaceManifestSources(manifest) {
3929
- return manifest.version === 1 ? manifest.repositories.map((repository) => ({ ...repository, type: "git" })) : manifest.sources;
3930
- }
3931
- function getWorkspaceManifestPluginSourceId(manifest, plugin) {
3932
- return manifest.version === 1 ? plugin.repository : plugin.source;
3933
- }
3934
-
3935
- // src/copilot-content/plugin-resolver.ts
3936
4033
  var AWESOME_COPILOT_NAMESPACE = "com.github.awesome-copilot";
3937
4034
  var PATH_KINDS = {
3938
4035
  agent: "agents",
@@ -3946,6 +4043,7 @@ var EXECUTABLE_SUFFIXES = /* @__PURE__ */ new Set([".py", ".rb"]);
3946
4043
  var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".vscode", "dist", "build", "out", "plugins"]);
3947
4044
  async function resolveWorkspaceContentPlan(input) {
3948
4045
  const entries = [];
4046
+ const conflicts = [];
3949
4047
  const seenTargets = /* @__PURE__ */ new Map();
3950
4048
  const sources = getWorkspaceManifestSources(input.manifest);
3951
4049
  for (const plugin of input.manifest.plugins) {
@@ -3995,15 +4093,17 @@ async function resolveWorkspaceContentPlan(input) {
3995
4093
  const targetKey = entry.kind === "mcp" || entry.kind === "hook" ? `${entry.kind}:${plugin.id}:${entry.name}` : `${entry.kind}:${entry.name}`;
3996
4094
  const owner = seenTargets.get(targetKey);
3997
4095
  if (owner !== void 0) {
3998
- throw new Error(
3999
- `Duplicate ${entry.kind} target ${entry.name} from plugins ${owner} and ${plugin.id}`
4000
- );
4096
+ conflicts.push({
4097
+ identity: entry.identity,
4098
+ message: `Duplicate ${entry.kind} target ${entry.name} from plugins ${owner} and ${plugin.id}`
4099
+ });
4100
+ continue;
4001
4101
  }
4002
4102
  seenTargets.set(targetKey, plugin.id);
4003
4103
  entries.push(entry);
4004
4104
  }
4005
4105
  }
4006
- return { entries };
4106
+ return { entries, conflicts };
4007
4107
  }
4008
4108
  async function resolvePluginEntriesLenient(input) {
4009
4109
  const namespace = input.manifest.extensions[AWESOME_COPILOT_NAMESPACE];
@@ -4013,6 +4113,7 @@ async function resolvePluginEntriesLenient(input) {
4013
4113
  path16.join(input.repoRoot, "plugins", input.pluginId),
4014
4114
  input.repositoryId,
4015
4115
  input.pluginId,
4116
+ void 0,
4016
4117
  input.manifest
4017
4118
  );
4018
4119
  }
@@ -4021,9 +4122,9 @@ async function resolvePluginEntriesLenient(input) {
4021
4122
  if (kind === "mcp" || kind === "hook") continue;
4022
4123
  const declared = namespace[manifestKey];
4023
4124
  if (!Array.isArray(declared)) continue;
4024
- for (const relative4 of declared) {
4025
- if (typeof relative4 !== "string") continue;
4026
- const effective = await resolveDeclaredRelative(input.repoRoot, relative4);
4125
+ for (const relative5 of declared) {
4126
+ if (typeof relative5 !== "string") continue;
4127
+ const effective = await resolveDeclaredRelative(input.repoRoot, relative5);
4027
4128
  const sourcePath = path16.resolve(input.repoRoot, effective);
4028
4129
  if (!sourcePath.startsWith(`${input.repoRoot}${path16.sep}`)) continue;
4029
4130
  try {
@@ -4063,10 +4164,12 @@ async function tryReadPluginManifest(repoRoot, pluginId) {
4063
4164
  function selectedKind(artifacts, kind) {
4064
4165
  return artifacts?.[kind] !== false;
4065
4166
  }
4066
- async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, pluginId, manifest) {
4167
+ async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, pluginId, artifacts, manifest) {
4067
4168
  const entries = [];
4068
4169
  const seen = /* @__PURE__ */ new Set();
4170
+ const wants = artifacts === void 0 || Object.keys(artifacts).length === 0 ? () => true : (kind) => artifacts[kind] === true;
4069
4171
  const push = async (kind, base, subdir, options = {}) => {
4172
+ if (!wants(kind)) return;
4070
4173
  const dir = path16.join(base, subdir);
4071
4174
  const dirents = await fsp12.readdir(dir, { withFileTypes: true }).catch(() => []);
4072
4175
  for (const dirent of dirents) {
@@ -4093,11 +4196,18 @@ async function resolveConventionEntries(repoRoot, pluginDir, repositoryId, plugi
4093
4196
  await push("prompt", base, "commands");
4094
4197
  await push("hook", base, "hooks");
4095
4198
  }
4096
- return entries;
4199
+ const byTarget = /* @__PURE__ */ new Map();
4200
+ for (const entry of entries) {
4201
+ const key = `${entry.kind}:${entry.name}`;
4202
+ if (!byTarget.has(key)) {
4203
+ byTarget.set(key, entry);
4204
+ }
4205
+ }
4206
+ return [...byTarget.values()];
4097
4207
  }
4098
- async function resolveDeclaredRelative(repoRoot, relative4) {
4099
- if (await pathExists2(path16.resolve(repoRoot, relative4))) return relative4;
4100
- const normalized = relative4.replace(/^\.\//, "");
4208
+ async function resolveDeclaredRelative(repoRoot, relative5) {
4209
+ if (await pathExists2(path16.resolve(repoRoot, relative5))) return relative5;
4210
+ const normalized = relative5.replace(/^\.\//, "");
4101
4211
  if (normalized.startsWith("agents/") && normalized.endsWith(".md")) {
4102
4212
  const base = path16.basename(normalized, ".md");
4103
4213
  const candidate = path16.join(path16.dirname(normalized), `${base}.agent.md`);
@@ -4105,7 +4215,7 @@ async function resolveDeclaredRelative(repoRoot, relative4) {
4105
4215
  return `./${candidate}`;
4106
4216
  }
4107
4217
  }
4108
- return relative4;
4218
+ return relative5;
4109
4219
  }
4110
4220
  async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, artifacts, pluginManifest) {
4111
4221
  const namespace = pluginManifest.extensions[AWESOME_COPILOT_NAMESPACE];
@@ -4115,6 +4225,7 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
4115
4225
  path16.join(repoRoot, "plugins", pluginId),
4116
4226
  repositoryId,
4117
4227
  pluginId,
4228
+ artifacts,
4118
4229
  pluginManifest
4119
4230
  );
4120
4231
  }
@@ -4123,8 +4234,8 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
4123
4234
  if (!selectedKind(artifacts, kind)) continue;
4124
4235
  const declared = namespace[manifestKey];
4125
4236
  if (!Array.isArray(declared)) continue;
4126
- for (const relative4 of declared) {
4127
- if (typeof relative4 !== "string") {
4237
+ for (const relative5 of declared) {
4238
+ if (typeof relative5 !== "string") {
4128
4239
  throw new Error(`Plugin ${pluginId} declares a non-string ${manifestKey} path`);
4129
4240
  }
4130
4241
  entries.push(
@@ -4133,7 +4244,7 @@ async function resolvePluginManifestEntries(repoRoot, repositoryId, pluginId, ar
4133
4244
  repositoryId,
4134
4245
  pluginId,
4135
4246
  kind,
4136
- await resolveDeclaredRelative(repoRoot, relative4),
4247
+ await resolveDeclaredRelative(repoRoot, relative5),
4137
4248
  pluginManifest
4138
4249
  )
4139
4250
  );
@@ -4239,23 +4350,23 @@ async function findFlatFile(repoRoot, filename, preferredDir) {
4239
4350
  }
4240
4351
  return null;
4241
4352
  }
4242
- async function materializePathEntry(repoRoot, repositoryId, pluginId, kind, relative4, pluginManifest) {
4243
- const sourcePath = path16.resolve(repoRoot, relative4);
4353
+ async function materializePathEntry(repoRoot, repositoryId, pluginId, kind, relative5, pluginManifest) {
4354
+ const sourcePath = path16.resolve(repoRoot, relative5);
4244
4355
  if (sourcePath !== repoRoot && !sourcePath.startsWith(`${repoRoot}${path16.sep}`)) {
4245
- throw new Error(`Plugin ${pluginId} path escapes repository root: ${relative4}`);
4356
+ throw new Error(`Plugin ${pluginId} path escapes repository root: ${relative5}`);
4246
4357
  }
4247
- const stat15 = await fsp12.stat(sourcePath).catch(() => null);
4248
- if (!stat15) {
4249
- throw new Error(`Plugin ${pluginId} source missing: ${relative4}`);
4358
+ const stat16 = await fsp12.stat(sourcePath).catch(() => null);
4359
+ if (!stat16) {
4360
+ throw new Error(`Plugin ${pluginId} source missing: ${relative5}`);
4250
4361
  }
4251
4362
  if (kind === "skill") {
4252
- if (!stat15.isDirectory() || !await pathExists2(path16.join(sourcePath, "SKILL.md"))) {
4253
- throw new Error(`Plugin ${pluginId} skill source has no SKILL.md: ${relative4}`);
4363
+ if (!stat16.isDirectory() || !await pathExists2(path16.join(sourcePath, "SKILL.md"))) {
4364
+ throw new Error(`Plugin ${pluginId} skill source has no SKILL.md: ${relative5}`);
4254
4365
  }
4255
4366
  return [await buildEntry(repositoryId, pluginId, kind, sourcePath, false, pluginManifest)];
4256
4367
  }
4257
- if (!stat15.isFile()) {
4258
- throw new Error(`Plugin ${pluginId} ${kind} source is not a file: ${relative4}`);
4368
+ if (!stat16.isFile()) {
4369
+ throw new Error(`Plugin ${pluginId} ${kind} source is not a file: ${relative5}`);
4259
4370
  }
4260
4371
  return [await buildEntry(repositoryId, pluginId, kind, sourcePath, true, pluginManifest)];
4261
4372
  }
@@ -4377,9 +4488,9 @@ async function resolveCatalogPackage(sourceId, repoRoot, pluginId, cache) {
4377
4488
  }
4378
4489
  const cacheKey = `${sourceId}/${pluginId}`;
4379
4490
  try {
4380
- const stat15 = await fsp13.stat(manifestPath);
4491
+ const stat16 = await fsp13.stat(manifestPath);
4381
4492
  const cached = cache.get(cacheKey);
4382
- if (cached && cached.mtimeMs === stat15.mtimeMs && cached.size === stat15.size) {
4493
+ if (cached && cached.mtimeMs === stat16.mtimeMs && cached.size === stat16.size) {
4383
4494
  return cached.pkg;
4384
4495
  }
4385
4496
  const parsed = JSON.parse(await fsp13.readFile(manifestPath, "utf8"));
@@ -4408,7 +4519,7 @@ async function resolveCatalogPackage(sourceId, repoRoot, pluginId, cache) {
4408
4519
  ...typeof parsed.version === "string" && parsed.version.trim().length > 0 ? { version: parsed.version.trim() } : {},
4409
4520
  artifacts: pluginEntries.map(toArtifactSummary)
4410
4521
  };
4411
- cache.set(cacheKey, { mtimeMs: stat15.mtimeMs, size: stat15.size, pkg });
4522
+ cache.set(cacheKey, { mtimeMs: stat16.mtimeMs, size: stat16.size, pkg });
4412
4523
  return pkg;
4413
4524
  } catch {
4414
4525
  cache.delete(cacheKey);
@@ -4495,8 +4606,8 @@ var CopilotSourceCatalogService = class {
4495
4606
  * exactly like git checkouts, so availability is a directory check.
4496
4607
  */
4497
4608
  async isSourceAvailable(sourceId) {
4498
- const stat15 = await fsp14.stat(path18.join(this.reposDir, sourceId)).catch(() => null);
4499
- return stat15?.isDirectory() === true;
4609
+ const stat16 = await fsp14.stat(path18.join(this.reposDir, sourceId)).catch(() => null);
4610
+ return stat16?.isDirectory() === true;
4500
4611
  }
4501
4612
  /**
4502
4613
  * Deterministic, side-effect-free update preview: compares the
@@ -4573,9 +4684,243 @@ var CopilotSourceCatalogService = class {
4573
4684
  }
4574
4685
  };
4575
4686
 
4576
- // src/copilot-content/workspace-copilot-content-reconciler.ts
4687
+ // src/copilot-content/workspace-content-detector.ts
4688
+ import { createHash as createHash3 } from "crypto";
4577
4689
  import * as fsp15 from "fs/promises";
4578
4690
  import * as path19 from "path";
4691
+ function unmanagedDetectionSignature(detection) {
4692
+ const parts = [
4693
+ ...detection.adoptions.map((a) => `a:${a.kind}:${a.name}`),
4694
+ ...detection.ambiguous.map((a) => `b:${a.kind}:${a.name}`),
4695
+ ...detection.integrations.map((i) => `i:${i.kind}:${i.pluginId}`)
4696
+ ].sort();
4697
+ return parts.join("|");
4698
+ }
4699
+ function coveredTargetsOf(manifest) {
4700
+ const targets = /* @__PURE__ */ new Set();
4701
+ const extensions = manifest.extensions;
4702
+ if (!extensions || typeof extensions !== "object") return targets;
4703
+ for (const namespace of Object.values(extensions)) {
4704
+ if (!namespace || typeof namespace !== "object") continue;
4705
+ for (const [field, refs] of Object.entries(namespace)) {
4706
+ if (!Array.isArray(refs)) continue;
4707
+ for (const ref of refs) {
4708
+ if (typeof ref !== "string" || !ref.startsWith("./")) continue;
4709
+ const normalized = ref.replace(/^\.\//, "").replace(/\/+$/, "");
4710
+ const segments = normalized.split("/");
4711
+ const dir = segments[0];
4712
+ const base = (segments[1] ?? "").replace(/\.(agent\.)?md$/i, "");
4713
+ if (!base) continue;
4714
+ if (dir === "skills" || field === "skills") targets.add(`skill:${base}`);
4715
+ if (dir === "agents" || field === "agents") targets.add(`agent:${base}`);
4716
+ }
4717
+ }
4718
+ }
4719
+ return targets;
4720
+ }
4721
+ async function findPluginCoveringArtifact(repoRoot, kind, name) {
4722
+ const root = path19.resolve(repoRoot);
4723
+ const pluginsDir = path19.join(root, "plugins");
4724
+ const dirents = await fsp15.readdir(pluginsDir, { withFileTypes: true }).catch(() => []);
4725
+ const target = `${kind}:${name}`;
4726
+ for (const dirent of dirents) {
4727
+ if (!dirent.isDirectory()) continue;
4728
+ const pluginDir = path19.join(pluginsDir, dirent.name);
4729
+ let pluginName;
4730
+ let pluginManifest;
4731
+ for (const manifestRel of ["plugin.json", path19.join(".plugin", "plugin.json")]) {
4732
+ const manifestPath = path19.resolve(pluginDir, manifestRel);
4733
+ if (!manifestPath.startsWith(root + path19.sep)) continue;
4734
+ try {
4735
+ const parsed = JSON.parse(await fsp15.readFile(manifestPath, "utf8"));
4736
+ if (typeof parsed?.name !== "string" || parsed.name.length === 0) continue;
4737
+ pluginName = parsed.name;
4738
+ pluginManifest = parsed;
4739
+ break;
4740
+ } catch {
4741
+ }
4742
+ }
4743
+ if (pluginName === void 0 || pluginManifest === void 0) continue;
4744
+ if (coveredTargetsOf(pluginManifest).has(target)) {
4745
+ return pluginName;
4746
+ }
4747
+ const conventionBase = pluginManifest.extensions && typeof pluginManifest.extensions === "object" ? void 0 : path19.join(pluginDir, kind === "skill" ? "skills" : "agents");
4748
+ if (conventionBase !== void 0) {
4749
+ const conventionPath = path19.resolve(
4750
+ conventionBase,
4751
+ kind === "skill" ? path19.join(name, "SKILL.md") : `${name}.agent.md`
4752
+ );
4753
+ if (!conventionPath.startsWith(root + path19.sep)) continue;
4754
+ if (await fsp15.stat(conventionPath).then(
4755
+ () => true,
4756
+ () => false
4757
+ )) {
4758
+ return pluginName;
4759
+ }
4760
+ }
4761
+ }
4762
+ return null;
4763
+ }
4764
+ function dedupeAdoptionsByPluginManifests(entries, pluginManifests) {
4765
+ const covered = /* @__PURE__ */ new Map();
4766
+ for (const [key, manifest] of pluginManifests) {
4767
+ covered.set(key, coveredTargetsOf(manifest));
4768
+ }
4769
+ const kept = [];
4770
+ const dropped = [];
4771
+ for (const entry of entries) {
4772
+ let owner;
4773
+ for (const other of entries) {
4774
+ if (other.repoId !== entry.repoId || other.name === entry.name) continue;
4775
+ const targets = covered.get(`${entry.repoId}::${other.name}`);
4776
+ if (targets?.has(`${entry.kind}:${entry.name}`)) {
4777
+ owner = other.name;
4778
+ break;
4779
+ }
4780
+ }
4781
+ if (owner === void 0) {
4782
+ kept.push(entry);
4783
+ } else {
4784
+ dropped.push({ entry, coveredBy: owner });
4785
+ }
4786
+ }
4787
+ return { kept, dropped };
4788
+ }
4789
+ async function digestFile2(file) {
4790
+ const hash = createHash3("sha256");
4791
+ hash.update(await fsp15.readFile(file));
4792
+ return hash.digest("hex");
4793
+ }
4794
+ async function digestTree(dir) {
4795
+ const hash = createHash3("sha256");
4796
+ await digestInto2(dir, hash);
4797
+ return hash.digest("hex");
4798
+ }
4799
+ async function digestInto2(dir, hash) {
4800
+ const dirents = await fsp15.readdir(dir, { withFileTypes: true });
4801
+ dirents.sort((a, b) => a.name.localeCompare(b.name));
4802
+ for (const dirent of dirents) {
4803
+ const child = path19.join(dir, dirent.name);
4804
+ hash.update(dirent.name);
4805
+ if (dirent.isFile()) {
4806
+ hash.update(await fsp15.readFile(child));
4807
+ } else if (dirent.isDirectory()) {
4808
+ await digestInto2(child, hash);
4809
+ }
4810
+ }
4811
+ }
4812
+ async function digestCatalogEntry(entry) {
4813
+ return entry.dir === entry.manifestPath ? digestFile2(entry.manifestPath) : digestTree(entry.dir);
4814
+ }
4815
+ function entryNameFromLinkBasename(basename12, kind) {
4816
+ if (kind === "agent") {
4817
+ return basename12.replace(/\.agent\.md$/i, "");
4818
+ }
4819
+ return basename12;
4820
+ }
4821
+ async function readServicemeIdentities(configPath) {
4822
+ try {
4823
+ const raw = await fsp15.readFile(configPath, "utf8");
4824
+ const parsed = JSON.parse(raw);
4825
+ if (parsed._serviceme && typeof parsed._serviceme === "object") {
4826
+ return new Map(Object.entries(parsed._serviceme));
4827
+ }
4828
+ } catch {
4829
+ }
4830
+ return /* @__PURE__ */ new Map();
4831
+ }
4832
+ async function detectUnmanagedWorkspaceContent(input) {
4833
+ const detection = {
4834
+ adoptions: [],
4835
+ ambiguous: [],
4836
+ integrations: [],
4837
+ unmatched: []
4838
+ };
4839
+ const catalogByName = /* @__PURE__ */ new Map();
4840
+ for (const entry of input.catalog) {
4841
+ if (!input.knownRepoIds.has(entry.repoId)) continue;
4842
+ const key = `${entry.kind}:${entry.name}`;
4843
+ catalogByName.set(key, [...catalogByName.get(key) ?? [], entry]);
4844
+ }
4845
+ for (const kind of ["skill", "agent"]) {
4846
+ const scanDir = path19.join(
4847
+ input.workspaceDir,
4848
+ ".github",
4849
+ kind === "agent" ? "agents" : "skills"
4850
+ );
4851
+ const children = await fsp15.readdir(scanDir, { withFileTypes: true }).catch(() => []);
4852
+ for (const child of children) {
4853
+ const childPath = path19.join(scanDir, child.name);
4854
+ const name = entryNameFromLinkBasename(child.name, kind);
4855
+ const stat16 = await fsp15.lstat(childPath).catch(() => null);
4856
+ if (!stat16) continue;
4857
+ if (stat16.isSymbolicLink()) {
4858
+ const target = await fsp15.readlink(childPath);
4859
+ const resolved = path19.resolve(scanDir, target);
4860
+ const relative5 = path19.relative(input.reposDir, resolved);
4861
+ if (relative5.startsWith("..") || path19.isAbsolute(relative5)) {
4862
+ detection.unmatched.push({ name, kind, reason: "foreign-symlink" });
4863
+ continue;
4864
+ }
4865
+ const repoId = relative5.split(path19.sep)[0] ?? "";
4866
+ if (!repoId || !input.knownRepoIds.has(repoId)) {
4867
+ detection.unmatched.push({ name, kind, reason: "foreign-symlink" });
4868
+ continue;
4869
+ }
4870
+ detection.adoptions.push({ repoId, name, kind, source: "symlink" });
4871
+ continue;
4872
+ }
4873
+ const digest = stat16.isFile() ? await digestFile2(childPath) : await digestTree(childPath);
4874
+ const candidates = catalogByName.get(`${kind}:${name}`) ?? [];
4875
+ const matches = [];
4876
+ for (const candidate of candidates) {
4877
+ if (await digestCatalogEntry(candidate) === digest) {
4878
+ matches.push(candidate.repoId);
4879
+ }
4880
+ }
4881
+ if (matches.length === 1) {
4882
+ detection.adoptions.push({
4883
+ repoId: matches[0] ?? "",
4884
+ name,
4885
+ kind,
4886
+ source: "fingerprint"
4887
+ });
4888
+ } else if (matches.length > 1) {
4889
+ detection.ambiguous.push({ name, kind, candidates: matches.sort() });
4890
+ } else {
4891
+ detection.unmatched.push({ name, kind, reason: "no-catalog-match" });
4892
+ }
4893
+ }
4894
+ }
4895
+ for (const [configFile, kind] of [
4896
+ ["hooks.serviceme.json", "hook"],
4897
+ ["mcp.serviceme.json", "mcp"]
4898
+ ]) {
4899
+ const identities = await readServicemeIdentities(
4900
+ path19.join(input.workspaceDir, ".vscode", configFile)
4901
+ );
4902
+ const seen = /* @__PURE__ */ new Set();
4903
+ for (const identity of identities.keys()) {
4904
+ const segments = identity.split("::");
4905
+ if (segments.length < 3) continue;
4906
+ const repoId = segments[0] ?? "";
4907
+ const pluginId = segments[1] ?? "";
4908
+ const kindPart = (segments[2] ?? "").split(":")[0] ?? "";
4909
+ if (!repoId || !pluginId || kindPart !== kind || !input.knownRepoIds.has(repoId)) {
4910
+ continue;
4911
+ }
4912
+ const dedupe = `${repoId}::${pluginId}::${kind}`;
4913
+ if (seen.has(dedupe)) continue;
4914
+ seen.add(dedupe);
4915
+ detection.integrations.push({ repoId, pluginId, kind });
4916
+ }
4917
+ }
4918
+ return detection;
4919
+ }
4920
+
4921
+ // src/copilot-content/workspace-copilot-content-reconciler.ts
4922
+ import * as fsp16 from "fs/promises";
4923
+ import * as path20 from "path";
4579
4924
  var LINK_KINDS = /* @__PURE__ */ new Set(["agent", "skill", "instruction", "prompt"]);
4580
4925
  var INTEGRATION_KINDS = /* @__PURE__ */ new Set(["mcp", "hook"]);
4581
4926
  var WorkspaceCopilotContentReconciler = class {
@@ -4614,21 +4959,28 @@ var WorkspaceCopilotContentReconciler = class {
4614
4959
  }
4615
4960
  return { changed: false, entries };
4616
4961
  }
4617
- const reposDir = path19.join(this.homeDir, "repos");
4962
+ const reposDir = path20.join(this.homeDir, "repos");
4618
4963
  const plan = await resolveWorkspaceContentPlan({ manifest, reposDir });
4964
+ const sharedDisabled = /* @__PURE__ */ new Set();
4965
+ for (const plugin of manifest.plugins) {
4966
+ for (const target of plugin.disabledArtifacts ?? []) {
4967
+ sharedDisabled.add(`${plugin.id}|${target}`);
4968
+ }
4969
+ }
4619
4970
  let linkEntries = plan.entries.filter((entry) => LINK_KINDS.has(entry.kind));
4620
4971
  let integrationEntries = plan.entries.filter((entry) => INTEGRATION_KINDS.has(entry.kind));
4972
+ const isPlanEntryDisabled = (entry) => sharedDisabled.has(`${entry.pluginId}|${entry.kind}:${entry.name}`) || this.isDisabled !== void 0 && disabledIdentities.has(entry.identity);
4973
+ const disabledIdentities = /* @__PURE__ */ new Set();
4621
4974
  if (this.isDisabled !== void 0) {
4622
- const disabled = /* @__PURE__ */ new Set();
4623
4975
  for (const entry of plan.entries) {
4624
4976
  if (await this.isDisabled(entry.identity)) {
4625
- disabled.add(entry.identity);
4977
+ disabledIdentities.add(entry.identity);
4626
4978
  }
4627
4979
  }
4628
- if (disabled.size > 0) {
4629
- linkEntries = linkEntries.filter((entry) => !disabled.has(entry.identity));
4630
- integrationEntries = integrationEntries.filter((entry) => !disabled.has(entry.identity));
4631
- }
4980
+ }
4981
+ if (sharedDisabled.size > 0 || disabledIdentities.size > 0) {
4982
+ linkEntries = linkEntries.filter((entry) => !isPlanEntryDisabled(entry));
4983
+ integrationEntries = integrationEntries.filter((entry) => !isPlanEntryDisabled(entry));
4632
4984
  }
4633
4985
  const scopeOverlaps = previousState.entries.filter(
4634
4986
  (entry) => LINK_KINDS.has(entry.kind) && plan.entries.some((planned) => planned.identity === entry.identity) && !isWorkspaceScopePath(entry.linkPath)
@@ -4644,22 +4996,25 @@ var WorkspaceCopilotContentReconciler = class {
4644
4996
  });
4645
4997
  return { changed: false, entries };
4646
4998
  }
4647
- const excludeStore = new WorkspaceExcludeStore({ workspaceDir: this.workspaceDir });
4999
+ const excludeStore = new WorkspaceExcludeStore({
5000
+ workspaceDir: this.workspaceDir
5001
+ });
4648
5002
  const linkMaterialized = await this.materializer.reconcile({
4649
5003
  workspaceDir: this.workspaceDir,
4650
5004
  entries: linkEntries,
4651
5005
  previousState
4652
5006
  });
5007
+ const conflictedIds = new Set(plan.conflicts.map((conflict) => conflict.identity));
4653
5008
  const effectiveLinkIds = new Set(linkEntries.map((entry) => entry.identity));
4654
5009
  let removedLinks = 0;
4655
5010
  for (const previous of previousState.entries) {
4656
5011
  if (!LINK_KINDS.has(previous.kind) || effectiveLinkIds.has(previous.identity)) {
4657
5012
  continue;
4658
5013
  }
4659
- if (!isWorkspaceScopePath(previous.linkPath)) {
5014
+ if (conflictedIds.has(previous.identity) || !isWorkspaceScopePath(previous.linkPath)) {
4660
5015
  continue;
4661
5016
  }
4662
- await fsp15.rm(path19.resolve(this.workspaceDir, previous.linkPath), {
5017
+ await fsp16.rm(path20.resolve(this.workspaceDir, previous.linkPath), {
4663
5018
  force: true,
4664
5019
  recursive: true
4665
5020
  });
@@ -4692,7 +5047,15 @@ var WorkspaceCopilotContentReconciler = class {
4692
5047
  await excludeStore.reconcile(managedPaths);
4693
5048
  return {
4694
5049
  changed: linkMaterialized.changed || integrationMaterialized.changed || removedLinks > 0,
4695
- entries: [...linkMaterialized.entries, ...integrationMaterialized.entries]
5050
+ entries: [
5051
+ ...linkMaterialized.entries,
5052
+ ...integrationMaterialized.entries,
5053
+ ...plan.conflicts.map((conflict) => ({
5054
+ identity: conflict.identity,
5055
+ status: "conflict",
5056
+ message: conflict.message
5057
+ }))
5058
+ ]
4696
5059
  };
4697
5060
  }
4698
5061
  /**
@@ -4737,14 +5100,18 @@ var WorkspaceCopilotContentReconciler = class {
4737
5100
  }
4738
5101
  try {
4739
5102
  if (entry.kind === "mcp") {
4740
- const raw = await fsp15.readFile(entry.sourcePath, "utf8");
5103
+ const raw = await fsp16.readFile(entry.sourcePath, "utf8");
4741
5104
  for (const server of parseMcpJson(raw)) {
4742
- await mcpAdapter.applyServer({ workspaceDir: this.workspaceDir, entry, server });
5105
+ await mcpAdapter.applyServer({
5106
+ workspaceDir: this.workspaceDir,
5107
+ entry,
5108
+ server
5109
+ });
4743
5110
  changed = true;
4744
5111
  }
4745
5112
  } else {
4746
- const hooksJsonPath = await findRepoHooksJson(path19.resolve(entry.sourcePath, "..", ".."), entry.name) ?? path19.join(entry.sourcePath, "hooks.json");
4747
- const raw = await fsp15.readFile(hooksJsonPath, "utf8");
5113
+ const hooksJsonPath = await findRepoHooksJson(path20.resolve(entry.sourcePath, "..", ".."), entry.name) ?? path20.join(entry.sourcePath, "hooks.json");
5114
+ const raw = await fsp16.readFile(hooksJsonPath, "utf8");
4748
5115
  await hookAdapter.applyHook({
4749
5116
  workspaceDir: this.workspaceDir,
4750
5117
  entry,
@@ -4798,14 +5165,17 @@ var WorkspaceCopilotContentReconciler = class {
4798
5165
  return this.reconcile();
4799
5166
  }
4800
5167
  async defaultEnsureRepository(manifest) {
4801
- const reposDir = path19.join(this.homeDir, "repos");
5168
+ const reposDir = path20.join(this.homeDir, "repos");
4802
5169
  const sources = /* @__PURE__ */ new Map();
4803
5170
  for (const source of getWorkspaceManifestSources(manifest)) {
4804
- const localPath = path19.resolve(reposDir, source.id);
4805
- const stat15 = await fsp15.stat(localPath).catch(() => null);
5171
+ const localPath = path20.resolve(reposDir, source.id);
5172
+ const stat16 = await fsp16.stat(localPath).catch(() => null);
4806
5173
  sources.set(
4807
5174
  source.id,
4808
- stat15?.isDirectory() ? { ready: true, localPath } : { ready: false, error: "Source not materialized under ~/.serviceme/repos" }
5175
+ stat16?.isDirectory() ? { ready: true, localPath } : {
5176
+ ready: false,
5177
+ error: "Source not materialized under ~/.serviceme/repos"
5178
+ }
4809
5179
  );
4810
5180
  }
4811
5181
  return sources;
@@ -4854,7 +5224,7 @@ function declaredPinnedVersion(manifest, sourceId) {
4854
5224
  }
4855
5225
 
4856
5226
  // src/device/deviceAuth.ts
4857
- import { createHash as createHash3 } from "crypto";
5227
+ import { createHash as createHash4 } from "crypto";
4858
5228
  var DeviceAuthHeaders = {
4859
5229
  deviceId: "x-ms-device-id",
4860
5230
  deviceSecret: "x-ms-device-secret",
@@ -4870,7 +5240,7 @@ function createDeviceRequestSignature(params) {
4870
5240
  params.body,
4871
5241
  params.secret
4872
5242
  ].join("\n");
4873
- return createHash3("sha256").update(basis).digest("hex");
5243
+ return createHash4("sha256").update(basis).digest("hex");
4874
5244
  }
4875
5245
  function buildSignedHeaders(params) {
4876
5246
  const timestamp = params.timestamp ?? Date.now();
@@ -4894,7 +5264,7 @@ function buildSignedHeaders(params) {
4894
5264
  import { randomBytes } from "crypto";
4895
5265
 
4896
5266
  // src/device/InstallationId.ts
4897
- import { createHash as createHash4, randomUUID } from "crypto";
5267
+ import { createHash as createHash5, randomUUID } from "crypto";
4898
5268
  import * as os3 from "os";
4899
5269
  function fingerprintMaterial() {
4900
5270
  let username = "unknown";
@@ -4913,7 +5283,7 @@ function fingerprintMaterial() {
4913
5283
  }
4914
5284
  function deriveInstallationId() {
4915
5285
  const material = fingerprintMaterial();
4916
- const digest = createHash4("sha256").update(material).digest("hex");
5286
+ const digest = createHash5("sha256").update(material).digest("hex");
4917
5287
  return formatAsV4(digest.slice(0, 32));
4918
5288
  }
4919
5289
  function randomInstallationId() {
@@ -5148,9 +5518,9 @@ async function emptyIdentity(random) {
5148
5518
  }
5149
5519
 
5150
5520
  // src/device/IdentityStore.ts
5151
- import * as fsp16 from "fs/promises";
5521
+ import * as fsp17 from "fs/promises";
5152
5522
  import * as os4 from "os";
5153
- import * as path20 from "path";
5523
+ import * as path21 from "path";
5154
5524
  import { setTimeout as delay } from "timers/promises";
5155
5525
 
5156
5526
  // src/device/types.ts
@@ -5166,7 +5536,7 @@ var TMP_SUFFIX = ".tmp";
5166
5536
  var FsIdentityFileBackend = class {
5167
5537
  async exists(filePath) {
5168
5538
  try {
5169
- await fsp16.access(filePath);
5539
+ await fsp17.access(filePath);
5170
5540
  return true;
5171
5541
  } catch {
5172
5542
  return false;
@@ -5174,7 +5544,7 @@ var FsIdentityFileBackend = class {
5174
5544
  }
5175
5545
  async read(filePath) {
5176
5546
  try {
5177
- const buf = await fsp16.readFile(filePath, "utf8");
5547
+ const buf = await fsp17.readFile(filePath, "utf8");
5178
5548
  const parsed = JSON.parse(buf);
5179
5549
  return migratePersistedIdentity(parsed);
5180
5550
  } catch (err) {
@@ -5183,23 +5553,23 @@ var FsIdentityFileBackend = class {
5183
5553
  }
5184
5554
  }
5185
5555
  async write(filePath, payload) {
5186
- await fsp16.mkdir(path20.dirname(filePath), { recursive: true });
5556
+ await fsp17.mkdir(path21.dirname(filePath), { recursive: true });
5187
5557
  const tmpPath = `${filePath}${TMP_SUFFIX}`;
5188
5558
  const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
5189
- await fsp16.rm(tmpPath, { force: true });
5190
- const handle = await fsp16.open(tmpPath, "w", FILE_MODE);
5559
+ await fsp17.rm(tmpPath, { force: true });
5560
+ const handle = await fsp17.open(tmpPath, "w", FILE_MODE);
5191
5561
  try {
5192
5562
  await handle.writeFile(bytes);
5193
5563
  await handle.sync();
5194
5564
  } finally {
5195
5565
  await handle.close();
5196
5566
  }
5197
- await fsp16.rename(tmpPath, filePath);
5198
- await fsp16.chmod(filePath, FILE_MODE).catch(() => void 0);
5567
+ await fsp17.rename(tmpPath, filePath);
5568
+ await fsp17.chmod(filePath, FILE_MODE).catch(() => void 0);
5199
5569
  return { bytesWritten: bytes.byteLength, tmpPath };
5200
5570
  }
5201
5571
  async delete(filePath) {
5202
- await fsp16.rm(filePath, { force: true });
5572
+ await fsp17.rm(filePath, { force: true });
5203
5573
  }
5204
5574
  };
5205
5575
  function migratePersistedIdentity(parsed) {
@@ -5237,7 +5607,7 @@ var FileLock = class {
5237
5607
  constructor(filePath, timeoutMs, retryMs) {
5238
5608
  this.acquired = false;
5239
5609
  this.dirPath = `${filePath}.lock`;
5240
- this.pidFilePath = path20.join(this.dirPath, LOCK_PID_FILE);
5610
+ this.pidFilePath = path21.join(this.dirPath, LOCK_PID_FILE);
5241
5611
  this.timeoutMs = timeoutMs;
5242
5612
  this.retryMs = retryMs;
5243
5613
  }
@@ -5245,8 +5615,8 @@ var FileLock = class {
5245
5615
  const start = Date.now();
5246
5616
  while (true) {
5247
5617
  try {
5248
- await fsp16.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
5249
- await fsp16.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
5618
+ await fsp17.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
5619
+ await fsp17.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
5250
5620
  this.acquired = true;
5251
5621
  return;
5252
5622
  } catch (err) {
@@ -5255,7 +5625,7 @@ var FileLock = class {
5255
5625
  }
5256
5626
  const stale = await this.isStaleLock();
5257
5627
  if (stale) {
5258
- await fsp16.rm(this.dirPath, { recursive: true, force: true });
5628
+ await fsp17.rm(this.dirPath, { recursive: true, force: true });
5259
5629
  continue;
5260
5630
  }
5261
5631
  if (Date.now() - start >= this.timeoutMs) {
@@ -5268,11 +5638,11 @@ var FileLock = class {
5268
5638
  async isStaleLock() {
5269
5639
  let pidStr;
5270
5640
  try {
5271
- pidStr = await fsp16.readFile(this.pidFilePath, "utf8");
5641
+ pidStr = await fsp17.readFile(this.pidFilePath, "utf8");
5272
5642
  } catch {
5273
5643
  try {
5274
- const stat15 = await fsp16.stat(this.dirPath);
5275
- return Date.now() - stat15.mtimeMs > LOCK_STALE_GRACE_MS;
5644
+ const stat16 = await fsp17.stat(this.dirPath);
5645
+ return Date.now() - stat16.mtimeMs > LOCK_STALE_GRACE_MS;
5276
5646
  } catch {
5277
5647
  return false;
5278
5648
  }
@@ -5284,7 +5654,7 @@ var FileLock = class {
5284
5654
  async release() {
5285
5655
  if (!this.acquired) return;
5286
5656
  this.acquired = false;
5287
- await fsp16.rm(this.dirPath, { recursive: true, force: true });
5657
+ await fsp17.rm(this.dirPath, { recursive: true, force: true });
5288
5658
  }
5289
5659
  };
5290
5660
  var IdentityStore = class {
@@ -5374,8 +5744,8 @@ var IdentityStore = class {
5374
5744
  * Useful when the bootstrap phase5 placeholder wasn't run yet.
5375
5745
  */
5376
5746
  async ensureHome() {
5377
- await fsp16.mkdir(getServicemeHome(), { recursive: true });
5378
- await fsp16.mkdir(path20.dirname(this.filePath), { recursive: true });
5747
+ await fsp17.mkdir(getServicemeHome(), { recursive: true });
5748
+ await fsp17.mkdir(path21.dirname(this.filePath), { recursive: true });
5379
5749
  }
5380
5750
  };
5381
5751
 
@@ -5487,15 +5857,15 @@ function projectMetadata(stored) {
5487
5857
  // src/drafts/index.ts
5488
5858
  import * as crypto from "crypto";
5489
5859
  import * as fs6 from "fs/promises";
5490
- import * as path23 from "path";
5860
+ import * as path24 from "path";
5491
5861
 
5492
5862
  // src/skill-store/index.ts
5493
5863
  import * as fs5 from "fs/promises";
5494
- import * as path22 from "path";
5864
+ import * as path23 from "path";
5495
5865
 
5496
5866
  // src/repo-layout/index.ts
5497
5867
  import * as fs4 from "fs/promises";
5498
- import * as path21 from "path";
5868
+ import * as path22 from "path";
5499
5869
 
5500
5870
  // src/skill-store/index.ts
5501
5871
  function extractFrontmatter(raw) {
@@ -5572,7 +5942,7 @@ function generateDraftId() {
5572
5942
  }
5573
5943
  function resolveDraftDir(kind, id) {
5574
5944
  const root = kind === "skill" ? getSkillDraftsDir() : getAgentDraftsDir();
5575
- return path23.join(root, id);
5945
+ return path24.join(root, id);
5576
5946
  }
5577
5947
  var DraftsStore = class {
5578
5948
  /**
@@ -5600,10 +5970,10 @@ var DraftsStore = class {
5600
5970
  async tryReadSummary(kind, id) {
5601
5971
  const dir = resolveDraftDir(kind, id);
5602
5972
  const manifestFilename = kind === "skill" ? "SKILL.md" : "AGENT.md";
5603
- const manifestPath = path23.join(dir, manifestFilename);
5604
- let stat15;
5973
+ const manifestPath = path24.join(dir, manifestFilename);
5974
+ let stat16;
5605
5975
  try {
5606
- stat15 = await fs6.stat(manifestPath);
5976
+ stat16 = await fs6.stat(manifestPath);
5607
5977
  } catch {
5608
5978
  return null;
5609
5979
  }
@@ -5622,7 +5992,7 @@ var DraftsStore = class {
5622
5992
  dir,
5623
5993
  name,
5624
5994
  description,
5625
- modifiedAt: stat15.mtime.toISOString()
5995
+ modifiedAt: stat16.mtime.toISOString()
5626
5996
  };
5627
5997
  }
5628
5998
  /** Single draft detail (summary + all files). Throws when missing. */
@@ -5661,8 +6031,8 @@ var DraftsStore = class {
5661
6031
  await fs6.rm(dir, { recursive: true, force: true });
5662
6032
  await fs6.mkdir(dir, { recursive: true });
5663
6033
  for (const f of opts.files) {
5664
- const full = path23.join(dir, f.path);
5665
- await fs6.mkdir(path23.dirname(full), { recursive: true });
6034
+ const full = path24.join(dir, f.path);
6035
+ await fs6.mkdir(path24.dirname(full), { recursive: true });
5666
6036
  const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
5667
6037
  await fs6.writeFile(tmp, f.content, "utf8");
5668
6038
  await fs6.rename(tmp, full);
@@ -5690,7 +6060,7 @@ var DraftsStore = class {
5690
6060
  }
5691
6061
  for (const n of names) {
5692
6062
  if (n.endsWith(".tmp")) {
5693
- await fs6.rm(path23.join(dir, n), { force: true }).catch(() => void 0);
6063
+ await fs6.rm(path24.join(dir, n), { force: true }).catch(() => void 0);
5694
6064
  }
5695
6065
  }
5696
6066
  }
@@ -5704,12 +6074,12 @@ async function collectRecursive(absDir, root, out) {
5704
6074
  }
5705
6075
  for (const d of dirents) {
5706
6076
  if (d.name.endsWith(".tmp")) continue;
5707
- const full = path23.join(absDir, d.name);
6077
+ const full = path24.join(absDir, d.name);
5708
6078
  if (d.isDirectory()) {
5709
6079
  await collectRecursive(full, root, out);
5710
6080
  } else if (d.isFile()) {
5711
6081
  const content = await fs6.readFile(full, "utf8");
5712
- out.push({ path: path23.relative(root, full), content });
6082
+ out.push({ path: path24.relative(root, full), content });
5713
6083
  }
5714
6084
  }
5715
6085
  }
@@ -5717,7 +6087,7 @@ async function collectRecursive(absDir, root, out) {
5717
6087
  // src/env/environmentInspector.ts
5718
6088
  import { existsSync } from "fs";
5719
6089
  import { homedir as homedir2 } from "os";
5720
- import { join as join24 } from "path";
6090
+ import { join as join25 } from "path";
5721
6091
  import {
5722
6092
  createServicemeError as createServicemeError4,
5723
6093
  KNOWN_ENVIRONMENT_TOOLS
@@ -5952,7 +6322,7 @@ var EnvironmentInspector = class {
5952
6322
  candidates.push(dir.replace(/^\$HOME/, homedir2()));
5953
6323
  }
5954
6324
  for (const candidate of candidates) {
5955
- const scriptPath = join24(candidate, "nvm.sh");
6325
+ const scriptPath = join25(candidate, "nvm.sh");
5956
6326
  if (existsSync(scriptPath)) {
5957
6327
  return scriptPath;
5958
6328
  }
@@ -6056,7 +6426,7 @@ var EnvironmentInspector = class {
6056
6426
 
6057
6427
  // src/git-client/index.ts
6058
6428
  import { spawn as spawn3 } from "child_process";
6059
- import * as path24 from "path";
6429
+ import * as path25 from "path";
6060
6430
 
6061
6431
  // src/git-client/types.ts
6062
6432
  var GitError = class extends Error {
@@ -6250,8 +6620,8 @@ var GitClient = class {
6250
6620
  throw new GitError(["rev-parse", "--git-path", pathspec], result);
6251
6621
  }
6252
6622
  const resolved = result.stdout.trim();
6253
- if (!path24.isAbsolute(resolved)) {
6254
- return path24.resolve(localPath, resolved);
6623
+ if (!path25.isAbsolute(resolved)) {
6624
+ return path25.resolve(localPath, resolved);
6255
6625
  }
6256
6626
  return resolved;
6257
6627
  }
@@ -6313,7 +6683,7 @@ var GitClient = class {
6313
6683
  };
6314
6684
  var NodeGitSpawner = class {
6315
6685
  async spawn(args, opts) {
6316
- return new Promise((resolve11, reject) => {
6686
+ return new Promise((resolve13, reject) => {
6317
6687
  const child = spawn3("git", args, {
6318
6688
  cwd: opts.cwd,
6319
6689
  stdio: ["ignore", "pipe", "pipe"],
@@ -6325,7 +6695,7 @@ var NodeGitSpawner = class {
6325
6695
  child.stderr?.on("data", (c) => stderrChunks.push(c));
6326
6696
  child.on("error", reject);
6327
6697
  child.on("close", (code) => {
6328
- resolve11({
6698
+ resolve13({
6329
6699
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
6330
6700
  stderr: Buffer.concat(stderrChunks).toString("utf8"),
6331
6701
  code: code ?? 1
@@ -6354,7 +6724,7 @@ var StubGitSpawner = class {
6354
6724
  }
6355
6725
  };
6356
6726
  function toFileUrl(absolutePath) {
6357
- const normalized = path24.resolve(absolutePath);
6727
+ const normalized = path25.resolve(absolutePath);
6358
6728
  if (process.platform === "win32") {
6359
6729
  return `file:///${normalized.replace(/\\/g, "/")}`;
6360
6730
  }
@@ -6367,7 +6737,7 @@ function buildGitProxyBase(serverBaseUrl) {
6367
6737
 
6368
6738
  // src/image/imageTools.ts
6369
6739
  import * as fs7 from "fs/promises";
6370
- import * as path25 from "path";
6740
+ import * as path26 from "path";
6371
6741
  import {
6372
6742
  createServicemeError as createServicemeError5
6373
6743
  } from "@serviceme/devtools-protocol";
@@ -6382,7 +6752,7 @@ var ImageTools = class {
6382
6752
  if (!await this.pathExists(filePath)) {
6383
6753
  return { valid: false };
6384
6754
  }
6385
- if (!SUPPORTED_FORMATS.includes(path25.extname(filePath).toLowerCase())) {
6755
+ if (!SUPPORTED_FORMATS.includes(path26.extname(filePath).toLowerCase())) {
6386
6756
  return { valid: false };
6387
6757
  }
6388
6758
  await this.getInfo(filePath, sharpModulePath);
@@ -6434,7 +6804,7 @@ var ImageTools = class {
6434
6804
  async compressWithSharp(imagePath, options) {
6435
6805
  const sharp = this.loadSharp(options.sharpModulePath);
6436
6806
  let pipeline = sharp(imagePath);
6437
- switch (options.format ?? path25.extname(imagePath).toLowerCase().slice(1)) {
6807
+ switch (options.format ?? path26.extname(imagePath).toLowerCase().slice(1)) {
6438
6808
  case "jpg":
6439
6809
  case "jpeg":
6440
6810
  pipeline = pipeline.jpeg({ quality: options.quality });
@@ -6460,10 +6830,18 @@ var ImageTools = class {
6460
6830
  if (options.replaceOriginImage) {
6461
6831
  return inputPath;
6462
6832
  }
6463
- const dir = path25.dirname(inputPath);
6464
- const ext = path25.extname(inputPath);
6465
- const name = path25.basename(inputPath, ext);
6466
- return path25.join(dir, `${name}_compressed${ext}`);
6833
+ const dir = path26.dirname(inputPath);
6834
+ const ext = path26.extname(inputPath);
6835
+ const name = path26.basename(inputPath, ext);
6836
+ const outputPath = path26.join(dir, `${name}_compressed${ext}`);
6837
+ const resolvedDir = path26.resolve(dir);
6838
+ if (!path26.resolve(outputPath).startsWith(resolvedDir + path26.sep)) {
6839
+ throw createServicemeError5(
6840
+ "invalid_params",
6841
+ "Derived output path escapes the image directory."
6842
+ );
6843
+ }
6844
+ return outputPath;
6467
6845
  }
6468
6846
  async pathExists(targetPath) {
6469
6847
  try {
@@ -6577,7 +6955,7 @@ function detectIndent(text) {
6577
6955
  import { randomUUID as randomUUID2 } from "crypto";
6578
6956
  import * as fs8 from "fs/promises";
6579
6957
  import { open as open2 } from "fs/promises";
6580
- import * as path26 from "path";
6958
+ import * as path27 from "path";
6581
6959
  async function readServerProxyGlobal() {
6582
6960
  const filePath = getServerProxyGlobalPath();
6583
6961
  try {
@@ -6596,7 +6974,7 @@ async function readServerProxyGlobal() {
6596
6974
  }
6597
6975
  async function writeServerProxyGlobal(patch) {
6598
6976
  const filePath = getServerProxyGlobalPath();
6599
- const dirPath = path26.dirname(filePath);
6977
+ const dirPath = path27.dirname(filePath);
6600
6978
  await fs8.mkdir(dirPath, { recursive: true });
6601
6979
  const current = await readServerProxyGlobal() ?? {
6602
6980
  enabled: false,
@@ -6651,7 +7029,7 @@ function isENOENT(err) {
6651
7029
  // src/phase5/bootstrap.ts
6652
7030
  import { randomUUID as randomUUID3 } from "crypto";
6653
7031
  import * as fs9 from "fs/promises";
6654
- import * as path27 from "path";
7032
+ import * as path28 from "path";
6655
7033
  function getPhase5FileSpecs() {
6656
7034
  return [
6657
7035
  {
@@ -6707,7 +7085,7 @@ async function bootstrapPhase5Placeholders() {
6707
7085
  result.skipped.push(spec.path);
6708
7086
  } catch {
6709
7087
  try {
6710
- await fs9.mkdir(path27.dirname(spec.path), { recursive: true });
7088
+ await fs9.mkdir(path28.dirname(spec.path), { recursive: true });
6711
7089
  await fs9.writeFile(spec.path, spec.defaultContent, "utf8");
6712
7090
  result.created.push(spec.path);
6713
7091
  } catch (writeErr) {
@@ -6720,29 +7098,29 @@ async function bootstrapPhase5Placeholders() {
6720
7098
 
6721
7099
  // src/project/projectTools.ts
6722
7100
  import * as fs10 from "fs/promises";
6723
- import * as path28 from "path";
7101
+ import * as path29 from "path";
6724
7102
  import {
6725
7103
  createServicemeError as createServicemeError7
6726
7104
  } from "@serviceme/devtools-protocol";
6727
7105
 
6728
7106
  // src/utils/fileUtils.ts
6729
7107
  import { constants, createWriteStream } from "fs";
6730
- import { access as access7, copyFile as copyFile3, lstat as lstat6, mkdir as mkdir16, readdir as readdir8, rename as rename12, rm as rm10 } from "fs/promises";
6731
- import { dirname as dirname19, join as join26 } from "path";
7108
+ import { access as access7, copyFile as copyFile3, lstat as lstat7, mkdir as mkdir16, readdir as readdir9, rename as rename12, rm as rm10 } from "fs/promises";
7109
+ import { dirname as dirname19, join as join27 } from "path";
6732
7110
  import yauzl from "yauzl";
6733
7111
  var unzipFile = (zipPath, dest) => {
6734
- return new Promise((resolve11, reject) => {
7112
+ return new Promise((resolve13, reject) => {
6735
7113
  yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
6736
7114
  if (err) return reject(err);
6737
7115
  if (!zipfile) return reject(new Error("Failed to open zip file."));
6738
7116
  zipfile.readEntry();
6739
7117
  zipfile.on("entry", (entry) => {
6740
7118
  if (/\/$/.test(entry.fileName)) {
6741
- void mkdir16(join26(dest, entry.fileName), { recursive: true }).then(() => {
7119
+ void mkdir16(join27(dest, entry.fileName), { recursive: true }).then(() => {
6742
7120
  zipfile.readEntry();
6743
7121
  }).catch(reject);
6744
7122
  } else {
6745
- const outputPath = join26(dest, entry.fileName);
7123
+ const outputPath = join27(dest, entry.fileName);
6746
7124
  void mkdir16(dirname19(outputPath), { recursive: true }).then(() => {
6747
7125
  zipfile.openReadStream(
6748
7126
  entry,
@@ -6762,7 +7140,7 @@ var unzipFile = (zipPath, dest) => {
6762
7140
  }
6763
7141
  });
6764
7142
  zipfile.on("end", () => {
6765
- resolve11();
7143
+ resolve13();
6766
7144
  });
6767
7145
  zipfile.on("error", (zipError) => {
6768
7146
  reject(zipError);
@@ -6772,13 +7150,13 @@ var unzipFile = (zipPath, dest) => {
6772
7150
  };
6773
7151
  var tryLstat = async (targetPath) => {
6774
7152
  try {
6775
- return await lstat6(targetPath);
7153
+ return await lstat7(targetPath);
6776
7154
  } catch {
6777
7155
  return null;
6778
7156
  }
6779
7157
  };
6780
7158
  var mergeEntry = async (sourcePath, destPath, overwrite) => {
6781
- const sourceStat = await lstat6(sourcePath);
7159
+ const sourceStat = await lstat7(sourcePath);
6782
7160
  const destStat = await tryLstat(destPath);
6783
7161
  if (sourceStat.isDirectory()) {
6784
7162
  if (destStat && !destStat.isDirectory()) {
@@ -6789,9 +7167,9 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
6789
7167
  await rm10(destPath, { recursive: true, force: true });
6790
7168
  }
6791
7169
  await mkdir16(destPath, { recursive: true });
6792
- const children = await readdir8(sourcePath);
7170
+ const children = await readdir9(sourcePath);
6793
7171
  for (const child of children) {
6794
- await mergeEntry(join26(sourcePath, child), join26(destPath, child), overwrite);
7172
+ await mergeEntry(join27(sourcePath, child), join27(destPath, child), overwrite);
6795
7173
  }
6796
7174
  await rm10(sourcePath, { recursive: true, force: true });
6797
7175
  return;
@@ -6812,10 +7190,10 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
6812
7190
  };
6813
7191
  var moveFiles = async (sourceDir, destDir, overwrite = false) => {
6814
7192
  await mkdir16(destDir, { recursive: true });
6815
- const files = await readdir8(sourceDir);
7193
+ const files = await readdir9(sourceDir);
6816
7194
  for (const file of files) {
6817
- const sourceFile = join26(sourceDir, file);
6818
- const destFile = join26(destDir, file);
7195
+ const sourceFile = join27(sourceDir, file);
7196
+ const destFile = join27(destDir, file);
6819
7197
  if (!overwrite) {
6820
7198
  try {
6821
7199
  await access7(destFile, constants.F_OK);
@@ -6832,7 +7210,7 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
6832
7210
  var ProjectTools = class {
6833
7211
  async extractTemplate(zipPath, workspacePath, tempExtractDir, input) {
6834
7212
  await unzipFile(zipPath, tempExtractDir);
6835
- let sourceDir = path28.join(tempExtractDir, input.extractedDirName);
7213
+ let sourceDir = path29.join(tempExtractDir, input.extractedDirName);
6836
7214
  let actualDirName = input.extractedDirName;
6837
7215
  if (!await this.pathExists(sourceDir)) {
6838
7216
  const entries = await fs10.readdir(tempExtractDir, { withFileTypes: true });
@@ -6847,7 +7225,7 @@ var ProjectTools = class {
6847
7225
  );
6848
7226
  if (selectedDirectory) {
6849
7227
  actualDirName = selectedDirectory;
6850
- sourceDir = path28.join(tempExtractDir, actualDirName);
7228
+ sourceDir = path29.join(tempExtractDir, actualDirName);
6851
7229
  } else if (directories.length === 0) {
6852
7230
  throw new Error(
6853
7231
  `No directory found after extraction. Expected directory: ${input.extractedDirName}`
@@ -6870,6 +7248,13 @@ var ProjectTools = class {
6870
7248
  if (!command) {
6871
7249
  throw createServicemeError7("invalid_params", "Expected install command.");
6872
7250
  }
7251
+ const hasControlCharacter = [...command].some((character) => {
7252
+ const code = character.charCodeAt(0);
7253
+ return code > 0 && code < 32 && character !== " ";
7254
+ });
7255
+ if (hasControlCharacter) {
7256
+ throw createServicemeError7("invalid_params", "Install command contains control characters.");
7257
+ }
6873
7258
  await runCommand(command, {
6874
7259
  cwd: workspacePath,
6875
7260
  shell: true
@@ -6945,7 +7330,7 @@ var ProjectTools = class {
6945
7330
  };
6946
7331
  }
6947
7332
  async ensurePresetManifest(workspacePath, preset) {
6948
- const presetManifestPath = path28.join(
7333
+ const presetManifestPath = path29.join(
6949
7334
  workspacePath,
6950
7335
  ".ms-scaffold",
6951
7336
  "presets",
@@ -6954,7 +7339,7 @@ var ProjectTools = class {
6954
7339
  if (await this.pathExists(presetManifestPath)) {
6955
7340
  return;
6956
7341
  }
6957
- const projectModePath = path28.join(workspacePath, ".ms-scaffold", "project-mode.json");
7342
+ const projectModePath = path29.join(workspacePath, ".ms-scaffold", "project-mode.json");
6958
7343
  if (!await this.pathExists(projectModePath)) {
6959
7344
  return;
6960
7345
  }
@@ -6970,7 +7355,7 @@ var ProjectTools = class {
6970
7355
  mergeManagedFiles: [],
6971
7356
  userOwnedPaths: []
6972
7357
  };
6973
- await fs10.mkdir(path28.dirname(presetManifestPath), { recursive: true });
7358
+ await fs10.mkdir(path29.dirname(presetManifestPath), { recursive: true });
6974
7359
  await fs10.writeFile(
6975
7360
  presetManifestPath,
6976
7361
  `${JSON.stringify(synthesizedPreset, null, 2)}
@@ -6987,7 +7372,7 @@ var ProjectTools = class {
6987
7372
  return results;
6988
7373
  }
6989
7374
  for (const entry of entries) {
6990
- const fullPath = path28.join(dir, entry.name);
7375
+ const fullPath = path29.join(dir, entry.name);
6991
7376
  if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
6992
7377
  results.push(...await this.findScripts(fullPath, extensions));
6993
7378
  } else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
@@ -7015,7 +7400,7 @@ var ProjectTools = class {
7015
7400
  const matches = [];
7016
7401
  for (const directoryName of directoryNames) {
7017
7402
  if (await this.directoryMatchesProjectPattern(
7018
- path28.join(tempExtractDir, directoryName),
7403
+ path29.join(tempExtractDir, directoryName),
7019
7404
  projectFilePattern
7020
7405
  )) {
7021
7406
  matches.push(directoryName);
@@ -7041,7 +7426,7 @@ function createProjectTools() {
7041
7426
 
7042
7427
  // src/repo-manager/index.ts
7043
7428
  import * as fs11 from "fs/promises";
7044
- import * as path29 from "path";
7429
+ import * as path30 from "path";
7045
7430
 
7046
7431
  // src/repos/types.ts
7047
7432
  function isDefaultRepo(repo) {
@@ -7133,7 +7518,7 @@ var RepoManager = class {
7133
7518
  }
7134
7519
  try {
7135
7520
  if (!this.skipClone) {
7136
- await fs11.mkdir(path29.dirname(localPath), { recursive: true });
7521
+ await fs11.mkdir(path30.dirname(localPath), { recursive: true });
7137
7522
  await this.git.clone(repo.id, repo.url, localPath, repo.branch, true);
7138
7523
  }
7139
7524
  await this.store.updateRepo(repo.id, {
@@ -7175,7 +7560,7 @@ var RepoManager = class {
7175
7560
  await fs11.rm(localPath, { recursive: true, force: true });
7176
7561
  }
7177
7562
  if (!exists || !await this.pathExists(localPath)) {
7178
- await fs11.mkdir(path29.dirname(localPath), { recursive: true });
7563
+ await fs11.mkdir(path30.dirname(localPath), { recursive: true });
7179
7564
  if (!this.skipClone) {
7180
7565
  await this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);
7181
7566
  }
@@ -7242,7 +7627,7 @@ var RepoManager = class {
7242
7627
  await fs11.rm(localPath, { recursive: true, force: true });
7243
7628
  }
7244
7629
  if (!exists || !await this.pathExists(localPath)) {
7245
- await fs11.mkdir(path29.dirname(localPath), { recursive: true });
7630
+ await fs11.mkdir(path30.dirname(localPath), { recursive: true });
7246
7631
  if (!this.skipClone) {
7247
7632
  await this.git.clone(
7248
7633
  input.repository.useProxy ?? true ? input.repository.id : input.repository.id,
@@ -7389,7 +7774,7 @@ var RepoManager = class {
7389
7774
  let cloned = false;
7390
7775
  if (!this.skipClone) {
7391
7776
  const localPath = getRepoDir(uniqueId);
7392
- await fs11.mkdir(path29.dirname(localPath), { recursive: true });
7777
+ await fs11.mkdir(path30.dirname(localPath), { recursive: true });
7393
7778
  await this.git.clone(userProxyId, url, localPath, branch, useProxy);
7394
7779
  cloned = true;
7395
7780
  }
@@ -7445,7 +7830,7 @@ var RepoManager = class {
7445
7830
  * (e.g. from an interrupted clone) return `false`.
7446
7831
  */
7447
7832
  async isValidGitRepo(p) {
7448
- return this.pathExists(path29.join(p, ".git"));
7833
+ return this.pathExists(path30.join(p, ".git"));
7449
7834
  }
7450
7835
  async pathExists(p) {
7451
7836
  try {
@@ -7612,7 +7997,7 @@ function resolveDefaultRepoId(existing, existingIds) {
7612
7997
 
7613
7998
  // src/repos/loader.ts
7614
7999
  import * as fs12 from "fs/promises";
7615
- import * as path30 from "path";
8000
+ import * as path31 from "path";
7616
8001
  import { z as z2 } from "zod";
7617
8002
  var ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})$/;
7618
8003
  var repoIdSchema = z2.string().min(1).max(64).regex(SAFE_REPO_ID_PATTERN, "repo id must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/");
@@ -7748,7 +8133,7 @@ var ReposLoader = class {
7748
8133
  */
7749
8134
  async save(config) {
7750
8135
  const validated = reposFileSchema.parse(config);
7751
- const dir = path30.dirname(this.configPath);
8136
+ const dir = path31.dirname(this.configPath);
7752
8137
  await this.fileSystem.mkdir(dir, { recursive: true });
7753
8138
  const serialized = `${JSON.stringify(validated, null, 2)}
7754
8139
  `;
@@ -8109,14 +8494,14 @@ async function bootstrapDefaults(store) {
8109
8494
 
8110
8495
  // src/scheduled-tasks/daemon/DaemonLogger.ts
8111
8496
  import * as fs14 from "fs";
8112
- import * as path31 from "path";
8497
+ import * as path32 from "path";
8113
8498
  var CONFIG_DIR = ".serviceme";
8114
8499
  var LOG_FILE = "scheduler.log";
8115
8500
  var MAX_LOG_SIZE = 1024 * 1024;
8116
8501
  var DaemonLogger = class {
8117
8502
  constructor(workspacePath, options = {}) {
8118
- this.logPath = options.logPath ?? path31.join(workspacePath, CONFIG_DIR, LOG_FILE);
8119
- const dir = path31.dirname(this.logPath);
8503
+ this.logPath = options.logPath ?? path32.join(workspacePath, CONFIG_DIR, LOG_FILE);
8504
+ const dir = path32.dirname(this.logPath);
8120
8505
  if (!fs14.existsSync(dir)) {
8121
8506
  fs14.mkdirSync(dir, { recursive: true });
8122
8507
  }
@@ -8148,31 +8533,31 @@ var DaemonLogger = class {
8148
8533
 
8149
8534
  // src/scheduled-tasks/daemon/PidManager.ts
8150
8535
  import * as fs15 from "fs";
8151
- import * as path32 from "path";
8536
+ import * as path33 from "path";
8152
8537
  var CONFIG_DIR2 = ".serviceme";
8153
8538
  var PID_FILE = "scheduler.pid";
8154
8539
  var PidManager = class {
8155
8540
  constructor(workspacePath, options = {}) {
8156
- this.pidPath = options.pidPath ?? path32.join(workspacePath, CONFIG_DIR2, PID_FILE);
8541
+ this.pidPath = options.pidPath ?? path33.join(workspacePath, CONFIG_DIR2, PID_FILE);
8157
8542
  }
8158
8543
  getPidPath() {
8159
8544
  return this.pidPath;
8160
8545
  }
8161
8546
  writePid(pid) {
8162
- const dir = path32.dirname(this.pidPath);
8547
+ const dir = path33.dirname(this.pidPath);
8163
8548
  if (!fs15.existsSync(dir)) {
8164
8549
  fs15.mkdirSync(dir, { recursive: true });
8165
8550
  }
8166
8551
  fs15.writeFileSync(this.pidPath, String(pid), "utf-8");
8167
8552
  }
8168
8553
  readPid() {
8169
- let stat15;
8554
+ let stat16;
8170
8555
  try {
8171
- stat15 = fs15.statSync(this.pidPath);
8556
+ stat16 = fs15.statSync(this.pidPath);
8172
8557
  } catch {
8173
8558
  return null;
8174
8559
  }
8175
- if (!stat15.isFile()) return null;
8560
+ if (!stat16.isFile()) return null;
8176
8561
  const raw = fs15.readFileSync(this.pidPath, "utf-8").trim();
8177
8562
  const pid = Number.parseInt(raw, 10);
8178
8563
  return Number.isNaN(pid) ? null : pid;
@@ -8202,7 +8587,7 @@ var PidManager = class {
8202
8587
  // src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
8203
8588
  import * as fs20 from "fs";
8204
8589
  import * as os6 from "os";
8205
- import * as path36 from "path";
8590
+ import * as path37 from "path";
8206
8591
 
8207
8592
  // src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
8208
8593
  import { spawn as spawn4 } from "child_process";
@@ -8293,16 +8678,16 @@ var GithubCopilotCliExecutor = class {
8293
8678
  executeStreaming(payload, onOutput, abortSignal) {
8294
8679
  const p = payload;
8295
8680
  const execution = resolveGithubCopilotCliExecution(p);
8296
- let resolve11;
8681
+ let resolve13;
8297
8682
  const resultPromise = new Promise((r) => {
8298
- resolve11 = r;
8683
+ resolve13 = r;
8299
8684
  });
8300
8685
  let settled = false;
8301
8686
  const settle = (result) => {
8302
8687
  if (settled) return;
8303
8688
  settled = true;
8304
8689
  if (timer) clearTimeout(timer);
8305
- resolve11?.(result);
8690
+ resolve13?.(result);
8306
8691
  };
8307
8692
  if (abortSignal?.aborted) {
8308
8693
  return {
@@ -8448,7 +8833,7 @@ ${body}`.trim()
8448
8833
  // src/scheduled-tasks/executors/ShellExecutor.ts
8449
8834
  import { spawn as spawn5 } from "child_process";
8450
8835
  import * as fs17 from "fs";
8451
- import * as path33 from "path";
8836
+ import * as path34 from "path";
8452
8837
  var MAX_OUTPUT_BYTES2 = 1024 * 1024;
8453
8838
  var DEFAULT_TIMEOUT_MS4 = 6e4;
8454
8839
  var POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
@@ -8500,10 +8885,10 @@ function findWindowsPosixShell(env, fileExists) {
8500
8885
  if (fileExists(candidate)) return candidate;
8501
8886
  }
8502
8887
  const pathValue = env.Path ?? env.PATH ?? "";
8503
- for (const dir of pathValue.split(path33.win32.delimiter)) {
8888
+ for (const dir of pathValue.split(path34.win32.delimiter)) {
8504
8889
  if (!dir) continue;
8505
8890
  for (const executable of POSIX_SHELL_CANDIDATES) {
8506
- const candidate = path33.win32.join(dir, executable);
8891
+ const candidate = path34.win32.join(dir, executable);
8507
8892
  if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
8508
8893
  return candidate;
8509
8894
  }
@@ -8512,7 +8897,7 @@ function findWindowsPosixShell(env, fileExists) {
8512
8897
  return null;
8513
8898
  }
8514
8899
  function isWindowsWslLauncher(candidate) {
8515
- const normalized = path33.win32.normalize(candidate).toLowerCase();
8900
+ const normalized = path34.win32.normalize(candidate).toLowerCase();
8516
8901
  return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
8517
8902
  }
8518
8903
  function writeDiagnostic2(message) {
@@ -8539,16 +8924,16 @@ var ShellExecutor = class {
8539
8924
  executeStreaming(payload, onOutput, abortSignal) {
8540
8925
  const p = payload;
8541
8926
  const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS4);
8542
- let resolve11;
8927
+ let resolve13;
8543
8928
  const resultPromise = new Promise((r) => {
8544
- resolve11 = r;
8929
+ resolve13 = r;
8545
8930
  });
8546
8931
  let settled = false;
8547
8932
  const settle = (result) => {
8548
8933
  if (settled) return;
8549
8934
  settled = true;
8550
8935
  if (timer) clearTimeout(timer);
8551
- resolve11?.(result);
8936
+ resolve13?.(result);
8552
8937
  };
8553
8938
  if (abortSignal?.aborted) {
8554
8939
  return {
@@ -8663,7 +9048,7 @@ function getExecutor(taskType) {
8663
9048
  import { randomUUID as randomUUID4 } from "crypto";
8664
9049
  import * as fs18 from "fs";
8665
9050
  import * as os5 from "os";
8666
- import * as path34 from "path";
9051
+ import * as path35 from "path";
8667
9052
  import {
8668
9053
  createServicemeError as createServicemeError8,
8669
9054
  isScheduledTasksConfig,
@@ -8693,7 +9078,7 @@ function v1ContainerShape(value) {
8693
9078
  }
8694
9079
  function defaultWorkspaceContext() {
8695
9080
  const home = os5.homedir() || "/";
8696
- return { path: home, name: path34.basename(home) || home };
9081
+ return { path: home, name: path35.basename(home) || home };
8697
9082
  }
8698
9083
  function requireNonEmptyString(payload, field, taskType) {
8699
9084
  if (!isRecord2(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
@@ -8797,7 +9182,7 @@ var TaskConfigManager = class {
8797
9182
  snippet: raw.slice(0, 500),
8798
9183
  recordedAt: (/* @__PURE__ */ new Date()).toISOString()
8799
9184
  });
8800
- fs18.mkdirSync(path34.dirname(target), { recursive: true });
9185
+ fs18.mkdirSync(path35.dirname(target), { recursive: true });
8801
9186
  fs18.writeFileSync(target, JSON.stringify(failures, null, " "), "utf-8");
8802
9187
  } catch (writeError) {
8803
9188
  this.warn(
@@ -8806,7 +9191,7 @@ var TaskConfigManager = class {
8806
9191
  }
8807
9192
  }
8808
9193
  writeConfig(config) {
8809
- const dir = path34.dirname(this.configPath);
9194
+ const dir = path35.dirname(this.configPath);
8810
9195
  if (!fs18.existsSync(dir)) {
8811
9196
  fs18.mkdirSync(dir, { recursive: true });
8812
9197
  }
@@ -9091,7 +9476,7 @@ var TaskExecutionEngine = class {
9091
9476
  // src/scheduled-tasks/TaskLogManager.ts
9092
9477
  import { randomUUID as randomUUID5 } from "crypto";
9093
9478
  import * as fs19 from "fs";
9094
- import * as path35 from "path";
9479
+ import * as path36 from "path";
9095
9480
  var MAX_LOGS = 200;
9096
9481
  function emptyLogFile() {
9097
9482
  return { logs: [] };
@@ -9148,7 +9533,7 @@ var TaskLogManager = class {
9148
9533
  }
9149
9534
  }
9150
9535
  writeLogFile(file) {
9151
- const dir = path35.dirname(this.logPath);
9536
+ const dir = path36.dirname(this.logPath);
9152
9537
  if (!fs19.existsSync(dir)) {
9153
9538
  fs19.mkdirSync(dir, { recursive: true });
9154
9539
  }
@@ -9221,7 +9606,7 @@ var SchedulerDaemonV2 = class {
9221
9606
  this.logManager = options.logManager ?? new TaskLogManager();
9222
9607
  this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
9223
9608
  this.logger = options.logger ?? new DaemonLogger(os6.homedir(), {
9224
- logPath: path36.join(path36.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
9609
+ logPath: path37.join(path37.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
9225
9610
  });
9226
9611
  this.getExecutor = options.getExecutor ?? getExecutor;
9227
9612
  this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
@@ -9437,14 +9822,14 @@ function matchCronField(field, value) {
9437
9822
 
9438
9823
  // src/scheduled-tasks/migration/MigrateToGlobal.ts
9439
9824
  import * as fs21 from "fs";
9440
- import * as path37 from "path";
9825
+ import * as path38 from "path";
9441
9826
  import { isScheduledTasksConfigV1 as isScheduledTasksConfigV12, migrateV1ToV2 as migrateV1ToV22 } from "@serviceme/devtools-protocol";
9442
9827
  var WORKSPACE_DIR = ".serviceme";
9443
9828
  var V1_FILENAME = "scheduled-tasks.json";
9444
9829
  function defaultProbe(workspacePath) {
9445
9830
  return {
9446
9831
  path: workspacePath,
9447
- name: path37.basename(workspacePath) || workspacePath
9832
+ name: path38.basename(workspacePath) || workspacePath
9448
9833
  };
9449
9834
  }
9450
9835
  function readV1Config(v1Path) {
@@ -9478,7 +9863,7 @@ function safeDelete(filePath) {
9478
9863
  }
9479
9864
  }
9480
9865
  function ensureDir(filePath) {
9481
- const dir = path37.dirname(filePath);
9866
+ const dir = path38.dirname(filePath);
9482
9867
  if (!fs21.existsSync(dir)) {
9483
9868
  fs21.mkdirSync(dir, { recursive: true });
9484
9869
  }
@@ -9523,7 +9908,7 @@ async function migrateToGlobal(options) {
9523
9908
  const conflicts = [];
9524
9909
  const issues = [];
9525
9910
  for (const workspacePath of options.workspacePaths) {
9526
- const v1Path = path37.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
9911
+ const v1Path = path38.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
9527
9912
  if (!fs21.existsSync(v1Path)) continue;
9528
9913
  const v1 = readV1Config(v1Path);
9529
9914
  if (!v1.ok) {
@@ -9585,7 +9970,7 @@ async function migrateToGlobal(options) {
9585
9970
  // src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
9586
9971
  import { spawn as spawn6 } from "child_process";
9587
9972
  import * as fs22 from "fs";
9588
- import * as path38 from "path";
9973
+ import * as path39 from "path";
9589
9974
  var DEFAULT_TIMEOUT_MS5 = 2e3;
9590
9975
  var GitTimeoutError = class extends Error {
9591
9976
  constructor() {
@@ -9601,7 +9986,7 @@ function isTimeout(err) {
9601
9986
  return false;
9602
9987
  }
9603
9988
  function defaultRunGit(gitBinary, args, cwd, timeoutMs) {
9604
- return new Promise((resolve11, reject) => {
9989
+ return new Promise((resolve13, reject) => {
9605
9990
  let settled = false;
9606
9991
  const child = spawn6(gitBinary, args, {
9607
9992
  cwd,
@@ -9631,7 +10016,7 @@ function defaultRunGit(gitBinary, args, cwd, timeoutMs) {
9631
10016
  if (settled) return;
9632
10017
  settled = true;
9633
10018
  clearTimeout(timer);
9634
- resolve11({ stdout, stderr, code: code ?? 0 });
10019
+ resolve13({ stdout, stderr, code: code ?? 0 });
9635
10020
  });
9636
10021
  });
9637
10022
  }
@@ -9648,7 +10033,7 @@ var WorkspaceProbe = class {
9648
10033
  }
9649
10034
  }
9650
10035
  async probe(workspacePath) {
9651
- const name = path38.basename(workspacePath) || workspacePath;
10036
+ const name = path39.basename(workspacePath) || workspacePath;
9652
10037
  if (!workspacePath || !fs22.existsSync(workspacePath)) {
9653
10038
  return {
9654
10039
  workspace: { path: workspacePath, name },
@@ -9802,7 +10187,7 @@ var SkillReconciler = class {
9802
10187
 
9803
10188
  // src/skills/SkillStore.ts
9804
10189
  import * as fs23 from "fs/promises";
9805
- import * as path39 from "path";
10190
+ import * as path40 from "path";
9806
10191
  var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
9807
10192
  var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
9808
10193
  var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
@@ -9838,10 +10223,10 @@ var SkillStore = class {
9838
10223
  return WORKSPACE_SKILLS_MARKER_RELATIVE;
9839
10224
  }
9840
10225
  getUserSkillPath(skillId) {
9841
- return path39.join(this.userSkillsRoot, skillId);
10226
+ return path40.join(this.userSkillsRoot, skillId);
9842
10227
  }
9843
10228
  async listWorkspaceSkillIds() {
9844
- const skillsRootPath = path39.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
10229
+ const skillsRootPath = path40.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
9845
10230
  try {
9846
10231
  const entries = await this.fileSystem.readdir(skillsRootPath, {
9847
10232
  withFileTypes: true
@@ -9865,7 +10250,7 @@ var SkillStore = class {
9865
10250
  const targetDir = this.getUserSkillPath(skillId);
9866
10251
  await this.fileSystem.mkdir(targetDir, { recursive: true });
9867
10252
  await this.fileSystem.writeFile(
9868
- path39.join(targetDir, USER_SKILL_MARKER_FILE),
10253
+ path40.join(targetDir, USER_SKILL_MARKER_FILE),
9869
10254
  JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
9870
10255
  "utf-8"
9871
10256
  );
@@ -9874,7 +10259,7 @@ var SkillStore = class {
9874
10259
  await this.migrateLegacyUserSkillMarker(skillId);
9875
10260
  try {
9876
10261
  const marker = await this.fileSystem.readFile(
9877
- path39.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
10262
+ path40.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
9878
10263
  "utf-8"
9879
10264
  );
9880
10265
  const parsed = JSON.parse(marker);
@@ -9891,8 +10276,8 @@ var SkillStore = class {
9891
10276
  */
9892
10277
  async migrateLegacyUserSkillMarker(skillId) {
9893
10278
  const targetDir = this.getUserSkillPath(skillId);
9894
- const newPath = path39.join(targetDir, USER_SKILL_MARKER_FILE);
9895
- const legacyPath = path39.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
10279
+ const newPath = path40.join(targetDir, USER_SKILL_MARKER_FILE);
10280
+ const legacyPath = path40.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
9896
10281
  try {
9897
10282
  await this.fileSystem.readFile(newPath, "utf-8");
9898
10283
  return;
@@ -9905,12 +10290,12 @@ var SkillStore = class {
9905
10290
  }
9906
10291
  }
9907
10292
  async writeSkillFiles(skillId, scope, files) {
9908
- const root = scope === "workspace" ? path39.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
9909
- const targetDir = path39.join(root, skillId);
10293
+ const root = scope === "workspace" ? path40.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
10294
+ const targetDir = path40.join(root, skillId);
9910
10295
  await this.fileSystem.mkdir(targetDir, { recursive: true });
9911
10296
  for (const file of files) {
9912
- const filePath = path39.join(targetDir, file.path);
9913
- await this.fileSystem.mkdir(path39.dirname(filePath), { recursive: true });
10297
+ const filePath = path40.join(targetDir, file.path);
10298
+ await this.fileSystem.mkdir(path40.dirname(filePath), { recursive: true });
9914
10299
  await this.fileSystem.writeFile(filePath, file.content, "utf-8");
9915
10300
  if (file.executable) {
9916
10301
  try {
@@ -9923,8 +10308,8 @@ var SkillStore = class {
9923
10308
  };
9924
10309
  async function migrateLegacyUserSkillContent(input) {
9925
10310
  const fileSystem = input.fileSystem ?? fs23;
9926
- const legacyRoot = path39.join(input.homeDir, ...LEGACY_USER_SKILLS_ROOT_RELATIVE.split("/"));
9927
- const targetRoot = path39.join(input.homeDir, ".copilot", "skills");
10311
+ const legacyRoot = path40.join(input.homeDir, ...LEGACY_USER_SKILLS_ROOT_RELATIVE.split("/"));
10312
+ const targetRoot = path40.join(input.homeDir, ".copilot", "skills");
9928
10313
  let entries;
9929
10314
  try {
9930
10315
  entries = await fileSystem.readdir(legacyRoot, { withFileTypes: true });
@@ -9936,8 +10321,8 @@ async function migrateLegacyUserSkillContent(input) {
9936
10321
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
9937
10322
  result.push({
9938
10323
  id: entry.name,
9939
- legacyPath: path39.join(legacyRoot, entry.name),
9940
- targetPath: path39.join(targetRoot, entry.name),
10324
+ legacyPath: path40.join(legacyRoot, entry.name),
10325
+ targetPath: path40.join(targetRoot, entry.name),
9941
10326
  status: "migration_available"
9942
10327
  });
9943
10328
  }
@@ -9946,7 +10331,7 @@ async function migrateLegacyUserSkillContent(input) {
9946
10331
 
9947
10332
  // src/submit/index.ts
9948
10333
  import * as fs24 from "fs/promises";
9949
- import * as path40 from "path";
10334
+ import * as path41 from "path";
9950
10335
 
9951
10336
  // src/submit/types.ts
9952
10337
  var SubmitError = class extends Error {
@@ -9996,11 +10381,11 @@ var SubmitClient = class {
9996
10381
  throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
9997
10382
  }
9998
10383
  const localRepoPath = getRepoDir(repoId);
9999
- const targetDir = path40.join(localRepoPath, "skills", skillName);
10384
+ const targetDir = path41.join(localRepoPath, "skills", skillName);
10000
10385
  await fs24.mkdir(targetDir, { recursive: true });
10001
10386
  for (const f of files) {
10002
- const full = path40.join(targetDir, f.path);
10003
- await fs24.mkdir(path40.dirname(full), { recursive: true });
10387
+ const full = path41.join(targetDir, f.path);
10388
+ await fs24.mkdir(path41.dirname(full), { recursive: true });
10004
10389
  const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
10005
10390
  await fs24.writeFile(tmp, f.content, "utf8");
10006
10391
  await fs24.rename(tmp, full);
@@ -10071,8 +10456,8 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
10071
10456
  }
10072
10457
 
10073
10458
  // src/toolbox/ToolboxStore.ts
10074
- import * as fsp17 from "fs/promises";
10075
- import * as path41 from "path";
10459
+ import * as fsp18 from "fs/promises";
10460
+ import * as path42 from "path";
10076
10461
  import { setTimeout as delay2 } from "timers/promises";
10077
10462
 
10078
10463
  // src/toolbox/types.ts
@@ -10108,19 +10493,19 @@ var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
10108
10493
  var DEFAULT_LOCK_RETRY_MS2 = 25;
10109
10494
  var LOCK_STALE_GRACE_MS2 = 200;
10110
10495
  var TMP_SUFFIX2 = ".tmp";
10111
- var WORKSPACE_TOOLBOX_RELATIVE_PATH = path41.join(".github", ".serviceme-toolbox.json");
10496
+ var WORKSPACE_TOOLBOX_RELATIVE_PATH = path42.join(".github", ".serviceme-toolbox.json");
10112
10497
  var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
10113
10498
  async function migrateLegacyWorkspaceToolboxFile(filePath) {
10114
10499
  if (!filePath) return;
10115
- const legacyPath = path41.join(path41.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
10500
+ const legacyPath = path42.join(path42.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
10116
10501
  if (legacyPath === filePath) return;
10117
10502
  try {
10118
- await fsp17.access(filePath);
10503
+ await fsp18.access(filePath);
10119
10504
  return;
10120
10505
  } catch {
10121
10506
  }
10122
10507
  try {
10123
- await fsp17.rename(legacyPath, filePath);
10508
+ await fsp18.rename(legacyPath, filePath);
10124
10509
  } catch {
10125
10510
  }
10126
10511
  }
@@ -10130,7 +10515,7 @@ var FsToolboxFileBackend = class {
10130
10515
  }
10131
10516
  async exists(filePath) {
10132
10517
  try {
10133
- await fsp17.access(filePath);
10518
+ await fsp18.access(filePath);
10134
10519
  return true;
10135
10520
  } catch {
10136
10521
  return false;
@@ -10139,7 +10524,7 @@ var FsToolboxFileBackend = class {
10139
10524
  async read(filePath) {
10140
10525
  let buf;
10141
10526
  try {
10142
- buf = await fsp17.readFile(filePath, "utf8");
10527
+ buf = await fsp18.readFile(filePath, "utf8");
10143
10528
  } catch (err) {
10144
10529
  if (isNodeError2(err) && err.code === "ENOENT") return null;
10145
10530
  throw err;
@@ -10155,43 +10540,43 @@ var FsToolboxFileBackend = class {
10155
10540
  async backupCorruptedFile(filePath) {
10156
10541
  try {
10157
10542
  const backupPath = `${filePath}.corrupted.${Date.now()}.bak`;
10158
- await fsp17.copyFile(filePath, backupPath);
10543
+ await fsp18.copyFile(filePath, backupPath);
10159
10544
  await this.purgeExcessBackups(filePath);
10160
10545
  } catch {
10161
10546
  }
10162
10547
  }
10163
10548
  async purgeExcessBackups(filePath) {
10164
- const dir = path41.dirname(filePath);
10165
- const base = path41.basename(filePath);
10549
+ const dir = path42.dirname(filePath);
10550
+ const base = path42.basename(filePath);
10166
10551
  let entries;
10167
10552
  try {
10168
- entries = await fsp17.readdir(dir);
10553
+ entries = await fsp18.readdir(dir);
10169
10554
  } catch {
10170
10555
  return;
10171
10556
  }
10172
- const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path41.join(dir, n) })).sort((a, b) => {
10557
+ const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path42.join(dir, n) })).sort((a, b) => {
10173
10558
  return a.name.localeCompare(b.name);
10174
10559
  });
10175
10560
  const excess = backups.length - this.maxBackupCount;
10176
10561
  if (excess <= 0) return;
10177
10562
  await Promise.all(
10178
- backups.slice(0, excess).map((b) => fsp17.rm(b.filePath).catch(() => void 0))
10563
+ backups.slice(0, excess).map((b) => fsp18.rm(b.filePath).catch(() => void 0))
10179
10564
  );
10180
10565
  }
10181
10566
  async write(filePath, payload) {
10182
- await fsp17.mkdir(path41.dirname(filePath), { recursive: true });
10567
+ await fsp18.mkdir(path42.dirname(filePath), { recursive: true });
10183
10568
  const tmpPath = `${filePath}${TMP_SUFFIX2}`;
10184
10569
  const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
10185
- await fsp17.rm(tmpPath, { force: true });
10186
- const handle = await fsp17.open(tmpPath, "w", FILE_MODE2);
10570
+ await fsp18.rm(tmpPath, { force: true });
10571
+ const handle = await fsp18.open(tmpPath, "w", FILE_MODE2);
10187
10572
  try {
10188
10573
  await handle.writeFile(bytes);
10189
10574
  await handle.sync();
10190
10575
  } finally {
10191
10576
  await handle.close();
10192
10577
  }
10193
- await fsp17.rename(tmpPath, filePath);
10194
- await fsp17.chmod(filePath, FILE_MODE2).catch(() => void 0);
10578
+ await fsp18.rename(tmpPath, filePath);
10579
+ await fsp18.chmod(filePath, FILE_MODE2).catch(() => void 0);
10195
10580
  }
10196
10581
  };
10197
10582
  function coercePersistedToolbox(parsed) {
@@ -10223,7 +10608,7 @@ var ToolboxFileLock = class {
10223
10608
  constructor(filePath, timeoutMs, retryMs) {
10224
10609
  this.acquired = false;
10225
10610
  this.dirPath = `${filePath}.lock`;
10226
- this.pidFilePath = path41.join(this.dirPath, "pid");
10611
+ this.pidFilePath = path42.join(this.dirPath, "pid");
10227
10612
  this.timeoutMs = timeoutMs;
10228
10613
  this.retryMs = retryMs;
10229
10614
  }
@@ -10231,15 +10616,15 @@ var ToolboxFileLock = class {
10231
10616
  const start = Date.now();
10232
10617
  while (true) {
10233
10618
  try {
10234
- await fsp17.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
10235
- await fsp17.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
10619
+ await fsp18.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
10620
+ await fsp18.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
10236
10621
  this.acquired = true;
10237
10622
  return;
10238
10623
  } catch (err) {
10239
10624
  if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
10240
10625
  const stale = await this.isStaleLock();
10241
10626
  if (stale) {
10242
- await fsp17.rm(this.dirPath, { recursive: true, force: true });
10627
+ await fsp18.rm(this.dirPath, { recursive: true, force: true });
10243
10628
  continue;
10244
10629
  }
10245
10630
  if (Date.now() - start >= this.timeoutMs) {
@@ -10252,11 +10637,11 @@ var ToolboxFileLock = class {
10252
10637
  async isStaleLock() {
10253
10638
  let pidStr;
10254
10639
  try {
10255
- pidStr = await fsp17.readFile(this.pidFilePath, "utf8");
10640
+ pidStr = await fsp18.readFile(this.pidFilePath, "utf8");
10256
10641
  } catch {
10257
10642
  try {
10258
- const stat15 = await fsp17.stat(this.dirPath);
10259
- return Date.now() - stat15.mtimeMs > LOCK_STALE_GRACE_MS2;
10643
+ const stat16 = await fsp18.stat(this.dirPath);
10644
+ return Date.now() - stat16.mtimeMs > LOCK_STALE_GRACE_MS2;
10260
10645
  } catch {
10261
10646
  return false;
10262
10647
  }
@@ -10268,12 +10653,12 @@ var ToolboxFileLock = class {
10268
10653
  async release() {
10269
10654
  if (!this.acquired) return;
10270
10655
  this.acquired = false;
10271
- await fsp17.rm(this.dirPath, { recursive: true, force: true });
10656
+ await fsp18.rm(this.dirPath, { recursive: true, force: true });
10272
10657
  }
10273
10658
  };
10274
10659
  function defaultWorkspacePath() {
10275
10660
  if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
10276
- return path41.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
10661
+ return path42.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
10277
10662
  }
10278
10663
  var ToolboxStore = class {
10279
10664
  constructor(opts = {}) {
@@ -10362,7 +10747,7 @@ var ToolboxStore = class {
10362
10747
  async clear(scope) {
10363
10748
  const filePath = this.filePathFor(scope);
10364
10749
  if (!filePath) return;
10365
- await fsp17.rm(filePath, { force: true });
10750
+ await fsp18.rm(filePath, { force: true });
10366
10751
  }
10367
10752
  /** Test seam — resolve the user-scope file path. */
10368
10753
  getUserFilePath() {
@@ -10623,13 +11008,16 @@ export {
10623
11008
  assertSafeRepoId,
10624
11009
  bootstrapDefaults,
10625
11010
  bootstrapPhase5Placeholders,
11011
+ buildContentIdentity,
10626
11012
  buildCopilotCustomizationView,
10627
11013
  buildDefaultReposFile,
11014
+ buildDisabledIdentityMatcher,
10628
11015
  buildGitHubLocalEmail,
10629
11016
  buildGitProxyBase,
10630
11017
  buildSignedHeaders,
10631
11018
  copilotDoctor,
10632
11019
  copilotPrompt,
11020
+ coveredTargetsOf,
10633
11021
  createConsoleLogger,
10634
11022
  createCopilotAuthRequiredError,
10635
11023
  createCopilotNotInstalledError,
@@ -10640,10 +11028,13 @@ export {
10640
11028
  createPluginCatalogService,
10641
11029
  createProjectTools,
10642
11030
  createReposStore,
11031
+ dedupeAdoptionsByPluginManifests,
10643
11032
  defaultRepoSchema as defaultRepoConfigSchema,
10644
11033
  deriveCopilotUserStatus,
10645
11034
  deriveInstallationId,
11035
+ detectUnmanagedWorkspaceContent,
10646
11036
  ensureDefaultsInstalled,
11037
+ findPluginCoveringArtifact,
10647
11038
  findPluginMcpJson,
10648
11039
  findRepoHooksJson,
10649
11040
  fingerprintSource,
@@ -10697,6 +11088,7 @@ export {
10697
11088
  narrowRepoConfig,
10698
11089
  noopLogger,
10699
11090
  parseAgentToolPermissions,
11091
+ parseContentIdentity,
10700
11092
  parseHooksJson,
10701
11093
  parseMcpJson,
10702
11094
  randomInstallationId,
@@ -10713,6 +11105,7 @@ export {
10713
11105
  resolveTaskExecutionPayload,
10714
11106
  resolveWorkspaceContentPlan,
10715
11107
  setUserHomeOverrides,
11108
+ setWorkspacePluginArtifactsDisabled,
10716
11109
  sortByRecentFirst,
10717
11110
  sortByUserOrder,
10718
11111
  supportsPersonalIntegrationKind,
@@ -10720,6 +11113,7 @@ export {
10720
11113
  toArtifactSummary,
10721
11114
  toFileUrl,
10722
11115
  touchLastUsedAt,
11116
+ unmanagedDetectionSignature,
10723
11117
  unzipFile,
10724
11118
  upsertWorkspaceContentSelection,
10725
11119
  userRepoSchema as userRepoConfigSchema,