@alessandroraffa/tangyr 0.21.0 → 0.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17874,8 +17874,8 @@ async function runCacheClearCommand(options, logger) {
17874
17874
  }
17875
17875
 
17876
17876
  // src/commands/cleanup.ts
17877
- import fs25 from "fs";
17878
- import path22 from "path";
17877
+ import fs27 from "fs";
17878
+ import path24 from "path";
17879
17879
 
17880
17880
  // node_modules/glob/dist/esm/index.min.js
17881
17881
  import { fileURLToPath as Wi } from "url";
@@ -21021,8 +21021,8 @@ function removeManagedEntries(filePath, parser, serializer, removeManaged, logge
21021
21021
  }
21022
21022
 
21023
21023
  // src/commands/uninstall.ts
21024
- import fs24 from "fs";
21025
- import path21 from "path";
21024
+ import fs26 from "fs";
21025
+ import path23 from "path";
21026
21026
 
21027
21027
  // src/core/managed-sources.ts
21028
21028
  var import_yaml5 = __toESM(require_dist(), 1);
@@ -21491,25 +21491,134 @@ function reportHooksSourceConflict(report, targets, hooksDir, hookNames) {
21491
21491
  return message;
21492
21492
  }
21493
21493
 
21494
- // src/core/remove.ts
21494
+ // src/adapters/hook-scripts-bridge.ts
21495
+ import fs24 from "fs";
21496
+ import path22 from "path";
21497
+
21498
+ // src/utils/fs.ts
21495
21499
  import fs23 from "fs";
21500
+ import path21 from "path";
21501
+ function ensureDir(dirPath) {
21502
+ fs23.mkdirSync(dirPath, { recursive: true });
21503
+ }
21504
+ function createRelativeSymlink(source, destination, type, options) {
21505
+ const absoluteSource = path21.resolve(source);
21506
+ const relative = path21.relative(
21507
+ realPathOrInput(path21.dirname(destination)),
21508
+ absoluteSource
21509
+ );
21510
+ if (process.platform === "win32" && type === "dir") {
21511
+ fs23.symlinkSync(absoluteSource, destination, "junction");
21512
+ return;
21513
+ }
21514
+ if (process.platform === "win32" && type === "file") {
21515
+ try {
21516
+ fs23.symlinkSync(relative, destination, "file");
21517
+ } catch (error) {
21518
+ if (!hasErrorCode(error, "EPERM")) {
21519
+ throw error;
21520
+ }
21521
+ if (!options?.provenanceMarker) {
21522
+ throw new Error(
21523
+ `${destination}: file symlink unavailable on Windows and no provenance marker was provided.`
21524
+ );
21525
+ }
21526
+ createManagedCopy(
21527
+ source,
21528
+ destination,
21529
+ options.provenanceMarker,
21530
+ options.logger
21531
+ );
21532
+ options.logger?.warn(
21533
+ `${destination}: file symlink unavailable on Windows \u2014 using managed copy with provenance metadata (behavioral contract C-09)`
21534
+ );
21535
+ }
21536
+ return;
21537
+ }
21538
+ fs23.symlinkSync(relative, destination, type);
21539
+ }
21540
+ function createManagedCopy(source, destination, provenanceMarker, _logger) {
21541
+ ensureDir(path21.dirname(destination));
21542
+ const content = fs23.readFileSync(source, "utf8");
21543
+ fs23.writeFileSync(destination, `${provenanceMarker}
21544
+ ${content}`);
21545
+ }
21546
+ function hasErrorCode(error, expectedCode) {
21547
+ return typeof error === "object" && error !== null && "code" in error && error.code === expectedCode;
21548
+ }
21549
+ function realPathOrInput(filePath) {
21550
+ try {
21551
+ return fs23.realpathSync(filePath);
21552
+ } catch {
21553
+ return filePath;
21554
+ }
21555
+ }
21556
+
21557
+ // src/adapters/hook-scripts-bridge.ts
21558
+ var SHARED_HOOK_SCRIPTS_KEY = "shared/hook_scripts";
21559
+ function syncHookScriptsBridge(args) {
21560
+ const { source, destination, config, sourceRoot, options, logger } = args;
21561
+ if (!fs24.existsSync(source)) {
21562
+ return {
21563
+ component: "hooks",
21564
+ status: "skipped",
21565
+ path: `${source}: source not configured`
21566
+ };
21567
+ }
21568
+ if (options.dryRun) {
21569
+ logger.info(`[dry-run] hooks: symlink ${source} -> ${destination}`);
21570
+ return { component: "hooks", status: "dry-run", path: destination };
21571
+ }
21572
+ const artifactClass = classifyArtifact(destination, config, sourceRoot);
21573
+ if (artifactClass === "managed-symlink" && isSymlinkTo(destination, source)) {
21574
+ return { component: "hooks", status: "skipped-current", path: destination };
21575
+ }
21576
+ if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
21577
+ fs24.unlinkSync(destination);
21578
+ } else if (artifactClass === "unmanaged-conflict" && !args.onConflict()) {
21579
+ return { component: "hooks", status: "conflict", path: destination };
21580
+ } else if (artifactClass === "managed-symlink") {
21581
+ fs24.unlinkSync(destination);
21582
+ }
21583
+ ensureDir(path22.dirname(destination));
21584
+ createRelativeSymlink(source, destination, "dir");
21585
+ if (args.manifest) {
21586
+ addArtifactToManifest(args.manifest, {
21587
+ relativePath: SHARED_HOOK_SCRIPTS_KEY,
21588
+ hash: `dir:${source}`,
21589
+ origin: "tangyr-managed"
21590
+ });
21591
+ }
21592
+ return { component: "hooks", status: "success", path: destination };
21593
+ }
21594
+ function isSymlinkTo(linkPath, source) {
21595
+ try {
21596
+ return fs24.realpathSync(linkPath) === fs24.realpathSync(source);
21597
+ } catch {
21598
+ return false;
21599
+ }
21600
+ }
21601
+
21602
+ // src/core/remove.ts
21603
+ import fs25 from "fs";
21496
21604
  function removeManagedPath(managedPath) {
21497
21605
  let stat;
21498
21606
  try {
21499
- stat = fs23.lstatSync(managedPath);
21607
+ stat = fs25.lstatSync(managedPath);
21500
21608
  } catch {
21501
21609
  return;
21502
21610
  }
21503
21611
  if (stat.isSymbolicLink() || stat.isFile()) {
21504
- fs23.unlinkSync(managedPath);
21612
+ fs25.unlinkSync(managedPath);
21505
21613
  return;
21506
21614
  }
21507
21615
  if (stat.isDirectory()) {
21508
- fs23.rmSync(managedPath, { recursive: true, force: true });
21616
+ fs25.rmSync(managedPath, { recursive: true, force: true });
21509
21617
  }
21510
21618
  }
21511
21619
 
21512
21620
  // src/commands/uninstall.ts
21621
+ var SHARED_ARTIFACT_TOOL = SHARED_HOOK_SCRIPTS_KEY.split("/")[0];
21513
21622
  var codexManagedMcpStart = "# tangyr: managed mcp start";
21514
21623
  var codexManagedMcpEnd = "# tangyr: managed mcp end";
21515
21624
  var legacyClaudeSharedPermission = "Read(~/.agents/**)";
@@ -21549,8 +21658,9 @@ async function runUninstallCommand(options, logger) {
21549
21658
  process.exit(1);
21550
21659
  }
21551
21660
  const selectedTools = resolveSelectedTools(options.tools, manifest, runtime);
21661
+ const fullUninstall = isFullToolUninstall(manifest, selectedTools);
21552
21662
  const manifestArtifacts = manifest.artifacts.filter(
21553
- (file) => shouldRemoveManifestFile(file.relativePath, selectedTools)
21663
+ (file) => shouldRemoveManifestFile(file.relativePath, selectedTools, fullUninstall)
21554
21664
  );
21555
21665
  const kitArtifacts = runtime ? resolveKitManagedArtifacts(
21556
21666
  runtime,
@@ -21613,7 +21723,7 @@ async function runUninstallCommand(options, logger) {
21613
21723
  runtime
21614
21724
  );
21615
21725
  for (const actualPath of paths) {
21616
- if (actualPath && (fs24.existsSync(actualPath) || isSymlink(actualPath))) {
21726
+ if (actualPath && (fs26.existsSync(actualPath) || isSymlink(actualPath))) {
21617
21727
  removeManagedPath(actualPath);
21618
21728
  logger.verbose(` removed: ${actualPath}`);
21619
21729
  removedCount++;
@@ -21627,7 +21737,7 @@ async function runUninstallCommand(options, logger) {
21627
21737
  scope,
21628
21738
  runtime
21629
21739
  );
21630
- if (actualPath && (fs24.existsSync(actualPath) || isSymlink(actualPath))) {
21740
+ if (actualPath && (fs26.existsSync(actualPath) || isSymlink(actualPath))) {
21631
21741
  removeManagedPath(actualPath);
21632
21742
  logger.verbose(` removed: ${actualPath}`);
21633
21743
  removedCount++;
@@ -21655,27 +21765,30 @@ async function runUninstallCommand(options, logger) {
21655
21765
  );
21656
21766
  }
21657
21767
  }
21658
- const fullUninstall = isFullToolUninstall(manifest, selectedTools);
21659
21768
  if (fullUninstall) {
21660
21769
  for (const backup of manifest.backups) {
21661
21770
  restoreBackup(backup, logger);
21662
21771
  }
21663
- const manifestPath = path21.join(scopePath, "manifest.json");
21664
- if (fs24.existsSync(manifestPath)) {
21665
- fs24.unlinkSync(manifestPath);
21772
+ const manifestPath = path23.join(scopePath, "manifest.json");
21773
+ if (fs26.existsSync(manifestPath)) {
21774
+ fs26.unlinkSync(manifestPath);
21666
21775
  logger.verbose(` removed manifest: ${manifestPath}`);
21667
21776
  }
21668
21777
  try {
21669
- const remaining = fs24.readdirSync(scopePath);
21778
+ const remaining = fs26.readdirSync(scopePath);
21670
21779
  if (remaining.length === 0) {
21671
- fs24.rmdirSync(scopePath);
21780
+ fs26.rmdirSync(scopePath);
21672
21781
  logger.verbose(` removed empty scope directory: ${scopePath}`);
21673
21782
  }
21674
21783
  } catch {
21675
21784
  }
21676
21785
  } else if (manifestWasPresent) {
21677
21786
  manifest.artifacts = manifest.artifacts.filter(
21678
- (file) => !shouldRemoveManifestFile(file.relativePath, selectedTools)
21787
+ (file) => !shouldRemoveManifestFile(
21788
+ file.relativePath,
21789
+ selectedTools,
21790
+ fullUninstall
21791
+ )
21679
21792
  );
21680
21793
  manifest.tools = manifest.tools.filter((tool) => !selectedTools.has(tool));
21681
21794
  writeManifest(scopePath, manifest);
@@ -21694,6 +21807,12 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21694
21807
  const [tool, ...rest] = parts;
21695
21808
  const component = rest.join("/");
21696
21809
  const projectRoot = scope === "project" ? process.cwd() : void 0;
21810
+ if (tool === SHARED_ARTIFACT_TOOL) {
21811
+ if (component === "hook_scripts") {
21812
+ return resolveSharedHookScriptsPath(runtime, scope, projectRoot);
21813
+ }
21814
+ return null;
21815
+ }
21697
21816
  if (tool === "claude-code") {
21698
21817
  const claudePaths = resolveClaudeCodePaths(runtime, scope, projectRoot);
21699
21818
  if (!claudePaths) {
@@ -21708,7 +21827,7 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21708
21827
  if (!slotPath) {
21709
21828
  return null;
21710
21829
  }
21711
- return subPath ? path21.join(slotPath, subPath) : slotPath;
21830
+ return subPath ? path23.join(slotPath, subPath) : slotPath;
21712
21831
  }
21713
21832
  if (tool === "copilot") {
21714
21833
  const copilotPaths = resolveCopilotPaths(runtime, scope, projectRoot);
@@ -21716,20 +21835,23 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21716
21835
  return copilotPaths.skills;
21717
21836
  }
21718
21837
  if (component.startsWith("agents/")) {
21719
- return path21.join(copilotPaths.agents, component.slice("agents/".length));
21838
+ return path23.join(copilotPaths.agents, component.slice("agents/".length));
21720
21839
  }
21721
21840
  if (component.startsWith("prompts/")) {
21722
- return path21.join(
21841
+ return path23.join(
21723
21842
  copilotPaths.prompts,
21724
21843
  component.slice("prompts/".length)
21725
21844
  );
21726
21845
  }
21727
21846
  if (component.startsWith("hooks/")) {
21728
- return path21.join(copilotPaths.hooks, component.slice("hooks/".length));
21847
+ return path23.join(copilotPaths.hooks, component.slice("hooks/".length));
21729
21848
  }
21730
- if (component === path21.basename(copilotPaths.mcpSharedFile)) {
21849
+ if (component === path23.basename(copilotPaths.mcpSharedFile)) {
21731
21850
  return copilotPaths.mcpSharedFile;
21732
21851
  }
21852
+ if (copilotPaths.instructions !== null && component === path23.basename(copilotPaths.instructions)) {
21853
+ return copilotPaths.instructions;
21854
+ }
21733
21855
  return null;
21734
21856
  }
21735
21857
  if (tool === "codex") {
@@ -21741,15 +21863,15 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21741
21863
  return codexPaths.skills;
21742
21864
  }
21743
21865
  if (component.startsWith("agents/")) {
21744
- return path21.join(codexPaths.agents, component.slice("agents/".length));
21866
+ return path23.join(codexPaths.agents, component.slice("agents/".length));
21745
21867
  }
21746
21868
  if (component.startsWith("rules/")) {
21747
- return path21.join(codexPaths.rules, component.slice("rules/".length));
21869
+ return path23.join(codexPaths.rules, component.slice("rules/".length));
21748
21870
  }
21749
- if (component === path21.basename(codexPaths.hooksSharedFile)) {
21871
+ if (component === path23.basename(codexPaths.hooksSharedFile)) {
21750
21872
  return codexPaths.hooksSharedFile;
21751
21873
  }
21752
- if (component === path21.basename(codexPaths.mcpSharedFile)) {
21874
+ if (component === path23.basename(codexPaths.mcpSharedFile)) {
21753
21875
  return codexPaths.mcpSharedFile;
21754
21876
  }
21755
21877
  return null;
@@ -21766,15 +21888,15 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21766
21888
  return opencodePaths.skillsShared;
21767
21889
  }
21768
21890
  if (component.startsWith("agents/")) {
21769
- return path21.join(opencodePaths.agents, component.slice("agents/".length));
21891
+ return path23.join(opencodePaths.agents, component.slice("agents/".length));
21770
21892
  }
21771
21893
  if (component.startsWith("commands/")) {
21772
- return path21.join(
21894
+ return path23.join(
21773
21895
  opencodePaths.commands,
21774
21896
  component.slice("commands/".length)
21775
21897
  );
21776
21898
  }
21777
- if (component === path21.basename(opencodePaths.configDoc)) {
21899
+ if (component === path23.basename(opencodePaths.configDoc)) {
21778
21900
  return opencodePaths.configDoc;
21779
21901
  }
21780
21902
  return null;
@@ -21800,13 +21922,13 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21800
21922
  return cursorPaths.permissionsFile;
21801
21923
  }
21802
21924
  if (component.startsWith("agents/")) {
21803
- return path21.join(cursorPaths.agents, component.slice("agents/".length));
21925
+ return path23.join(cursorPaths.agents, component.slice("agents/".length));
21804
21926
  }
21805
21927
  if (component.startsWith("rules/")) {
21806
- return path21.join(cursorPaths.rules, component.slice("rules/".length));
21928
+ return path23.join(cursorPaths.rules, component.slice("rules/".length));
21807
21929
  }
21808
21930
  if (component.startsWith("skills/")) {
21809
- return path21.join(cursorPaths.skills, component.slice("skills/".length));
21931
+ return path23.join(cursorPaths.skills, component.slice("skills/".length));
21810
21932
  }
21811
21933
  return null;
21812
21934
  }
@@ -21822,10 +21944,10 @@ function resolveManifestKeyToPath(key, scope, runtime) {
21822
21944
  return clinePaths.mcpFile;
21823
21945
  }
21824
21946
  if (component.startsWith("rules/")) {
21825
- return path21.join(clinePaths.rules, component.slice("rules/".length));
21947
+ return path23.join(clinePaths.rules, component.slice("rules/".length));
21826
21948
  }
21827
21949
  if (component.startsWith("workflows/")) {
21828
- return path21.join(
21950
+ return path23.join(
21829
21951
  clinePaths.workflows,
21830
21952
  component.slice("workflows/".length)
21831
21953
  );
@@ -21859,22 +21981,22 @@ function resolveClaudeCodePaths(runtime, scope, projectRoot) {
21859
21981
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21860
21982
  if (scope === "global") {
21861
21983
  return {
21862
- instructions: path21.join(home, ".claude", "CLAUDE.md"),
21863
- agents: path21.join(home, ".claude", "agents"),
21864
- skills: path21.join(home, ".claude", "skills"),
21865
- commands: path21.join(home, ".claude", "commands"),
21866
- rules: path21.join(home, ".claude", "rules"),
21867
- output_styles: path21.join(home, ".claude", "output-styles")
21984
+ instructions: path23.join(home, ".claude", "CLAUDE.md"),
21985
+ agents: path23.join(home, ".claude", "agents"),
21986
+ skills: path23.join(home, ".claude", "skills"),
21987
+ commands: path23.join(home, ".claude", "commands"),
21988
+ rules: path23.join(home, ".claude", "rules"),
21989
+ output_styles: path23.join(home, ".claude", "output-styles")
21868
21990
  };
21869
21991
  }
21870
21992
  const root = projectRoot ?? process.cwd();
21871
21993
  return {
21872
- instructions: path21.join(root, "CLAUDE.md"),
21873
- agents: path21.join(root, ".claude", "agents"),
21874
- skills: path21.join(root, ".claude", "skills"),
21875
- commands: path21.join(root, ".claude", "commands"),
21876
- rules: path21.join(root, ".claude", "rules"),
21877
- output_styles: path21.join(root, ".claude", "output-styles")
21994
+ instructions: path23.join(root, "CLAUDE.md"),
21995
+ agents: path23.join(root, ".claude", "agents"),
21996
+ skills: path23.join(root, ".claude", "skills"),
21997
+ commands: path23.join(root, ".claude", "commands"),
21998
+ rules: path23.join(root, ".claude", "rules"),
21999
+ output_styles: path23.join(root, ".claude", "output-styles")
21878
22000
  };
21879
22001
  }
21880
22002
  function resolveClaudeRootsForUninstall(manifest, runtime) {
@@ -21893,12 +22015,12 @@ function resolveClaudeRootsForUninstall(manifest, runtime) {
21893
22015
  projectRoot: void 0,
21894
22016
  mappings: mappings.platformPaths
21895
22017
  })["claude-code"];
21896
- return [path21.dirname(profile.agents)];
22018
+ return [path23.dirname(profile.agents)];
21897
22019
  } catch {
21898
22020
  }
21899
22021
  }
21900
22022
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21901
- return [path21.resolve(expandHome(`${home}/.claude`))];
22023
+ return [path23.resolve(expandHome(`${home}/.claude`))];
21902
22024
  }
21903
22025
  function resolveAllClaudeRoots(manifest, runtime, claudeConfigDirs) {
21904
22026
  const home = process.env.HOME ?? process.env.USERPROFILE;
@@ -21910,13 +22032,13 @@ function resolveAllClaudeRoots(manifest, runtime, claudeConfigDirs) {
21910
22032
  env: process.env.TANGYR_CLAUDE_CONFIG_DIRS,
21911
22033
  config: runtime.config.claudeCodeGlobalPaths
21912
22034
  })
21913
- ].map((root) => path21.resolve(root))
22035
+ ].map((root) => path23.resolve(root))
21914
22036
  );
21915
- if (home && fs24.existsSync(home)) {
22037
+ if (home && fs26.existsSync(home)) {
21916
22038
  try {
21917
- for (const entry of fs24.readdirSync(home, { withFileTypes: true })) {
22039
+ for (const entry of fs26.readdirSync(home, { withFileTypes: true })) {
21918
22040
  if (entry.isDirectory() && entry.name.startsWith(".claude-")) {
21919
- roots.add(path21.resolve(home, entry.name));
22041
+ roots.add(path23.resolve(home, entry.name));
21920
22042
  }
21921
22043
  }
21922
22044
  } catch {
@@ -21935,13 +22057,13 @@ function resolveManifestKeyToMultiPaths(key, claudeRoots, runtime) {
21935
22057
  const rootsToResolve = isShared ? [claudeRoots[0]] : claudeRoots;
21936
22058
  if (!runtime) {
21937
22059
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21938
- const defaultRoot = path21.resolve(expandHome(`${home}/.claude`));
22060
+ const defaultRoot = path23.resolve(expandHome(`${home}/.claude`));
21939
22061
  return rootsToResolve.flatMap((root) => {
21940
22062
  const hardcoded = resolveHardcodedClaudeSlot(root, defaultRoot, slotName);
21941
22063
  if (!hardcoded) {
21942
22064
  return [];
21943
22065
  }
21944
- return [subPath ? path21.join(hardcoded, subPath) : hardcoded];
22066
+ return [subPath ? path23.join(hardcoded, subPath) : hardcoded];
21945
22067
  });
21946
22068
  }
21947
22069
  try {
@@ -21960,17 +22082,17 @@ function resolveManifestKeyToMultiPaths(key, claudeRoots, runtime) {
21960
22082
  if (!slotPath) {
21961
22083
  return [];
21962
22084
  }
21963
- return [subPath ? path21.join(slotPath, subPath) : slotPath];
22085
+ return [subPath ? path23.join(slotPath, subPath) : slotPath];
21964
22086
  });
21965
22087
  } catch {
21966
22088
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21967
- const defaultRoot = path21.resolve(expandHome(`${home}/.claude`));
22089
+ const defaultRoot = path23.resolve(expandHome(`${home}/.claude`));
21968
22090
  return rootsToResolve.flatMap((root) => {
21969
22091
  const hardcoded = resolveHardcodedClaudeSlot(root, defaultRoot, slotName);
21970
22092
  if (!hardcoded) {
21971
22093
  return [];
21972
22094
  }
21973
- return [subPath ? path21.join(hardcoded, subPath) : hardcoded];
22095
+ return [subPath ? path23.join(hardcoded, subPath) : hardcoded];
21974
22096
  });
21975
22097
  }
21976
22098
  }
@@ -21993,9 +22115,9 @@ function resolveHardcodedClaudeSlot(root, defaultRoot, slotName) {
21993
22115
  return null;
21994
22116
  }
21995
22117
  if (root === defaultRoot) {
21996
- return path21.join(root, relative);
22118
+ return path23.join(root, relative);
21997
22119
  }
21998
- return path21.join(root, relative);
22120
+ return path23.join(root, relative);
21999
22121
  }
22000
22122
  function resolveRuntimeIfAvailable(options, logger) {
22001
22123
  try {
@@ -22024,8 +22146,11 @@ function resolveSelectedTools(rawTools, manifest, runtime) {
22024
22146
  }
22025
22147
  return selectedTools;
22026
22148
  }
22027
- function shouldRemoveManifestFile(relativePath, selectedTools) {
22149
+ function shouldRemoveManifestFile(relativePath, selectedTools, isFullUninstall) {
22028
22150
  const tool = relativePath.split("/")[0];
22151
+ if (tool === SHARED_ARTIFACT_TOOL) {
22152
+ return isFullUninstall;
22153
+ }
22029
22154
  return selectedTools.has(tool);
22030
22155
  }
22031
22156
  function isFullToolUninstall(manifest, selectedTools) {
@@ -22041,7 +22166,7 @@ function addCompiledGlobPlans(artifacts, pattern, expectedTarget, component) {
22041
22166
  for (const artifactPath of ts(pattern).sort()) {
22042
22167
  let marker = null;
22043
22168
  try {
22044
- marker = extractProvenanceMarker(fs24.readFileSync(artifactPath, "utf8"));
22169
+ marker = extractProvenanceMarker(fs26.readFileSync(artifactPath, "utf8"));
22045
22170
  } catch {
22046
22171
  continue;
22047
22172
  }
@@ -22061,12 +22186,12 @@ function addCompiledGlobPlans(artifacts, pattern, expectedTarget, component) {
22061
22186
  function addManagedSymlinkChildren(artifacts, directory, runtime, component) {
22062
22187
  let entries;
22063
22188
  try {
22064
- entries = fs24.readdirSync(directory, { withFileTypes: true });
22189
+ entries = fs26.readdirSync(directory, { withFileTypes: true });
22065
22190
  } catch {
22066
22191
  return;
22067
22192
  }
22068
22193
  for (const entry of entries) {
22069
- const artifactPath = path21.join(directory, entry.name);
22194
+ const artifactPath = path23.join(directory, entry.name);
22070
22195
  const artifactClass = classifyArtifact(
22071
22196
  artifactPath,
22072
22197
  runtime.config,
@@ -22105,8 +22230,8 @@ function collectStringValuesByKey(value, keyName, values) {
22105
22230
  }
22106
22231
  function addLegacyOperatingGuideCopies(artifacts, claudeRoot) {
22107
22232
  for (const artifactPath of [
22108
- ...ts(path21.join(claudeRoot, "CLAUDE.md.bak.*")),
22109
- ...ts(path21.join(claudeRoot, "AGENTS.md.bak.*"))
22233
+ ...ts(path23.join(claudeRoot, "CLAUDE.md.bak.*")),
22234
+ ...ts(path23.join(claudeRoot, "AGENTS.md.bak.*"))
22110
22235
  ].sort()) {
22111
22236
  artifacts.push({
22112
22237
  component: "claude-code legacy operating guide backup",
@@ -22160,7 +22285,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22160
22285
  for (const skill of runtime.kitInfo.components.skills.declared) {
22161
22286
  artifacts.push({
22162
22287
  component: "claude-code skill",
22163
- path: path21.join(profile.skills, skill),
22288
+ path: path23.join(profile.skills, skill),
22164
22289
  kind: "direct"
22165
22290
  });
22166
22291
  }
@@ -22179,7 +22304,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22179
22304
  if (isFullToolUninstall(manifest, selectedTools)) {
22180
22305
  addLegacyOperatingGuideCopies(
22181
22306
  artifacts,
22182
- path21.dirname(profile.instructions)
22307
+ path23.dirname(profile.instructions)
22183
22308
  );
22184
22309
  }
22185
22310
  }
@@ -22202,20 +22327,20 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22202
22327
  if (selectedTools.has("copilot")) {
22203
22328
  addCompiledGlobPlans(
22204
22329
  artifacts,
22205
- path21.join(copilotPaths.agents, "*.agent.md"),
22330
+ path23.join(copilotPaths.agents, "*.agent.md"),
22206
22331
  "copilot",
22207
22332
  "copilot agent residue"
22208
22333
  );
22209
22334
  addCompiledGlobPlans(
22210
22335
  artifacts,
22211
- path21.join(copilotPaths.prompts, "*.prompt.md"),
22336
+ path23.join(copilotPaths.prompts, "*.prompt.md"),
22212
22337
  "copilot",
22213
22338
  "copilot prompt residue"
22214
22339
  );
22215
22340
  for (const agentEntry of runtime.kitInfo.components.agents.declared) {
22216
22341
  artifacts.push({
22217
22342
  component: "copilot agent",
22218
- path: path21.join(copilotPaths.agents, `${agentEntry}.agent.md`),
22343
+ path: path23.join(copilotPaths.agents, `${agentEntry}.agent.md`),
22219
22344
  kind: "compiled",
22220
22345
  expectedTarget: "copilot",
22221
22346
  expectedSource: `agents/${agentEntry}.md`
@@ -22224,7 +22349,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22224
22349
  for (const commandEntry of runtime.kitInfo.components.commands.declared) {
22225
22350
  artifacts.push({
22226
22351
  component: "copilot prompt",
22227
- path: path21.join(copilotPaths.prompts, `${commandEntry}.prompt.md`),
22352
+ path: path23.join(copilotPaths.prompts, `${commandEntry}.prompt.md`),
22228
22353
  kind: "compiled",
22229
22354
  expectedTarget: "copilot",
22230
22355
  expectedSource: `commands/${commandEntry}.md`
@@ -22240,13 +22365,13 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22240
22365
  artifacts.push(
22241
22366
  {
22242
22367
  component: "copilot hooks",
22243
- path: path21.join(copilotPaths.hooks, "tangyr-managed.json"),
22368
+ path: path23.join(copilotPaths.hooks, "tangyr-managed.json"),
22244
22369
  kind: "managed-json",
22245
22370
  expectedTarget: "copilot"
22246
22371
  },
22247
22372
  {
22248
22373
  component: "copilot legacy hooks",
22249
- path: path21.join(copilotPaths.hooks, "proteus-managed.json"),
22374
+ path: path23.join(copilotPaths.hooks, "proteus-managed.json"),
22250
22375
  kind: "managed-json",
22251
22376
  expectedTarget: "copilot"
22252
22377
  }
@@ -22255,7 +22380,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22255
22380
  if (hooksSourceExists(runtime.sourceRoot, runtime.config)) {
22256
22381
  artifacts.push({
22257
22382
  component: "copilot hooks",
22258
- path: path21.join(copilotPaths.hooks, "tangyr-managed.json"),
22383
+ path: path23.join(copilotPaths.hooks, "tangyr-managed.json"),
22259
22384
  kind: "compiled",
22260
22385
  expectedTarget: "copilot",
22261
22386
  expectedSource: hooksSource
@@ -22266,7 +22391,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22266
22391
  runtime.config
22267
22392
  );
22268
22393
  const mcpSource = mcpSourceLabel(runtime.config);
22269
- if (fs24.existsSync(mcpSourcePath)) {
22394
+ if (fs26.existsSync(mcpSourcePath)) {
22270
22395
  artifacts.push({
22271
22396
  component: "copilot mcp",
22272
22397
  path: copilotPaths.mcpSharedFile,
@@ -22285,13 +22410,13 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22285
22410
  );
22286
22411
  addCompiledGlobPlans(
22287
22412
  artifacts,
22288
- path21.join(codexPaths.agents, "*.toml"),
22413
+ path23.join(codexPaths.agents, "*.toml"),
22289
22414
  "codex",
22290
22415
  "codex agent residue"
22291
22416
  );
22292
22417
  addCompiledGlobPlans(
22293
22418
  artifacts,
22294
- path21.join(codexPaths.rules, "*.rules"),
22419
+ path23.join(codexPaths.rules, "*.rules"),
22295
22420
  "codex",
22296
22421
  "codex rule residue"
22297
22422
  );
@@ -22305,7 +22430,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22305
22430
  for (const agentEntry of runtime.kitInfo.components.agents.declared) {
22306
22431
  artifacts.push({
22307
22432
  component: "codex agent",
22308
- path: path21.join(codexPaths.agents, `${agentEntry}.toml`),
22433
+ path: path23.join(codexPaths.agents, `${agentEntry}.toml`),
22309
22434
  kind: "compiled",
22310
22435
  expectedTarget: "codex",
22311
22436
  expectedSource: `agents/${agentEntry}.md`
@@ -22314,7 +22439,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22314
22439
  for (const ruleEntry of runtime.kitInfo.components.rules.declared) {
22315
22440
  artifacts.push({
22316
22441
  component: "codex rule",
22317
- path: path21.join(codexPaths.rules, `${ruleEntry}.rules`),
22442
+ path: path23.join(codexPaths.rules, `${ruleEntry}.rules`),
22318
22443
  kind: "compiled",
22319
22444
  expectedTarget: "codex",
22320
22445
  expectedSource: `rules/${ruleEntry}.md`
@@ -22341,7 +22466,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22341
22466
  runtime.config
22342
22467
  );
22343
22468
  const mcpSource = mcpSourceLabel(runtime.config);
22344
- if (fs24.existsSync(mcpSourcePath)) {
22469
+ if (fs26.existsSync(mcpSourcePath)) {
22345
22470
  artifacts.push({
22346
22471
  component: "codex mcp",
22347
22472
  path: codexPaths.mcpSharedFile,
@@ -22366,13 +22491,13 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22366
22491
  );
22367
22492
  addCompiledGlobPlans(
22368
22493
  artifacts,
22369
- path21.join(opencodePaths.agents, "*.md"),
22494
+ path23.join(opencodePaths.agents, "*.md"),
22370
22495
  "opencode",
22371
22496
  "opencode agent residue"
22372
22497
  );
22373
22498
  addCompiledGlobPlans(
22374
22499
  artifacts,
22375
- path21.join(opencodePaths.commands, "*.md"),
22500
+ path23.join(opencodePaths.commands, "*.md"),
22376
22501
  "opencode",
22377
22502
  "opencode command residue"
22378
22503
  );
@@ -22391,7 +22516,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22391
22516
  for (const agentEntry of runtime.kitInfo.components.agents.declared) {
22392
22517
  artifacts.push({
22393
22518
  component: "opencode agent",
22394
- path: path21.join(opencodePaths.agents, `${agentEntry}.md`),
22519
+ path: path23.join(opencodePaths.agents, `${agentEntry}.md`),
22395
22520
  kind: "compiled",
22396
22521
  expectedTarget: "opencode",
22397
22522
  expectedSource: `agents/${agentEntry}.md`
@@ -22400,7 +22525,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22400
22525
  for (const commandEntry of runtime.kitInfo.components.commands.declared) {
22401
22526
  artifacts.push({
22402
22527
  component: "opencode command",
22403
- path: path21.join(opencodePaths.commands, `${commandEntry}.md`),
22528
+ path: path23.join(opencodePaths.commands, `${commandEntry}.md`),
22404
22529
  kind: "compiled",
22405
22530
  expectedTarget: "opencode",
22406
22531
  expectedSource: `commands/${commandEntry}.md`
@@ -22418,7 +22543,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22418
22543
  runtime.config
22419
22544
  );
22420
22545
  const mcpSource = mcpSourceLabel(runtime.config);
22421
- if (fs24.existsSync(mcpSourcePath)) {
22546
+ if (fs26.existsSync(mcpSourcePath)) {
22422
22547
  artifacts.push({
22423
22548
  component: "opencode config",
22424
22549
  path: opencodePaths.configDoc,
@@ -22455,19 +22580,19 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22455
22580
  );
22456
22581
  addCompiledGlobPlans(
22457
22582
  artifacts,
22458
- path21.join(cursorPaths.agents, "*.md"),
22583
+ path23.join(cursorPaths.agents, "*.md"),
22459
22584
  "cursor",
22460
22585
  "cursor agent residue"
22461
22586
  );
22462
22587
  addCompiledGlobPlans(
22463
22588
  artifacts,
22464
- path21.join(cursorPaths.rules, "*.mdc"),
22589
+ path23.join(cursorPaths.rules, "*.mdc"),
22465
22590
  "cursor",
22466
22591
  "cursor rule residue"
22467
22592
  );
22468
22593
  addCompiledGlobPlans(
22469
22594
  artifacts,
22470
- path21.join(cursorPaths.skills, "*", "SKILL.md"),
22595
+ path23.join(cursorPaths.skills, "*", "SKILL.md"),
22471
22596
  "cursor",
22472
22597
  "cursor command residue"
22473
22598
  );
@@ -22481,7 +22606,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22481
22606
  for (const agentEntry of runtime.kitInfo.components.agents.declared) {
22482
22607
  artifacts.push({
22483
22608
  component: "cursor agent",
22484
- path: path21.join(cursorPaths.agents, `${agentEntry}.md`),
22609
+ path: path23.join(cursorPaths.agents, `${agentEntry}.md`),
22485
22610
  kind: "compiled",
22486
22611
  expectedTarget: "cursor",
22487
22612
  expectedSource: `agents/${agentEntry}.md`
@@ -22499,7 +22624,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22499
22624
  runtime.config
22500
22625
  );
22501
22626
  const mcpSource = mcpSourceLabel(runtime.config);
22502
- if (fs24.existsSync(mcpSourcePath)) {
22627
+ if (fs26.existsSync(mcpSourcePath)) {
22503
22628
  artifacts.push({
22504
22629
  component: "cursor mcp",
22505
22630
  path: cursorPaths.mcpFile,
@@ -22518,13 +22643,13 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22518
22643
  );
22519
22644
  addCompiledGlobPlans(
22520
22645
  artifacts,
22521
- path21.join(clinePaths.rules, "*.md"),
22646
+ path23.join(clinePaths.rules, "*.md"),
22522
22647
  "cline",
22523
22648
  "cline rule residue"
22524
22649
  );
22525
22650
  addCompiledGlobPlans(
22526
22651
  artifacts,
22527
- path21.join(clinePaths.workflows, "*.md"),
22652
+ path23.join(clinePaths.workflows, "*.md"),
22528
22653
  "cline",
22529
22654
  "cline workflow residue"
22530
22655
  );
@@ -22552,7 +22677,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22552
22677
  for (const ruleEntry of runtime.kitInfo.components.rules.declared) {
22553
22678
  artifacts.push({
22554
22679
  component: "cline rule",
22555
- path: path21.join(clinePaths.rules, `${ruleEntry}.md`),
22680
+ path: path23.join(clinePaths.rules, `${ruleEntry}.md`),
22556
22681
  kind: "compiled",
22557
22682
  expectedTarget: "cline",
22558
22683
  expectedSource: `rules/${ruleEntry}.md`
@@ -22561,7 +22686,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22561
22686
  for (const commandEntry of runtime.kitInfo.components.commands.declared) {
22562
22687
  artifacts.push({
22563
22688
  component: "cline workflow",
22564
- path: path21.join(clinePaths.workflows, `${commandEntry}.md`),
22689
+ path: path23.join(clinePaths.workflows, `${commandEntry}.md`),
22565
22690
  kind: "compiled",
22566
22691
  expectedTarget: "cline",
22567
22692
  expectedSource: `commands/${commandEntry}.md`
@@ -22579,7 +22704,7 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22579
22704
  runtime.config
22580
22705
  );
22581
22706
  const mcpSource = mcpSourceLabel(runtime.config);
22582
- if (fs24.existsSync(mcpSourcePath)) {
22707
+ if (fs26.existsSync(mcpSourcePath)) {
22583
22708
  artifacts.push({
22584
22709
  component: "cline mcp",
22585
22710
  path: clinePaths.mcpFile,
@@ -22603,8 +22728,9 @@ function resolveKitManagedArtifacts(runtime, scope, selectedTools, manifest, cla
22603
22728
  return dedupeArtifacts(artifacts);
22604
22729
  }
22605
22730
  function addRecordedManifestPlans(artifacts, manifest, selectedTools, runtime) {
22731
+ const fullUninstall = isFullToolUninstall(manifest, selectedTools);
22606
22732
  for (const file of manifest.artifacts) {
22607
- if (file.origin !== "tangyr-managed" || !shouldRemoveManifestFile(file.relativePath, selectedTools)) {
22733
+ if (file.origin !== "tangyr-managed" || !shouldRemoveManifestFile(file.relativePath, selectedTools, fullUninstall)) {
22608
22734
  continue;
22609
22735
  }
22610
22736
  const artifactPath = resolveManifestKeyToPath(
@@ -22661,12 +22787,12 @@ function isKitManagedArtifact(artifact, runtime) {
22661
22787
  return artifactClass === "managed-symlink" || artifactClass === "managed-stale" || artifactClass === "managed-compiled";
22662
22788
  }
22663
22789
  if (artifact.kind === "compiled") {
22664
- if (!fs24.existsSync(artifact.path)) {
22790
+ if (!fs26.existsSync(artifact.path)) {
22665
22791
  return false;
22666
22792
  }
22667
22793
  try {
22668
22794
  const marker = extractProvenanceMarker(
22669
- fs24.readFileSync(artifact.path, "utf8")
22795
+ fs26.readFileSync(artifact.path, "utf8")
22670
22796
  );
22671
22797
  return marker?.target === artifact.expectedTarget && marker?.source === artifact.expectedSource;
22672
22798
  } catch {
@@ -22674,7 +22800,7 @@ function isKitManagedArtifact(artifact, runtime) {
22674
22800
  }
22675
22801
  }
22676
22802
  if (artifact.kind === "recorded") {
22677
- return fs24.existsSync(artifact.path) || isSymlink(artifact.path);
22803
+ return fs26.existsSync(artifact.path) || isSymlink(artifact.path);
22678
22804
  }
22679
22805
  if (artifact.kind === "kit-pointer") {
22680
22806
  return isKitPointer(artifact);
@@ -22683,11 +22809,11 @@ function isKitManagedArtifact(artifact, runtime) {
22683
22809
  return isLegacyOperatingGuideCopy(artifact.path);
22684
22810
  }
22685
22811
  if (artifact.kind === "codex-mcp") {
22686
- if (!fs24.existsSync(artifact.path)) {
22812
+ if (!fs26.existsSync(artifact.path)) {
22687
22813
  return false;
22688
22814
  }
22689
22815
  const section = readManagedTomlSection(
22690
- fs24.readFileSync(artifact.path, "utf8")
22816
+ fs26.readFileSync(artifact.path, "utf8")
22691
22817
  );
22692
22818
  const marker = section ? extractProvenanceMarker(section) : null;
22693
22819
  return marker?.target === artifact.expectedTarget && marker?.source === artifact.expectedSource;
@@ -22713,16 +22839,16 @@ function isKitManagedArtifact(artifact, runtime) {
22713
22839
  return hasManagedCodexHooks(parsed, artifact.expectedSource);
22714
22840
  }
22715
22841
  function removeManagedCompiledArtifact(artifact, logger) {
22716
- if (!fs24.existsSync(artifact.path)) {
22842
+ if (!fs26.existsSync(artifact.path)) {
22717
22843
  return false;
22718
22844
  }
22719
22845
  const marker = extractProvenanceMarker(
22720
- fs24.readFileSync(artifact.path, "utf8")
22846
+ fs26.readFileSync(artifact.path, "utf8")
22721
22847
  );
22722
22848
  if (marker?.target !== artifact.expectedTarget || marker?.source !== artifact.expectedSource) {
22723
22849
  return false;
22724
22850
  }
22725
- fs24.rmSync(artifact.path, { force: true });
22851
+ fs26.rmSync(artifact.path, { force: true });
22726
22852
  logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
22727
22853
  return true;
22728
22854
  }
@@ -22793,12 +22919,12 @@ function removeManagedJsonArtifact(artifact, logger) {
22793
22919
  if (!parsed || !hasManagedRootMetadata(parsed, artifact.expectedTarget)) {
22794
22920
  return false;
22795
22921
  }
22796
- fs24.rmSync(artifact.path, { force: true });
22922
+ fs26.rmSync(artifact.path, { force: true });
22797
22923
  logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
22798
22924
  return true;
22799
22925
  }
22800
22926
  function removeRecordedArtifact(artifact, logger) {
22801
- if (!fs24.existsSync(artifact.path) && !isSymlink(artifact.path)) {
22927
+ if (!fs26.existsSync(artifact.path) && !isSymlink(artifact.path)) {
22802
22928
  return false;
22803
22929
  }
22804
22930
  removeManagedPath(artifact.path);
@@ -22806,10 +22932,10 @@ function removeRecordedArtifact(artifact, logger) {
22806
22932
  return true;
22807
22933
  }
22808
22934
  function removeExactManagedFile(artifact, logger) {
22809
- if (!fs24.existsSync(artifact.path)) {
22935
+ if (!fs26.existsSync(artifact.path)) {
22810
22936
  return false;
22811
22937
  }
22812
- fs24.rmSync(artifact.path, { force: true });
22938
+ fs26.rmSync(artifact.path, { force: true });
22813
22939
  logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
22814
22940
  return true;
22815
22941
  }
@@ -22843,10 +22969,10 @@ function removeManagedCodexHooksArtifact(artifact, logger) {
22843
22969
  return true;
22844
22970
  }
22845
22971
  function removeManagedCodexMcpArtifact(artifact, logger) {
22846
- if (!fs24.existsSync(artifact.path)) {
22972
+ if (!fs26.existsSync(artifact.path)) {
22847
22973
  return false;
22848
22974
  }
22849
- const content = fs24.readFileSync(artifact.path, "utf8");
22975
+ const content = fs26.readFileSync(artifact.path, "utf8");
22850
22976
  const managedSection = readManagedTomlSection(content);
22851
22977
  if (!managedSection) {
22852
22978
  return false;
@@ -22861,9 +22987,9 @@ function removeManagedCodexMcpArtifact(artifact, logger) {
22861
22987
  codexManagedMcpEnd
22862
22988
  );
22863
22989
  if (cleaned.trim().length === 0) {
22864
- fs24.rmSync(artifact.path, { force: true });
22990
+ fs26.rmSync(artifact.path, { force: true });
22865
22991
  } else {
22866
- fs24.writeFileSync(artifact.path, cleaned);
22992
+ fs26.writeFileSync(artifact.path, cleaned);
22867
22993
  }
22868
22994
  logger.verbose(` removed ${artifact.component}: ${artifact.path}`);
22869
22995
  return true;
@@ -22886,12 +23012,26 @@ function resolveCopilotPaths(runtime, scope = "global", projectRoot) {
22886
23012
  prompts: profile.prompts,
22887
23013
  skills: profile.skills,
22888
23014
  hooks: profile.hooks,
22889
- mcpSharedFile: profile.mcp_shared_file
23015
+ mcpSharedFile: profile.mcp_shared_file,
23016
+ instructions: profile.instructions
22890
23017
  };
22891
23018
  } catch {
22892
23019
  }
22893
23020
  }
23021
+ if (scope === "project") {
23022
+ const root = projectRoot ?? process.cwd();
23023
+ return {
23024
+ instructions: path23.join(root, ".github", "copilot-instructions.md"),
23025
+ agents: path23.join(root, ".github", "agents"),
23026
+ prompts: path23.join(root, ".github", "prompts"),
23027
+ skills: path23.join(root, ".github", "skills"),
23028
+ hooks: path23.join(root, ".github", "hooks"),
23029
+ mcpSharedFile: path23.join(root, ".vscode", "mcp.json")
23030
+ };
23031
+ }
22894
23032
  return {
23033
+ // null, because Copilot's instructions surface exists only in a project.
23034
+ instructions: null,
22895
23035
  agents: resolveTargetPath("$VSCODE_USER_DATA/agents"),
22896
23036
  prompts: resolveTargetPath("$VSCODE_USER_DATA/prompts"),
22897
23037
  skills: resolveTargetPath("~/.copilot/skills"),
@@ -22923,6 +23063,17 @@ function resolveCodexPaths(runtime, scope = "global", projectRoot) {
22923
23063
  } catch {
22924
23064
  }
22925
23065
  }
23066
+ if (scope === "project") {
23067
+ const root = projectRoot ?? process.cwd();
23068
+ return {
23069
+ instructions: path23.join(root, "AGENTS.md"),
23070
+ agents: path23.join(root, ".codex", "agents"),
23071
+ rules: path23.join(root, ".codex", "rules"),
23072
+ skills: path23.join(root, ".codex", "skills"),
23073
+ hooksSharedFile: path23.join(root, ".codex", "hooks.json"),
23074
+ mcpSharedFile: path23.join(root, ".codex", "config.toml")
23075
+ };
23076
+ }
22926
23077
  return {
22927
23078
  instructions: resolveTargetPath("~/.codex/AGENTS.md"),
22928
23079
  agents: resolveTargetPath("~/.codex/agents"),
@@ -22932,6 +23083,27 @@ function resolveCodexPaths(runtime, scope = "global", projectRoot) {
22932
23083
  mcpSharedFile: resolveTargetPath("~/.codex/config.toml")
22933
23084
  };
22934
23085
  }
23086
+ function resolveSharedHookScriptsPath(runtime, scope, projectRoot) {
23087
+ if (runtime) {
23088
+ try {
23089
+ const mappings = loadMappings(
23090
+ runtime.sourceRoot,
23091
+ runtime.config,
23092
+ runtime.configDir
23093
+ );
23094
+ return resolveTargetPaths({
23095
+ scope,
23096
+ projectRoot,
23097
+ mappings: mappings.platformPaths
23098
+ }).codex.hook_scripts;
23099
+ } catch {
23100
+ }
23101
+ }
23102
+ if (scope === "project") {
23103
+ return path23.join(projectRoot ?? process.cwd(), ".agents", "hooks");
23104
+ }
23105
+ return resolveTargetPath("~/.agents/hooks");
23106
+ }
22935
23107
  function resolveOpenCodePaths(runtime, scope = "global", projectRoot) {
22936
23108
  if (runtime) {
22937
23109
  try {
@@ -23056,11 +23228,11 @@ function managedArtifactPlanPriority(artifact) {
23056
23228
  return 2;
23057
23229
  }
23058
23230
  function readJsonObject2(filePath) {
23059
- if (!fs24.existsSync(filePath)) {
23231
+ if (!fs26.existsSync(filePath)) {
23060
23232
  return null;
23061
23233
  }
23062
23234
  try {
23063
- const parsed = JSON.parse(fs24.readFileSync(filePath, "utf8"));
23235
+ const parsed = JSON.parse(fs26.readFileSync(filePath, "utf8"));
23064
23236
  return isRecord(parsed) ? parsed : null;
23065
23237
  } catch {
23066
23238
  return null;
@@ -23108,22 +23280,22 @@ function removeLegacyClaudeSharedPermission(value) {
23108
23280
  return Object.keys(cleaned).length === 0 ? void 0 : cleaned;
23109
23281
  }
23110
23282
  function isKitPointer(artifact) {
23111
- if (!artifact.expectedPath || !fs24.existsSync(artifact.path)) {
23283
+ if (!artifact.expectedPath || !fs26.existsSync(artifact.path)) {
23112
23284
  return false;
23113
23285
  }
23114
23286
  try {
23115
- const recordedPath = fs24.readFileSync(artifact.path, "utf8").trim();
23116
- return recordedPath.length > 0 && path21.resolve(recordedPath) === path21.resolve(artifact.expectedPath);
23287
+ const recordedPath = fs26.readFileSync(artifact.path, "utf8").trim();
23288
+ return recordedPath.length > 0 && path23.resolve(recordedPath) === path23.resolve(artifact.expectedPath);
23117
23289
  } catch {
23118
23290
  return false;
23119
23291
  }
23120
23292
  }
23121
23293
  function isLegacyOperatingGuideCopy(filePath) {
23122
- if (!fs24.existsSync(filePath)) {
23294
+ if (!fs26.existsSync(filePath)) {
23123
23295
  return false;
23124
23296
  }
23125
23297
  try {
23126
- return fs24.readFileSync(filePath, "utf8").startsWith(legacyOperatingGuideSignature);
23298
+ return fs26.readFileSync(filePath, "utf8").startsWith(legacyOperatingGuideSignature);
23127
23299
  } catch {
23128
23300
  return false;
23129
23301
  }
@@ -23145,10 +23317,10 @@ function matchesManagedMetadata(metadata, artifact) {
23145
23317
  }
23146
23318
  function writeJsonOrRemove(filePath, parsed) {
23147
23319
  if (isEmptyJsonValue(parsed)) {
23148
- fs24.rmSync(filePath, { force: true });
23320
+ fs26.rmSync(filePath, { force: true });
23149
23321
  return;
23150
23322
  }
23151
- fs24.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
23323
+ fs26.writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}
23152
23324
  `);
23153
23325
  }
23154
23326
  function isEmptyJsonValue(value) {
@@ -23274,20 +23446,20 @@ function removeDelimitedManagedSection(content, startMarker, endMarker) {
23274
23446
  return `${content.slice(0, start)}${content.slice(removalEnd)}`;
23275
23447
  }
23276
23448
  function restoreBackup(backup, logger) {
23277
- if (!fs24.existsSync(backup.backupLocation)) {
23449
+ if (!fs26.existsSync(backup.backupLocation)) {
23278
23450
  logger.warn(` backup not found: ${backup.backupLocation}`);
23279
23451
  return;
23280
23452
  }
23281
- const destDir = path21.dirname(backup.originalPath);
23282
- fs24.mkdirSync(destDir, { recursive: true });
23283
- fs24.renameSync(backup.backupLocation, backup.originalPath);
23453
+ const destDir = path23.dirname(backup.originalPath);
23454
+ fs26.mkdirSync(destDir, { recursive: true });
23455
+ fs26.renameSync(backup.backupLocation, backup.originalPath);
23284
23456
  logger.verbose(
23285
23457
  ` restored: ${backup.backupLocation} \u2192 ${backup.originalPath}`
23286
23458
  );
23287
23459
  }
23288
23460
  function isSymlink(p) {
23289
23461
  try {
23290
- return fs24.lstatSync(p).isSymbolicLink();
23462
+ return fs26.lstatSync(p).isSymbolicLink();
23291
23463
  } catch {
23292
23464
  return false;
23293
23465
  }
@@ -23361,7 +23533,7 @@ function cleanupClaudeCode(config, sourceRoot, configDir, logger, scope) {
23361
23533
  )) {
23362
23534
  const artifactClass = classifyArtifact(artifact.path, config, sourceRoot);
23363
23535
  if (artifactClass === "managed-symlink" || artifactClass === "managed-compiled" || artifactClass === "managed-stale") {
23364
- fs25.rmSync(artifact.path, { recursive: true, force: true });
23536
+ fs27.rmSync(artifact.path, { recursive: true, force: true });
23365
23537
  logger.info(`${artifact.component}: removed ${artifact.path}`);
23366
23538
  }
23367
23539
  }
@@ -23417,7 +23589,7 @@ function cleanupCopilot(config, sourceRoot, configDir, logger) {
23417
23589
  logger
23418
23590
  );
23419
23591
  removeManagedJsonFile(
23420
- path22.join(resolveTargetPath(paths.hooks ?? ""), "tangyr-managed.json"),
23592
+ path24.join(resolveTargetPath(paths.hooks ?? ""), "tangyr-managed.json"),
23421
23593
  "_tangyr",
23422
23594
  logger
23423
23595
  );
@@ -23574,40 +23746,40 @@ function removeManagedValueByKey2(value, keyName, keyValue) {
23574
23746
  function removeManagedDirectPath(artifactPath, config, sourceRoot, component, logger) {
23575
23747
  const artifactClass = classifyArtifact(artifactPath, config, sourceRoot);
23576
23748
  if (artifactClass === "managed-symlink" || artifactClass === "managed-compiled" || artifactClass === "managed-stale") {
23577
- fs25.rmSync(artifactPath, { recursive: true, force: true });
23749
+ fs27.rmSync(artifactPath, { recursive: true, force: true });
23578
23750
  logger.info(`${component}: removed ${artifactPath}`);
23579
23751
  }
23580
23752
  }
23581
23753
  function removeManagedCompiledFiles(dir, pattern, expectedTarget, logger) {
23582
- for (const filePath of ts(path22.join(dir, pattern)).sort()) {
23754
+ for (const filePath of ts(path24.join(dir, pattern)).sort()) {
23583
23755
  removeManagedCompiledFile(filePath, expectedTarget, logger);
23584
23756
  }
23585
23757
  }
23586
23758
  function removeManagedCompiledFile(filePath, expectedTarget, logger) {
23587
- if (!fs25.existsSync(filePath)) {
23759
+ if (!fs27.existsSync(filePath)) {
23588
23760
  return;
23589
23761
  }
23590
- const marker = extractProvenanceMarker(fs25.readFileSync(filePath, "utf8"));
23762
+ const marker = extractProvenanceMarker(fs27.readFileSync(filePath, "utf8"));
23591
23763
  if (marker?.target === expectedTarget) {
23592
- fs25.rmSync(filePath, { force: true });
23764
+ fs27.rmSync(filePath, { force: true });
23593
23765
  logger.info(`removed ${filePath}`);
23594
23766
  }
23595
23767
  }
23596
23768
  function removeManagedJsonFile(filePath, markerKey, logger) {
23597
- if (!fs25.existsSync(filePath)) {
23769
+ if (!fs27.existsSync(filePath)) {
23598
23770
  return;
23599
23771
  }
23600
23772
  const parsed = readJsonObject(filePath);
23601
23773
  if (isRecord(parsed[markerKey])) {
23602
- fs25.rmSync(filePath, { force: true });
23774
+ fs27.rmSync(filePath, { force: true });
23603
23775
  logger.info(`removed ${filePath}`);
23604
23776
  }
23605
23777
  }
23606
23778
  function removeCodexManagedTomlSection(filePath, logger) {
23607
- if (!fs25.existsSync(filePath)) {
23779
+ if (!fs27.existsSync(filePath)) {
23608
23780
  return;
23609
23781
  }
23610
- const content = fs25.readFileSync(filePath, "utf8");
23782
+ const content = fs27.readFileSync(filePath, "utf8");
23611
23783
  const next = removeDelimitedManagedSection2(
23612
23784
  content,
23613
23785
  codexManagedMcpStart2,
@@ -23616,7 +23788,7 @@ function removeCodexManagedTomlSection(filePath, logger) {
23616
23788
  if (next === content) {
23617
23789
  return;
23618
23790
  }
23619
- fs25.writeFileSync(filePath, next);
23791
+ fs27.writeFileSync(filePath, next);
23620
23792
  logger.info(`mcp: removed managed section from ${filePath}`);
23621
23793
  }
23622
23794
  function removeDelimitedManagedSection2(content, startMarker, endMarker) {
@@ -23688,9 +23860,9 @@ function toVerbosity(value) {
23688
23860
  }
23689
23861
 
23690
23862
  // src/commands/detect.ts
23691
- import fs26 from "fs";
23863
+ import fs28 from "fs";
23692
23864
  import os5 from "os";
23693
- import path23 from "path";
23865
+ import path25 from "path";
23694
23866
  import { spawnSync } from "child_process";
23695
23867
  var copilotEditorCommands = [
23696
23868
  "code",
@@ -23729,7 +23901,7 @@ var bundledCursorExecutablePathsByPlatform = {
23729
23901
  };
23730
23902
  function detectTargets(logger, dependencies = {}) {
23731
23903
  const spawnSyncImpl = dependencies.spawnSyncImpl ?? defaultSpawnSync;
23732
- const pathExists = dependencies.pathExists ?? fs26.existsSync;
23904
+ const pathExists = dependencies.pathExists ?? fs28.existsSync;
23733
23905
  const homeDirectory = dependencies.homeDirectory ?? os5.homedir();
23734
23906
  const platform = dependencies.platform ?? process.platform;
23735
23907
  const locator = platform === "win32" ? "where.exe" : "which";
@@ -23764,8 +23936,8 @@ function detectsCopilot(logger, dependencies = {}) {
23764
23936
  return detectCopilot(logger, dependencies);
23765
23937
  }
23766
23938
  function detectCopilot(logger, dependencies = {}) {
23767
- const pathExists = dependencies.pathExists ?? fs26.existsSync;
23768
- const readFile = dependencies.readFile ?? ((filePath) => fs26.readFileSync(filePath, "utf8"));
23939
+ const pathExists = dependencies.pathExists ?? fs28.existsSync;
23940
+ const readFile = dependencies.readFile ?? ((filePath) => fs28.readFileSync(filePath, "utf8"));
23769
23941
  const platform = dependencies.platform ?? process.platform;
23770
23942
  if (detectEditorExtension(
23771
23943
  "Copilot",
@@ -23793,8 +23965,8 @@ function detectCopilot(logger, dependencies = {}) {
23793
23965
  }
23794
23966
  function detectEditorExtension(label, matchesExtension, logger, dependencies = {}) {
23795
23967
  const spawnSyncImpl = dependencies.spawnSyncImpl ?? defaultSpawnSync;
23796
- const pathExists = dependencies.pathExists ?? fs26.existsSync;
23797
- const readDirectory = dependencies.readDirectory ?? fs26.readdirSync;
23968
+ const pathExists = dependencies.pathExists ?? fs28.existsSync;
23969
+ const readDirectory = dependencies.readDirectory ?? fs28.readdirSync;
23798
23970
  const homeDirectory = dependencies.homeDirectory ?? os5.homedir();
23799
23971
  const platform = dependencies.platform ?? process.platform;
23800
23972
  const commandCandidates = [
@@ -23822,7 +23994,7 @@ function detectEditorExtension(label, matchesExtension, logger, dependencies = {
23822
23994
  }
23823
23995
  }
23824
23996
  for (const relativeDirectory of copilotExtensionDirectories) {
23825
- const extensionDirectory = path23.join(homeDirectory, relativeDirectory);
23997
+ const extensionDirectory = path25.join(homeDirectory, relativeDirectory);
23826
23998
  if (!pathExists(extensionDirectory)) {
23827
23999
  continue;
23828
24000
  }
@@ -23845,7 +24017,7 @@ function cursorExecutablePaths(platform, homeDirectory) {
23845
24017
  }
23846
24018
  return [
23847
24019
  ...systemPaths,
23848
- path23.join(
24020
+ path25.join(
23849
24021
  homeDirectory,
23850
24022
  "Applications/Cursor.app/Contents/Resources/app/bin/cursor"
23851
24023
  )
@@ -23905,86 +24077,25 @@ function logDetectionError(attemptedCommand, error, logger) {
23905
24077
  }
23906
24078
 
23907
24079
  // src/commands/doctor.ts
23908
- import fs28 from "fs";
23909
- import path25 from "path";
23910
-
23911
- // src/utils/fs.ts
23912
- import fs27 from "fs";
23913
- import path24 from "path";
23914
- function ensureDir(dirPath) {
23915
- fs27.mkdirSync(dirPath, { recursive: true });
23916
- }
23917
- function createRelativeSymlink(source, destination, type, options) {
23918
- const absoluteSource = path24.resolve(source);
23919
- const relative = path24.relative(
23920
- realPathOrInput(path24.dirname(destination)),
23921
- absoluteSource
23922
- );
23923
- if (process.platform === "win32" && type === "dir") {
23924
- fs27.symlinkSync(absoluteSource, destination, "junction");
23925
- return;
23926
- }
23927
- if (process.platform === "win32" && type === "file") {
23928
- try {
23929
- fs27.symlinkSync(relative, destination, "file");
23930
- } catch (error) {
23931
- if (!hasErrorCode(error, "EPERM")) {
23932
- throw error;
23933
- }
23934
- if (!options?.provenanceMarker) {
23935
- throw new Error(
23936
- `${destination}: file symlink unavailable on Windows and no provenance marker was provided.`
23937
- );
23938
- }
23939
- createManagedCopy(
23940
- source,
23941
- destination,
23942
- options.provenanceMarker,
23943
- options.logger
23944
- );
23945
- options.logger?.warn(
23946
- `${destination}: file symlink unavailable on Windows \u2014 using managed copy with provenance metadata (behavioral contract C-09)`
23947
- );
23948
- }
23949
- return;
23950
- }
23951
- fs27.symlinkSync(relative, destination, type);
23952
- }
23953
- function createManagedCopy(source, destination, provenanceMarker, _logger) {
23954
- ensureDir(path24.dirname(destination));
23955
- const content = fs27.readFileSync(source, "utf8");
23956
- fs27.writeFileSync(destination, `${provenanceMarker}
23957
- ${content}`);
23958
- }
23959
- function hasErrorCode(error, expectedCode) {
23960
- return typeof error === "object" && error !== null && "code" in error && error.code === expectedCode;
23961
- }
23962
- function realPathOrInput(filePath) {
23963
- try {
23964
- return fs27.realpathSync(filePath);
23965
- } catch {
23966
- return filePath;
23967
- }
23968
- }
23969
-
23970
- // src/commands/doctor.ts
24080
+ import fs29 from "fs";
24081
+ import path26 from "path";
23971
24082
  function runDoctorCommand(options, logger) {
23972
24083
  const { config, configDir, configPath, sourceRoot } = resolveRuntime(
23973
24084
  options,
23974
24085
  import.meta.url
23975
24086
  );
23976
24087
  const findings = [];
23977
- if (!fs28.existsSync(configPath)) {
24088
+ if (!fs29.existsSync(configPath)) {
23978
24089
  findings.push(`${configPath}: missing`);
23979
24090
  }
23980
- const hooksJsonPath = path25.join(
24091
+ const hooksJsonPath = path26.join(
23981
24092
  sourceRoot,
23982
24093
  config.source.hooks,
23983
24094
  "hooks.json"
23984
24095
  );
23985
- if (fs28.existsSync(hooksJsonPath)) {
24096
+ if (fs29.existsSync(hooksJsonPath)) {
23986
24097
  try {
23987
- JSON.parse(fs28.readFileSync(hooksJsonPath, "utf8"));
24098
+ JSON.parse(fs29.readFileSync(hooksJsonPath, "utf8"));
23988
24099
  } catch (err) {
23989
24100
  findings.push(
23990
24101
  `hooks/hooks.json: ${err instanceof Error ? err.message : String(err)}`
@@ -24049,19 +24160,19 @@ function checkClaudeCode(options, findings, config, sourceRoot, configDir, logge
24049
24160
  projectRoot
24050
24161
  )) {
24051
24162
  const artifactClass = classifyArtifact(artifact.path, config, sourceRoot);
24052
- const sourcePath = artifact.sourceKey ? path25.resolve(sourceRoot, config.source[artifact.sourceKey]) : void 0;
24163
+ const sourcePath = artifact.sourceKey ? path26.resolve(sourceRoot, config.source[artifact.sourceKey]) : void 0;
24053
24164
  if (artifactClass === "managed-stale") {
24054
24165
  findings.push(
24055
24166
  `${artifact.component}: managed stale artifact at ${artifact.path}`
24056
24167
  );
24057
24168
  if (options.fix && sourcePath && isOurStaleSymlink(
24058
24169
  artifact.path,
24059
- /* @__PURE__ */ new Set([path25.resolve(artifact.path)]),
24170
+ /* @__PURE__ */ new Set([path26.resolve(artifact.path)]),
24060
24171
  config
24061
24172
  )) {
24062
- fs28.unlinkSync(artifact.path);
24063
- ensureDir(path25.dirname(artifact.path));
24064
- const symlinkType = fs28.statSync(sourcePath).isDirectory() ? "dir" : "file";
24173
+ fs29.unlinkSync(artifact.path);
24174
+ ensureDir(path26.dirname(artifact.path));
24175
+ const symlinkType = fs29.statSync(sourcePath).isDirectory() ? "dir" : "file";
24065
24176
  createRelativeSymlink(sourcePath, artifact.path, symlinkType, {
24066
24177
  logger,
24067
24178
  provenanceMarker: symlinkType === "file" && artifact.sourceKey ? formatProvenanceMarker(
@@ -24074,8 +24185,8 @@ function checkClaudeCode(options, findings, config, sourceRoot, configDir, logge
24074
24185
  logger.info(`${artifact.component}: recreated managed symlink`);
24075
24186
  }
24076
24187
  }
24077
- if (options.fix && !fs28.existsSync(path25.dirname(artifact.path))) {
24078
- ensureDir(path25.dirname(artifact.path));
24188
+ if (options.fix && !fs29.existsSync(path26.dirname(artifact.path))) {
24189
+ ensureDir(path26.dirname(artifact.path));
24079
24190
  }
24080
24191
  checkParentWritablePath(artifact.path, artifact.component, findings);
24081
24192
  }
@@ -24090,7 +24201,7 @@ function checkCopilot(config, sourceRoot, configDir, findings) {
24090
24201
  continue;
24091
24202
  }
24092
24203
  const label = `copilot: ${rawPath}`;
24093
- if (key.endsWith("_shared_file") || path25.extname(rawPath).length > 0) {
24204
+ if (key.endsWith("_shared_file") || path26.extname(rawPath).length > 0) {
24094
24205
  checkParentWritablePath(resolveTargetPath(rawPath), label, findings);
24095
24206
  continue;
24096
24207
  }
@@ -24110,7 +24221,7 @@ function checkCodex(config, sourceRoot, configDir, findings, logger) {
24110
24221
  continue;
24111
24222
  }
24112
24223
  const label = `codex: ${rawPath}`;
24113
- if (key.endsWith("_shared_file") || path25.extname(rawPath).length > 0) {
24224
+ if (key.endsWith("_shared_file") || path26.extname(rawPath).length > 0) {
24114
24225
  checkParentWritablePath(resolveTargetPath(rawPath), label, findings);
24115
24226
  continue;
24116
24227
  }
@@ -24127,33 +24238,33 @@ function resolveHooksScriptPath(config, sourceRoot, configDir) {
24127
24238
  scope: "global",
24128
24239
  mappings: mappings.platformPaths
24129
24240
  });
24130
- return path25.join(profile["claude-code"].hooks, "scripts");
24241
+ return path26.join(profile["claude-code"].hooks, "scripts");
24131
24242
  } catch {
24132
24243
  return resolveTargetPath("~/.agents/hooks/scripts");
24133
24244
  }
24134
24245
  }
24135
24246
  function checkParentWritablePath(targetPath, label, findings) {
24136
- checkWritablePath(path25.dirname(targetPath), `${label} parent`, findings);
24247
+ checkWritablePath(path26.dirname(targetPath), `${label} parent`, findings);
24137
24248
  }
24138
24249
  function checkWritablePath(checkPath, label, findings) {
24139
- if (!fs28.existsSync(checkPath)) {
24250
+ if (!fs29.existsSync(checkPath)) {
24140
24251
  findings.push(`${label}: path does not exist`);
24141
24252
  return;
24142
24253
  }
24143
24254
  try {
24144
- fs28.accessSync(checkPath, fs28.constants.R_OK | fs28.constants.W_OK);
24255
+ fs29.accessSync(checkPath, fs29.constants.R_OK | fs29.constants.W_OK);
24145
24256
  } catch {
24146
24257
  findings.push(`${label}: path is not readable and writable`);
24147
24258
  }
24148
24259
  }
24149
24260
  function walkHookScripts(dir) {
24150
- if (!fs28.existsSync(dir)) {
24261
+ if (!fs29.existsSync(dir)) {
24151
24262
  return [];
24152
24263
  }
24153
- return fs28.readdirSync(dir).map((entry) => path25.join(dir, entry)).filter((entryPath) => fs28.statSync(entryPath).isFile());
24264
+ return fs29.readdirSync(dir).map((entry) => path26.join(dir, entry)).filter((entryPath) => fs29.statSync(entryPath).isFile());
24154
24265
  }
24155
24266
  function fixManagedHookScriptModes(scriptRoot, sourceRoot, logger) {
24156
- if (!fs28.existsSync(scriptRoot)) {
24267
+ if (!fs29.existsSync(scriptRoot)) {
24157
24268
  return;
24158
24269
  }
24159
24270
  if (hasSymlinkAncestor(scriptRoot, resolveTargetPath("~/.agents")) || isInside(realPathOrInput2(scriptRoot), realPathOrInput2(sourceRoot))) {
@@ -24163,49 +24274,49 @@ function fixManagedHookScriptModes(scriptRoot, sourceRoot, logger) {
24163
24274
  return;
24164
24275
  }
24165
24276
  for (const scriptPath of walkHookScripts(scriptRoot)) {
24166
- fs28.chmodSync(scriptPath, 493);
24277
+ fs29.chmodSync(scriptPath, 493);
24167
24278
  }
24168
24279
  }
24169
24280
  function hasSymlinkAncestor(targetPath, stopAt) {
24170
- const relative = path25.relative(
24171
- path25.resolve(stopAt),
24172
- path25.resolve(targetPath)
24281
+ const relative = path26.relative(
24282
+ path26.resolve(stopAt),
24283
+ path26.resolve(targetPath)
24173
24284
  );
24174
- if (relative.startsWith("..") || path25.isAbsolute(relative)) {
24285
+ if (relative.startsWith("..") || path26.isAbsolute(relative)) {
24175
24286
  return false;
24176
24287
  }
24177
- return relative.split(path25.sep).some((_segment, index, segments) => {
24178
- const candidate = path25.join(
24179
- path25.resolve(stopAt),
24288
+ return relative.split(path26.sep).some((_segment, index, segments) => {
24289
+ const candidate = path26.join(
24290
+ path26.resolve(stopAt),
24180
24291
  ...segments.slice(0, index + 1)
24181
24292
  );
24182
24293
  try {
24183
- return fs28.lstatSync(candidate).isSymbolicLink();
24294
+ return fs29.lstatSync(candidate).isSymbolicLink();
24184
24295
  } catch {
24185
24296
  return false;
24186
24297
  }
24187
24298
  });
24188
24299
  }
24189
24300
  function isInside(childPath, parentPath) {
24190
- const relative = path25.relative(parentPath, childPath);
24191
- return relative.length === 0 || !relative.startsWith("..") && !path25.isAbsolute(relative);
24301
+ const relative = path26.relative(parentPath, childPath);
24302
+ return relative.length === 0 || !relative.startsWith("..") && !path26.isAbsolute(relative);
24192
24303
  }
24193
24304
  function realPathOrInput2(filePath) {
24194
24305
  try {
24195
- return fs28.realpathSync(filePath);
24306
+ return fs29.realpathSync(filePath);
24196
24307
  } catch {
24197
24308
  return filePath;
24198
24309
  }
24199
24310
  }
24200
24311
 
24201
24312
  // src/commands/init.ts
24202
- import fs29 from "fs";
24203
- import path26 from "path";
24313
+ import fs30 from "fs";
24314
+ import path27 from "path";
24204
24315
  var import_yaml7 = __toESM(require_dist(), 1);
24205
24316
  async function runInitCommand(_options, logger) {
24206
24317
  const sourceRoot = await dist_default6({
24207
24318
  message: "Canonical Tangyr kit root",
24208
- default: path26.resolve("projects/tangyr/agent-coding")
24319
+ default: path27.resolve("projects/tangyr/agent-coding")
24209
24320
  });
24210
24321
  const componentCount = [
24211
24322
  "CLAUDE.md",
@@ -24215,7 +24326,7 @@ async function runInitCommand(_options, logger) {
24215
24326
  "rules",
24216
24327
  "hooks"
24217
24328
  ].filter(
24218
- (relativePath) => fs29.existsSync(path26.join(sourceRoot, relativePath))
24329
+ (relativePath) => fs30.existsSync(path27.join(sourceRoot, relativePath))
24219
24330
  ).length;
24220
24331
  const detectedTargets = detectTargets(logger);
24221
24332
  const enabledTargets = await dist_default4({
@@ -24235,8 +24346,8 @@ async function runInitCommand(_options, logger) {
24235
24346
  value
24236
24347
  }))
24237
24348
  });
24238
- const configPath = path26.join(sourceRoot, "tangyr.config.yaml");
24239
- if (fs29.existsSync(configPath)) {
24349
+ const configPath = path27.join(sourceRoot, "tangyr.config.yaml");
24350
+ if (fs30.existsSync(configPath)) {
24240
24351
  const overwrite = await dist_default5({
24241
24352
  message: `${configPath} exists. Overwrite?`,
24242
24353
  default: false
@@ -24246,7 +24357,7 @@ async function runInitCommand(_options, logger) {
24246
24357
  return;
24247
24358
  }
24248
24359
  }
24249
- fs29.writeFileSync(
24360
+ fs30.writeFileSync(
24250
24361
  configPath,
24251
24362
  (0, import_yaml7.stringify)({
24252
24363
  schema: 1,
@@ -24293,11 +24404,11 @@ import fs39 from "fs";
24293
24404
  import path39 from "path";
24294
24405
 
24295
24406
  // src/core/channel-state.ts
24296
- import fs30 from "fs";
24297
- import path27 from "path";
24407
+ import fs31 from "fs";
24408
+ import path28 from "path";
24298
24409
  var CHANNEL_STATE_FILENAME = ".tangyr-channels.json";
24299
24410
  function stateFilePath(cacheRoot) {
24300
- return path27.join(cacheRoot, CHANNEL_STATE_FILENAME);
24411
+ return path28.join(cacheRoot, CHANNEL_STATE_FILENAME);
24301
24412
  }
24302
24413
  function channelKey(kit, channel) {
24303
24414
  return `${kit}/${channel}`;
@@ -24306,7 +24417,7 @@ function readState(cacheRoot) {
24306
24417
  const filePath = stateFilePath(cacheRoot);
24307
24418
  let raw;
24308
24419
  try {
24309
- raw = fs30.readFileSync(filePath, "utf8");
24420
+ raw = fs31.readFileSync(filePath, "utf8");
24310
24421
  } catch (err) {
24311
24422
  if (err.code === "ENOENT") {
24312
24423
  return { state: {} };
@@ -24353,13 +24464,13 @@ function writeChannelSequence(params) {
24353
24464
  [key]: { sequence, version, seenAt: now.toISOString() }
24354
24465
  };
24355
24466
  const filePath = stateFilePath(cacheRoot);
24356
- fs30.mkdirSync(path27.dirname(filePath), { recursive: true, mode: 448 });
24467
+ fs31.mkdirSync(path28.dirname(filePath), { recursive: true, mode: 448 });
24357
24468
  const tempPath = `${filePath}.${process.pid}.tmp`;
24358
- fs30.writeFileSync(tempPath, `${JSON.stringify(state, null, 2)}
24469
+ fs31.writeFileSync(tempPath, `${JSON.stringify(state, null, 2)}
24359
24470
  `, {
24360
24471
  mode: 384
24361
24472
  });
24362
- fs30.renameSync(tempPath, filePath);
24473
+ fs31.renameSync(tempPath, filePath);
24363
24474
  }
24364
24475
  function assertChannelNotRolledBack(params) {
24365
24476
  const { originUrl, kit, channel, seen, seenVersion, incoming } = params;
@@ -24375,8 +24486,8 @@ var CHANNEL_POINTER_TYPE = "tangyr.channel-pointer.v1";
24375
24486
  var DEFAULT_CHANNEL = "stable";
24376
24487
 
24377
24488
  // src/core/materialize.ts
24378
- import fs31 from "fs";
24379
- import path28 from "path";
24489
+ import fs32 from "fs";
24490
+ import path29 from "path";
24380
24491
 
24381
24492
  // src/core/integrity.ts
24382
24493
  import crypto3 from "crypto";
@@ -24712,7 +24823,7 @@ function assertFileWithinCeiling(entryPath, byteLength) {
24712
24823
  }
24713
24824
  var WINDOWS_DRIVE_ABSOLUTE = /^[a-zA-Z]:[\\/]/u;
24714
24825
  function sanitizeBundleFilePath(entryPath, stagingDir) {
24715
- if (path28.isAbsolute(entryPath) || WINDOWS_DRIVE_ABSOLUTE.test(entryPath)) {
24826
+ if (path29.isAbsolute(entryPath) || WINDOWS_DRIVE_ABSOLUTE.test(entryPath)) {
24716
24827
  throw new IntegrityError(
24717
24828
  `Bundle file path "${entryPath}" is absolute \u2014 every file inside a bundle must use a relative path (REQ-INT-004).`
24718
24829
  );
@@ -24728,13 +24839,13 @@ function sanitizeBundleFilePath(entryPath, stagingDir) {
24728
24839
  `Bundle file path "${entryPath}" contains a ".." segment \u2014 directory traversal is rejected (REQ-INT-004).`
24729
24840
  );
24730
24841
  }
24731
- const resolved = path28.resolve(stagingDir, entryPath);
24842
+ const resolved = path29.resolve(stagingDir, entryPath);
24732
24843
  if (resolved === stagingDir) {
24733
24844
  throw new IntegrityError(
24734
24845
  `Bundle file path "${entryPath}" resolves to the staging directory itself \u2014 every entry must be a file inside it (REQ-INT-004).`
24735
24846
  );
24736
24847
  }
24737
- if (!resolved.startsWith(stagingDir + path28.sep)) {
24848
+ if (!resolved.startsWith(stagingDir + path29.sep)) {
24738
24849
  throw new IntegrityError(
24739
24850
  `Bundle file path "${entryPath}" escapes the staging directory \u2014 refusing to write (REQ-INT-004).`
24740
24851
  );
@@ -24786,10 +24897,10 @@ function verifyBundleFileHashes(bundle) {
24786
24897
  function writeStagedFiles(stagingDir, decoded) {
24787
24898
  for (const [rawPath, bytes] of decoded) {
24788
24899
  const target = sanitizeBundleFilePath(rawPath, stagingDir);
24789
- fs31.mkdirSync(path28.dirname(target), { recursive: true, mode: 448 });
24900
+ fs32.mkdirSync(path29.dirname(target), { recursive: true, mode: 448 });
24790
24901
  let existingStat;
24791
24902
  try {
24792
- existingStat = fs31.lstatSync(target);
24903
+ existingStat = fs32.lstatSync(target);
24793
24904
  } catch (error) {
24794
24905
  if (error.code !== "ENOENT") {
24795
24906
  throw error;
@@ -24800,7 +24911,7 @@ function writeStagedFiles(stagingDir, decoded) {
24800
24911
  `Refusing to write "${rawPath}" \u2014 a symbolic link already exists at the target path.`
24801
24912
  );
24802
24913
  }
24803
- fs31.writeFileSync(target, bytes, { mode: 384, flag: "wx" });
24914
+ fs32.writeFileSync(target, bytes, { mode: 384, flag: "wx" });
24804
24915
  }
24805
24916
  }
24806
24917
  function verifyWarmCacheEntry(entryDir, reference, keys, now) {
@@ -24856,14 +24967,14 @@ async function materializeRemoteKit(params) {
24856
24967
  writeStagedFiles(stagingDir, decoded);
24857
24968
  writeBundleDocument(stagingDir, bundle);
24858
24969
  writeCompletionMarker(stagingDir, reference);
24859
- if (replace && fs31.existsSync(entryDir)) {
24860
- fs31.rmSync(entryDir, { recursive: true, force: true });
24970
+ if (replace && fs32.existsSync(entryDir)) {
24971
+ fs32.rmSync(entryDir, { recursive: true, force: true });
24861
24972
  }
24862
24973
  publishCacheEntry(stagingDir, entryDir);
24863
24974
  return entryDir;
24864
24975
  } catch (error) {
24865
24976
  if (stagingDir !== void 0) {
24866
- fs31.rmSync(stagingDir, { recursive: true, force: true });
24977
+ fs32.rmSync(stagingDir, { recursive: true, force: true });
24867
24978
  }
24868
24979
  throw error;
24869
24980
  }
@@ -25205,7 +25316,7 @@ import path33 from "path";
25205
25316
  // src/compiler/agents.ts
25206
25317
  var TOML = __toESM(require_toml(), 1);
25207
25318
  var import_yaml8 = __toESM(require_dist(), 1);
25208
- import path29 from "path";
25319
+ import path30 from "path";
25209
25320
  var writeCapabilityClasses = /* @__PURE__ */ new Set(["filesystem-write", "shell-execution"]);
25210
25321
  var copilotToolAliasesByClass = {
25211
25322
  delegation: "agent",
@@ -25217,7 +25328,7 @@ var copilotToolAliasesByClass = {
25217
25328
  };
25218
25329
  function compileAgent(sourceFile, target, mappings, extensions, lossReport) {
25219
25330
  const { data, body } = parseMarkdownFrontmatter(sourceFile);
25220
- const relativeSource = `agents/${path29.basename(sourceFile)}`;
25331
+ const relativeSource = `agents/${path30.basename(sourceFile)}`;
25221
25332
  if (target === "opencode") {
25222
25333
  return compileOpenCodeAgent(
25223
25334
  sourceFile,
@@ -25258,7 +25369,7 @@ function compileAgent(sourceFile, target, mappings, extensions, lossReport) {
25258
25369
  mappings,
25259
25370
  lossReport
25260
25371
  );
25261
- const overlayKey = `agents/${path29.basename(sourceFile, ".md")}.${target}.yaml`;
25372
+ const overlayKey = `agents/${path30.basename(sourceFile, ".md")}.${target}.yaml`;
25262
25373
  const targetFields = {
25263
25374
  ...translated.fields,
25264
25375
  ...extensions[overlayKey] ?? {}
@@ -25292,7 +25403,7 @@ ${TOML.stringify({
25292
25403
  function compileOpenCodeAgent(sourceFile, data, body, relativeSource, lossReport) {
25293
25404
  if (typeof data.description !== "string" || data.description.trim() === "") {
25294
25405
  throw new Error(
25295
- `${path29.basename(sourceFile)}: OpenCode agent requires a non-empty description field`
25406
+ `${path30.basename(sourceFile)}: OpenCode agent requires a non-empty description field`
25296
25407
  );
25297
25408
  }
25298
25409
  const frontmatter = {
@@ -25346,7 +25457,7 @@ function compileOpenCodeAgent(sourceFile, data, body, relativeSource, lossReport
25346
25457
  "permission",
25347
25458
  "permission-coarsening"
25348
25459
  ),
25349
- detail: `${path29.basename(sourceFile)}: per-tool permission '${tool}' mapped to coarse allow|deny action`
25460
+ detail: `${path30.basename(sourceFile)}: per-tool permission '${tool}' mapped to coarse allow|deny action`
25350
25461
  }
25351
25462
  ]
25352
25463
  });
@@ -25371,7 +25482,7 @@ ${(0, import_yaml8.stringify)(frontmatter).trim()}
25371
25482
  function compileCursorAgent(sourceFile, data, body, relativeSource, lossReport) {
25372
25483
  if (typeof data.description !== "string" || data.description.trim() === "") {
25373
25484
  throw new Error(
25374
- `${path29.basename(sourceFile)}: Cursor agent requires a non-empty description field`
25485
+ `${path30.basename(sourceFile)}: Cursor agent requires a non-empty description field`
25375
25486
  );
25376
25487
  }
25377
25488
  const frontmatter = {
@@ -25408,7 +25519,7 @@ function compileCursorAgent(sourceFile, data, body, relativeSource, lossReport)
25408
25519
  "permission",
25409
25520
  "permission-coarsening"
25410
25521
  ),
25411
- detail: `${path29.basename(sourceFile)}: per-agent tool allow set coarsened to readonly=${readonly} boolean (Cursor only exposes readonly control)`
25522
+ detail: `${path30.basename(sourceFile)}: per-agent tool allow set coarsened to readonly=${readonly} boolean (Cursor only exposes readonly control)`
25412
25523
  }
25413
25524
  ]
25414
25525
  });
@@ -25427,7 +25538,7 @@ function compileCursorAgent(sourceFile, data, body, relativeSource, lossReport)
25427
25538
  "provider",
25428
25539
  "provider-not-representable"
25429
25540
  ),
25430
- detail: `${path29.basename(sourceFile)}: custom provider cannot be expressed in Cursor subagent frontmatter \u2014 configure manually via Cursor settings`
25541
+ detail: `${path30.basename(sourceFile)}: custom provider cannot be expressed in Cursor subagent frontmatter \u2014 configure manually via Cursor settings`
25431
25542
  }
25432
25543
  ]
25433
25544
  });
@@ -25459,7 +25570,7 @@ function compileClineAgent(sourceFile, _data, _body, relativeSource, lossReport)
25459
25570
  "persona",
25460
25571
  "persona-not-representable"
25461
25572
  ),
25462
- detail: `${path29.basename(sourceFile)}: Cline has no committable per-agent persona file for the VSCode extension target; configure the agent through Cline Settings \u2192 Agent.`
25573
+ detail: `${path30.basename(sourceFile)}: Cline has no committable per-agent persona file for the VSCode extension target; configure the agent through Cline Settings \u2192 Agent.`
25463
25574
  }
25464
25575
  ]
25465
25576
  });
@@ -25507,7 +25618,7 @@ function applyFieldAction(sourceFile, field, value, action, target, mappings, tr
25507
25618
  field,
25508
25619
  "model-not-representable"
25509
25620
  ),
25510
- detail: `${path29.basename(sourceFile)}: model tier '${String(value)}' has no stable Copilot model literal; omitted so VS Code uses the selected model.`
25621
+ detail: `${path30.basename(sourceFile)}: model tier '${String(value)}' has no stable Copilot model literal; omitted so VS Code uses the selected model.`
25511
25622
  });
25512
25623
  return;
25513
25624
  }
@@ -25525,9 +25636,9 @@ function applyFieldAction(sourceFile, field, value, action, target, mappings, tr
25525
25636
  lossReport,
25526
25637
  target,
25527
25638
  "agents",
25528
- `agents/${path29.basename(sourceFile)}`,
25639
+ `agents/${path30.basename(sourceFile)}`,
25529
25640
  "info",
25530
- `${path29.basename(sourceFile)}: ${field} is delivered through another managed surface.`
25641
+ `${path30.basename(sourceFile)}: ${field} is delivered through another managed surface.`
25531
25642
  );
25532
25643
  return;
25533
25644
  }
@@ -25536,7 +25647,7 @@ function applyFieldAction(sourceFile, field, value, action, target, mappings, tr
25536
25647
  field,
25537
25648
  action: entryType,
25538
25649
  severity: resolveLossSeverity("agent-field", field, entryType),
25539
- detail: `${path29.basename(sourceFile)}: ${field} has no emitted field for this target.`
25650
+ detail: `${path30.basename(sourceFile)}: ${field} has no emitted field for this target.`
25540
25651
  });
25541
25652
  }
25542
25653
  function mapToolsToCapabilities(value, mappings) {
@@ -25630,8 +25741,8 @@ function compileCursorHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
25630
25741
  sourceEvent
25631
25742
  );
25632
25743
  if (mapping.status === "mapped" && mapping.target_event) {
25633
- const extracted = extractHookCommand(payload);
25634
- if (!extracted.command) {
25744
+ const entries = extractHookCommands(payload);
25745
+ if (entries.length === 0) {
25635
25746
  droppedEvents.push(sourceEvent);
25636
25747
  addHookLoss(
25637
25748
  lossReport,
@@ -25644,26 +25755,24 @@ function compileCursorHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
25644
25755
  dropped += 1;
25645
25756
  continue;
25646
25757
  }
25647
- hooks[mapping.target_event] = { command: extracted.command };
25758
+ const untranslatable = /* @__PURE__ */ new Set();
25759
+ hooks[mapping.target_event] = entries.map((entry) => {
25760
+ const matcher = entry.matcher === void 0 ? void 0 : translateCursorMatcher(entry.matcher, untranslatable);
25761
+ return {
25762
+ type: "command",
25763
+ command: entry.command,
25764
+ ...matcher !== void 0 ? { matcher } : {}
25765
+ };
25766
+ });
25648
25767
  translated += 1;
25649
- if (extracted.remaining.length > 0) {
25650
- addHookLoss(
25651
- lossReport,
25652
- "cursor",
25653
- sourceEvent,
25654
- sourceLabel,
25655
- "error",
25656
- `${sourceEvent}: the source registers ${extracted.remaining.length + 1} hooks on this event and Cursor's entry holds one command. Only '${extracted.command}' was written; ${extracted.remaining.map((c) => `'${c}'`).join(", ")} ${extracted.remaining.length === 1 ? "does" : "do"} not run under Cursor. To fix: give this event a single entry point that dispatches to the others, or accept the loss knowingly.`
25657
- );
25658
- }
25659
- if (extracted.matcher) {
25768
+ if (untranslatable.size > 0) {
25660
25769
  addInformational(
25661
25770
  lossReport,
25662
25771
  "cursor",
25663
25772
  "hooks",
25664
25773
  sourceLabel,
25665
25774
  "warning",
25666
- `${sourceEvent}: the source scopes this hook to '${extracted.matcher}', and Cursor's hook entry carries no matcher. It will run for every tool call on this event, not only the matched ones.`
25775
+ `${sourceEvent}: ${[...untranslatable].map((m) => `'${m}'`).join(", ")} ${untranslatable.size === 1 ? "names a tool" : "name tools"} with no known Cursor equivalent and ${untranslatable.size === 1 ? "was" : "were"} written through unchanged. Cursor matches on its own tool names \u2014 measured: Shell, Read, Write, Grep \u2014 so a hook scoped to a name it does not use will not run. To fix: add the name to CURSOR_TOOL_NAMES in the compiler once its Cursor equivalent has been measured.`
25667
25776
  );
25668
25777
  }
25669
25778
  continue;
@@ -25821,7 +25930,35 @@ function compileCopilotHooks(sourceHooksJson, mappings, lossReport, sourceLabel)
25821
25930
  }
25822
25931
  return { payload: { hooks }, translated, dropped, droppedEvents };
25823
25932
  }
25824
- function extractHookCommand(payload) {
25933
+ var CURSOR_TOOL_NAMES = {
25934
+ Bash: "Shell",
25935
+ Edit: "Write",
25936
+ MultiEdit: "Write",
25937
+ // Cursor's own names map to themselves. Without these a kit that already
25938
+ // writes a Cursor matcher — the natural thing to do once this mapping is
25939
+ // documented — would have it reported as untranslatable and warned about,
25940
+ // while it is in fact the only spelling that works.
25941
+ Shell: "Shell",
25942
+ Read: "Read",
25943
+ Write: "Write",
25944
+ Grep: "Grep"
25945
+ };
25946
+ function translateCursorMatcher(matcher, untranslatable) {
25947
+ const translated = [];
25948
+ for (const token of matcher.split("|")) {
25949
+ const trimmed = token.trim();
25950
+ if (!trimmed) continue;
25951
+ const mapped = CURSOR_TOOL_NAMES[trimmed];
25952
+ if (mapped === void 0) {
25953
+ untranslatable.add(trimmed);
25954
+ if (!translated.includes(trimmed)) translated.push(trimmed);
25955
+ continue;
25956
+ }
25957
+ if (!translated.includes(mapped)) translated.push(mapped);
25958
+ }
25959
+ return translated.join("|");
25960
+ }
25961
+ function extractHookCommands(payload) {
25825
25962
  const entries = Array.isArray(payload) ? payload : [payload];
25826
25963
  const found = [];
25827
25964
  for (const entry of entries) {
@@ -25838,15 +25975,7 @@ function extractHookCommand(payload) {
25838
25975
  }
25839
25976
  }
25840
25977
  }
25841
- if (found.length === 0) {
25842
- return { command: "", remaining: [] };
25843
- }
25844
- const [first, ...rest] = found;
25845
- return {
25846
- command: first.command,
25847
- ...first.matcher !== void 0 ? { matcher: first.matcher } : {},
25848
- remaining: rest.map((r) => r.command)
25849
- };
25978
+ return found;
25850
25979
  }
25851
25980
  function normalizeHookEventRule(value, target, sourceEvent) {
25852
25981
  if (typeof value === "string") {
@@ -25928,23 +26057,23 @@ function addHookLoss(lossReport, target, eventName, sourceLabel, severity, reaso
25928
26057
  }
25929
26058
 
25930
26059
  // src/core/hook-scripts.ts
25931
- import path30 from "path";
26060
+ import path31 from "path";
25932
26061
  function hookScriptsDir(bridge, target) {
25933
26062
  const globalBridge = resolveTargetPath("~/.agents/hooks");
25934
- if (path30.resolve(bridge) === path30.resolve(globalBridge)) {
26063
+ if (path31.resolve(bridge) === path31.resolve(globalBridge)) {
25935
26064
  return { prefix: GLOBAL_HOOK_SCRIPTS_DIR, machineLocal: false };
25936
26065
  }
25937
- const relative = path30.relative(process.cwd(), bridge);
25938
- const insideProject = !relative.startsWith("..") && !path30.isAbsolute(relative) && relative !== "";
26066
+ const relative = path31.relative(process.cwd(), bridge);
26067
+ const insideProject = !relative.startsWith("..") && !path31.isAbsolute(relative) && relative !== "";
25939
26068
  const variable = PROJECT_DIR_VARIABLE[target];
25940
26069
  if (insideProject && variable) {
25941
- const posix = path30.posix.join(
25942
- relative.split(path30.sep).join("/"),
26070
+ const posix = path31.posix.join(
26071
+ relative.split(path31.sep).join("/"),
25943
26072
  "scripts"
25944
26073
  );
25945
26074
  return { prefix: `$${variable.slice(1)}/${posix}`, machineLocal: false };
25946
26075
  }
25947
- return { prefix: path30.join(bridge, "scripts"), machineLocal: true };
26076
+ return { prefix: path31.join(bridge, "scripts"), machineLocal: true };
25948
26077
  }
25949
26078
  var PROJECT_DIR_VARIABLE = {
25950
26079
  "claude-code": "$CLAUDE_PROJECT_DIR"
@@ -25953,52 +26082,6 @@ function machineLocalScriptPathNotice(target, prefix) {
25953
26082
  return `The hook commands compiled for ${target} name '${prefix}', an absolute path. ${target} publishes no project-directory variable a command could use instead \u2014 measured, not assumed \u2014 and a bare relative path would resolve against whatever directory the harness runs the hook from. The compiled file is therefore correct on this machine and not on another: re-run 'tangyr sync' after cloning, and prefer keeping it out of version control.`;
25954
26083
  }
25955
26084
 
25956
- // src/adapters/hook-scripts-bridge.ts
25957
- import fs32 from "fs";
25958
- import path31 from "path";
25959
- function syncHookScriptsBridge(args) {
25960
- const { source, destination, config, sourceRoot, options, logger } = args;
25961
- if (!fs32.existsSync(source)) {
25962
- return {
25963
- component: "hooks",
25964
- status: "skipped",
25965
- path: `${source}: source not configured`
25966
- };
25967
- }
25968
- if (options.dryRun) {
25969
- logger.info(`[dry-run] hooks: symlink ${source} -> ${destination}`);
25970
- return { component: "hooks", status: "dry-run", path: destination };
25971
- }
25972
- const artifactClass = classifyArtifact(destination, config, sourceRoot);
25973
- if (artifactClass === "managed-symlink" && isSymlinkTo(destination, source)) {
25974
- return { component: "hooks", status: "skipped-current", path: destination };
25975
- }
25976
- if (artifactClass === "managed-stale" && isOurStaleSymlink(destination, /* @__PURE__ */ new Set([destination]), config)) {
25977
- fs32.unlinkSync(destination);
25978
- } else if (artifactClass === "unmanaged-conflict" && !args.onConflict()) {
25979
- return { component: "hooks", status: "conflict", path: destination };
25980
- } else if (artifactClass === "managed-symlink") {
25981
- fs32.unlinkSync(destination);
25982
- }
25983
- ensureDir(path31.dirname(destination));
25984
- createRelativeSymlink(source, destination, "dir");
25985
- if (args.manifest && args.scopePath) {
25986
- addArtifactToManifest(args.manifest, {
25987
- relativePath: path31.relative(args.scopePath, destination),
25988
- hash: `dir:${source}`,
25989
- origin: "tangyr-managed"
25990
- });
25991
- }
25992
- return { component: "hooks", status: "success", path: destination };
25993
- }
25994
- function isSymlinkTo(linkPath, source) {
25995
- try {
25996
- return fs32.realpathSync(linkPath) === fs32.realpathSync(source);
25997
- } catch {
25998
- return false;
25999
- }
26000
- }
26001
-
26002
26085
  // src/compiler/mcp.ts
26003
26086
  var TOML2 = __toESM(require_toml(), 1);
26004
26087
  import crypto4 from "crypto";
@@ -29781,7 +29864,14 @@ function installCopilotTool(config, kitInfo, manifest, options, mappings, logger
29781
29864
  manifest,
29782
29865
  copilotScopePath
29783
29866
  );
29784
- recordInstalledArtifacts(manifest, "copilot", result.outcomes, mappings);
29867
+ recordInstalledArtifacts(
29868
+ manifest,
29869
+ "copilot",
29870
+ result.outcomes,
29871
+ mappings,
29872
+ scope,
29873
+ projectRoot
29874
+ );
29785
29875
  return countInstalledArtifacts(result.outcomes);
29786
29876
  }
29787
29877
  function installCodexTool(config, kitInfo, manifest, options, mappings, logger, scope = "global", projectRoot, lossReport) {
@@ -29812,7 +29902,14 @@ function installCodexTool(config, kitInfo, manifest, options, mappings, logger,
29812
29902
  manifest,
29813
29903
  codexScopePath
29814
29904
  );
29815
- recordInstalledArtifacts(manifest, "codex", result.outcomes, mappings);
29905
+ recordInstalledArtifacts(
29906
+ manifest,
29907
+ "codex",
29908
+ result.outcomes,
29909
+ mappings,
29910
+ scope,
29911
+ projectRoot
29912
+ );
29816
29913
  return countInstalledArtifacts(result.outcomes);
29817
29914
  }
29818
29915
  function installOpenCodeTool(config, kitInfo, manifest, options, mappings, logger, scope = "global", projectRoot, lossReport) {
@@ -29974,16 +30071,16 @@ function syncOutcomeToManifestKey(tool, outcome, mappings, scope = "global", pro
29974
30071
  throwUnrecognizedTarget(tool);
29975
30072
  }
29976
30073
  if (tool === "cline") {
29977
- const profile = resolveTargetPaths({
30074
+ const profile2 = resolveTargetPaths({
29978
30075
  scope,
29979
30076
  projectRoot,
29980
30077
  mappings: mappings.platformPaths
29981
30078
  }).cline;
29982
- const instrFile = profile.instructions !== null ? resolveTargetPath(profile.instructions) : null;
29983
- const rulesRoot2 = resolveTargetPath(profile.rules);
29984
- const workflowsRoot = resolveTargetPath(profile.workflows);
29985
- const skillsRoot2 = resolveTargetPath(profile.skills);
29986
- const mcpFile2 = resolveTargetPath(profile.mcp_file);
30079
+ const instrFile = profile2.instructions !== null ? resolveTargetPath(profile2.instructions) : null;
30080
+ const rulesRoot2 = resolveTargetPath(profile2.rules);
30081
+ const workflowsRoot = resolveTargetPath(profile2.workflows);
30082
+ const skillsRoot2 = resolveTargetPath(profile2.skills);
30083
+ const mcpFile2 = resolveTargetPath(profile2.mcp_file);
29987
30084
  if (instrFile !== null && outcome.path === instrFile) {
29988
30085
  return "cline/AGENTS.md";
29989
30086
  }
@@ -30002,19 +30099,19 @@ function syncOutcomeToManifestKey(tool, outcome, mappings, scope = "global", pro
30002
30099
  return null;
30003
30100
  }
30004
30101
  if (tool === "cursor") {
30005
- const profile = resolveTargetPaths({
30102
+ const profile2 = resolveTargetPaths({
30006
30103
  scope,
30007
30104
  projectRoot,
30008
30105
  mappings: mappings.platformPaths
30009
30106
  }).cursor;
30010
- const instrFile = resolveTargetPath(profile.instructions);
30011
- const agentsRoot2 = resolveTargetPath(profile.agents);
30012
- const rulesRoot2 = resolveTargetPath(profile.rules);
30013
- const skillsRoot2 = resolveTargetPath(profile.skills);
30014
- const skillsSharedRoot = resolveTargetPath(profile.skills_shared);
30015
- const hooksFile2 = resolveTargetPath(profile.hooks_file);
30016
- const mcpFile2 = resolveTargetPath(profile.mcp_file);
30017
- const permissionsFile = resolveTargetPath(profile.permissions_file);
30107
+ const instrFile = resolveTargetPath(profile2.instructions);
30108
+ const agentsRoot2 = resolveTargetPath(profile2.agents);
30109
+ const rulesRoot2 = resolveTargetPath(profile2.rules);
30110
+ const skillsRoot2 = resolveTargetPath(profile2.skills);
30111
+ const skillsSharedRoot = resolveTargetPath(profile2.skills_shared);
30112
+ const hooksFile2 = resolveTargetPath(profile2.hooks_file);
30113
+ const mcpFile2 = resolveTargetPath(profile2.mcp_file);
30114
+ const permissionsFile = resolveTargetPath(profile2.permissions_file);
30018
30115
  if (outcome.path === instrFile) {
30019
30116
  return "cursor/AGENTS.md";
30020
30117
  }
@@ -30046,17 +30143,17 @@ function syncOutcomeToManifestKey(tool, outcome, mappings, scope = "global", pro
30046
30143
  return null;
30047
30144
  }
30048
30145
  if (tool === "opencode") {
30049
- const profile = resolveTargetPaths({
30146
+ const profile2 = resolveTargetPaths({
30050
30147
  scope,
30051
30148
  projectRoot,
30052
30149
  mappings: mappings.platformPaths
30053
30150
  }).opencode;
30054
- const instrFile = resolveTargetPath(profile.instructions);
30055
- const agentsRoot2 = resolveTargetPath(profile.agents);
30056
- const commandsRoot = resolveTargetPath(profile.commands);
30057
- const skillsRoot2 = resolveTargetPath(profile.skills);
30058
- const skillsSharedRoot = resolveTargetPath(profile.skills_shared);
30059
- const configDocFile = resolveTargetPath(profile.config_doc);
30151
+ const instrFile = resolveTargetPath(profile2.instructions);
30152
+ const agentsRoot2 = resolveTargetPath(profile2.agents);
30153
+ const commandsRoot = resolveTargetPath(profile2.commands);
30154
+ const skillsRoot2 = resolveTargetPath(profile2.skills);
30155
+ const skillsSharedRoot = resolveTargetPath(profile2.skills_shared);
30156
+ const configDocFile = resolveTargetPath(profile2.config_doc);
30060
30157
  if (outcome.path === instrFile) {
30061
30158
  return "opencode/AGENTS.md";
30062
30159
  }
@@ -30077,16 +30174,25 @@ function syncOutcomeToManifestKey(tool, outcome, mappings, scope = "global", pro
30077
30174
  }
30078
30175
  return null;
30079
30176
  }
30080
- const targetPaths = mappings.platformPaths.targets[tool].global;
30177
+ const profile = resolveTargetPaths({
30178
+ scope,
30179
+ projectRoot,
30180
+ mappings: mappings.platformPaths
30181
+ })[tool];
30081
30182
  if (tool === "copilot") {
30082
- const agentsRoot2 = resolveTargetPath(targetPaths.agents ?? "");
30083
- const promptsRoot = resolveTargetPath(targetPaths.prompts ?? "");
30084
- const skillsRoot2 = resolveTargetPath(targetPaths.skills ?? "");
30085
- const hooksRoot = resolveTargetPath(targetPaths.hooks ?? "");
30086
- const mcpFile2 = resolveTargetPath(targetPaths.mcp_shared_file ?? "");
30183
+ const copilot = profile;
30184
+ const agentsRoot2 = resolveTargetPath(copilot.agents);
30185
+ const promptsRoot = resolveTargetPath(copilot.prompts);
30186
+ const skillsRoot2 = resolveTargetPath(copilot.skills);
30187
+ const hooksRoot = resolveTargetPath(copilot.hooks);
30188
+ const mcpFile2 = resolveTargetPath(copilot.mcp_shared_file);
30189
+ const instructionsMirror = copilot.instructions !== null ? resolveTargetPath(copilot.instructions) : null;
30087
30190
  if (outcome.path === skillsRoot2) {
30088
30191
  return "copilot/skills";
30089
30192
  }
30193
+ if (instructionsMirror !== null && outcome.path === instructionsMirror) {
30194
+ return `copilot/${path39.basename(outcome.path)}`;
30195
+ }
30090
30196
  if (outcome.path.startsWith(`${agentsRoot2}${path39.sep}`)) {
30091
30197
  return `copilot/agents/${path39.basename(outcome.path)}`;
30092
30198
  }
@@ -30101,12 +30207,13 @@ function syncOutcomeToManifestKey(tool, outcome, mappings, scope = "global", pro
30101
30207
  }
30102
30208
  return null;
30103
30209
  }
30104
- const instructionsFile = resolveTargetPath(targetPaths.instructions ?? "");
30105
- const agentsRoot = resolveTargetPath(targetPaths.agents ?? "");
30106
- const rulesRoot = resolveTargetPath(targetPaths.rules ?? "");
30107
- const skillsRoot = resolveTargetPath(targetPaths.skills ?? "");
30108
- const hooksFile = resolveTargetPath(targetPaths.hooks_shared_file ?? "");
30109
- const mcpFile = resolveTargetPath(targetPaths.mcp_shared_file ?? "");
30210
+ const codex = profile;
30211
+ const instructionsFile = resolveTargetPath(codex.instructions);
30212
+ const agentsRoot = resolveTargetPath(codex.agents);
30213
+ const rulesRoot = resolveTargetPath(codex.rules);
30214
+ const skillsRoot = resolveTargetPath(codex.skills);
30215
+ const hooksFile = resolveTargetPath(codex.hooks_shared_file);
30216
+ const mcpFile = resolveTargetPath(codex.mcp_shared_file);
30110
30217
  if (outcome.path === instructionsFile) {
30111
30218
  return "codex/AGENTS.md";
30112
30219
  }