xtrm-tools 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.xtrm/config/hooks.json +0 -3
  2. package/.xtrm/registry.json +537 -565
  3. package/.xtrm/skills/default/gitnexus-cli/SKILL.md +82 -0
  4. package/.xtrm/skills/default/gitnexus-exploring/SKILL.md +78 -0
  5. package/.xtrm/skills/default/gitnexus-guide/SKILL.md +64 -0
  6. package/.xtrm/skills/default/init-session/SKILL.md +3 -0
  7. package/.xtrm/skills/default/last30days/SKILL.md +1 -1
  8. package/.xtrm/skills/default/last30days/scripts/lib/youtube_yt.py +59 -40
  9. package/.xtrm/skills/default/sync-docs/references/doc-structure.md +1 -1
  10. package/.xtrm/skills/default/sync-docs/references/schema.md +1 -1
  11. package/.xtrm/skills/default/sync-docs/scripts/doc_structure_analyzer.py +2 -2
  12. package/.xtrm/skills/default/using-specialists/SKILL.md +346 -138
  13. package/.xtrm/skills/default/using-specialists/SKILL.safe.md +1082 -0
  14. package/.xtrm/skills/default/using-specialists/SKILL.ultra.md +1082 -0
  15. package/.xtrm/skills/default/vaultctl/SKILL.md +150 -0
  16. package/CHANGELOG.md +4 -4
  17. package/cli/dist/index.cjs +328 -192
  18. package/cli/dist/index.cjs.map +1 -1
  19. package/cli/package.json +1 -1
  20. package/package.json +8 -8
  21. package/packages/pi-extensions/MIGRATION_NOTES.md +39 -0
  22. package/packages/pi-extensions/README.md +43 -0
  23. package/packages/pi-extensions/extensions/README.md +5 -0
  24. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/beads/index.ts +1 -1
  25. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/beads/package.json +1 -4
  26. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-footer/index.ts +1 -1
  27. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-footer/package.json +1 -4
  28. package/packages/pi-extensions/extensions/custom-provider-qwen-cli/package.json +16 -0
  29. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/pi-serena-compact/package.json +9 -2
  30. package/{.xtrm/ext-src → packages/pi-extensions/extensions}/quality-gates/index.ts +1 -1
  31. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/quality-gates/package.json +1 -4
  32. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/service-skills/index.ts +1 -1
  33. package/{.xtrm/ext-src → packages/pi-extensions/extensions}/service-skills/package.json +1 -4
  34. package/{.xtrm/ext-src → packages/pi-extensions/extensions}/session-flow/index.ts +1 -1
  35. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/session-flow/package.json +1 -4
  36. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-loader/index.ts +1 -1
  37. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-loader/package.json +1 -4
  38. package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/index.ts +1 -1
  39. package/packages/pi-extensions/package.json +46 -0
  40. package/packages/pi-extensions/src/core/README.md +9 -0
  41. package/{.xtrm/ext-src/core/lib.ts → packages/pi-extensions/src/core/index.ts} +3 -1
  42. package/packages/pi-extensions/src/extensions/auto-session-name.ts +3 -0
  43. package/packages/pi-extensions/src/extensions/auto-update.ts +3 -0
  44. package/packages/pi-extensions/src/extensions/beads.ts +3 -0
  45. package/packages/pi-extensions/src/extensions/compact-header.ts +3 -0
  46. package/packages/pi-extensions/src/extensions/custom-footer.ts +3 -0
  47. package/packages/pi-extensions/src/extensions/custom-provider-qwen-cli.ts +3 -0
  48. package/packages/pi-extensions/src/extensions/git-checkpoint.ts +3 -0
  49. package/packages/pi-extensions/src/extensions/lsp-bootstrap.ts +3 -0
  50. package/packages/pi-extensions/src/extensions/pi-serena-compact.ts +3 -0
  51. package/packages/pi-extensions/src/extensions/quality-gates.ts +3 -0
  52. package/packages/pi-extensions/src/extensions/service-skills.ts +3 -0
  53. package/packages/pi-extensions/src/extensions/session-flow.ts +3 -0
  54. package/packages/pi-extensions/src/extensions/xtrm-loader.ts +3 -0
  55. package/packages/pi-extensions/src/extensions/xtrm-ui.ts +3 -0
  56. package/packages/pi-extensions/src/index.ts +9 -0
  57. package/packages/pi-extensions/src/registry.ts +52 -0
  58. package/packages/pi-extensions/src/shared/index.ts +1 -0
  59. package/packages/pi-extensions/src/shared/legacy-path-map.ts +23 -0
  60. package/.xtrm/config/pi/extensions/core/package.json +0 -18
  61. package/.xtrm/config/pi/extensions/custom-provider-qwen-cli/package.json +0 -1
  62. package/.xtrm/config/pi/extensions/quality-gates/index.ts +0 -66
  63. package/.xtrm/config/pi/extensions/service-skills/package.json +0 -19
  64. package/.xtrm/config/pi/extensions/session-flow/index.ts +0 -96
  65. package/.xtrm/config/pi/extensions/xtrm-ui/themes/pidex-dark.json +0 -89
  66. package/.xtrm/config/pi/extensions/xtrm-ui/themes/pidex-light.json +0 -89
  67. package/.xtrm/ext-src/auto-session-name/index.ts +0 -29
  68. package/.xtrm/ext-src/auto-session-name/package.json +0 -16
  69. package/.xtrm/ext-src/auto-update/index.ts +0 -71
  70. package/.xtrm/ext-src/auto-update/package.json +0 -16
  71. package/.xtrm/ext-src/beads/index.ts +0 -232
  72. package/.xtrm/ext-src/beads/package.json +0 -19
  73. package/.xtrm/ext-src/compact-header/index.ts +0 -69
  74. package/.xtrm/ext-src/compact-header/package.json +0 -16
  75. package/.xtrm/ext-src/core/adapter.ts +0 -52
  76. package/.xtrm/ext-src/core/guard-rules.ts +0 -100
  77. package/.xtrm/ext-src/core/logger.ts +0 -45
  78. package/.xtrm/ext-src/core/package.json +0 -18
  79. package/.xtrm/ext-src/core/runner.ts +0 -71
  80. package/.xtrm/ext-src/core/session-state.ts +0 -59
  81. package/.xtrm/ext-src/custom-footer/index.ts +0 -398
  82. package/.xtrm/ext-src/custom-footer/package.json +0 -19
  83. package/.xtrm/ext-src/custom-provider-qwen-cli/index.ts +0 -363
  84. package/.xtrm/ext-src/custom-provider-qwen-cli/package.json +0 -1
  85. package/.xtrm/ext-src/git-checkpoint/index.ts +0 -53
  86. package/.xtrm/ext-src/git-checkpoint/package.json +0 -16
  87. package/.xtrm/ext-src/lsp-bootstrap/index.ts +0 -134
  88. package/.xtrm/ext-src/lsp-bootstrap/package.json +0 -17
  89. package/.xtrm/ext-src/pi-serena-compact/index.ts +0 -121
  90. package/.xtrm/ext-src/pi-serena-compact/package.json +0 -16
  91. package/.xtrm/ext-src/quality-gates/package.json +0 -19
  92. package/.xtrm/ext-src/service-skills/index.ts +0 -108
  93. package/.xtrm/ext-src/session-flow/package.json +0 -19
  94. package/.xtrm/ext-src/xtrm-loader/index.ts +0 -152
  95. package/.xtrm/ext-src/xtrm-loader/package.json +0 -19
  96. package/.xtrm/ext-src/xtrm-ui/format.ts +0 -282
  97. package/.xtrm/ext-src/xtrm-ui/index.ts +0 -1112
  98. package/.xtrm/ext-src/xtrm-ui/package.json +0 -21
  99. /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.combined.log +0 -0
  100. /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.stderr.log +0 -0
  101. /package/.xtrm/{ext-src → config/pi/extensions}/custom-footer/.pi/structured-returns/83051fe4-97da-4e2c-bdaa-343b32f4e714.stdout.log +0 -0
  102. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-session-name/index.ts +0 -0
  103. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-session-name/package.json +0 -0
  104. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-update/index.ts +0 -0
  105. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/auto-update/package.json +0 -0
  106. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/compact-header/index.ts +0 -0
  107. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/compact-header/package.json +0 -0
  108. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/custom-provider-qwen-cli/index.ts +0 -0
  109. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/git-checkpoint/index.ts +0 -0
  110. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/git-checkpoint/package.json +0 -0
  111. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/lsp-bootstrap/index.ts +0 -0
  112. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/lsp-bootstrap/package.json +0 -0
  113. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/pi-serena-compact/index.ts +0 -0
  114. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/format.ts +0 -0
  115. /package/{.xtrm/config/pi → packages/pi-extensions}/extensions/xtrm-ui/package.json +0 -0
  116. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/adapter.ts +0 -0
  117. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/guard-rules.ts +0 -0
  118. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/lib.ts +0 -0
  119. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/logger.ts +0 -0
  120. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/runner.ts +0 -0
  121. /package/{.xtrm/config/pi/extensions → packages/pi-extensions/src}/core/session-state.ts +0 -0
  122. /package/{.xtrm/ext-src/xtrm-ui/themes → packages/pi-extensions/themes/xtrm-ui}/pidex-dark.json +0 -0
  123. /package/{.xtrm/ext-src/xtrm-ui/themes → packages/pi-extensions/themes/xtrm-ui}/pidex-light.json +0 -0
@@ -43891,6 +43891,13 @@ async function setRuntimeEnabledPacks(skillsRoot, runtime, packNames) {
43891
43891
  }
43892
43892
 
43893
43893
  // src/core/skills-materializer.ts
43894
+ var PI_NPM_PROVIDED_SKILLS = /* @__PURE__ */ new Set([
43895
+ "gitnexus-debugging",
43896
+ "gitnexus-exploring",
43897
+ "gitnexus-impact-analysis",
43898
+ "gitnexus-pr-review",
43899
+ "gitnexus-refactoring"
43900
+ ]);
43894
43901
  function sortByName(entries) {
43895
43902
  return [...entries].sort((a, b) => a.name.localeCompare(b.name));
43896
43903
  }
@@ -43929,11 +43936,12 @@ async function selectRuntimeSkills(runtime, skillsRoot) {
43929
43936
  const defaultSkills = await discoverDefaultSkills(skillsRoot);
43930
43937
  const enabledPackSkills = await collectEnabledPackSkills(skillsRoot, enabledPacks);
43931
43938
  const allSkills = sortByName([...defaultSkills, ...enabledPackSkills]);
43932
- assertNoRuntimeCollisions(runtime, allSkills);
43939
+ const filteredSkills = runtime === "pi" ? allSkills.filter((s) => !PI_NPM_PROVIDED_SKILLS.has(s.name)) : allSkills;
43940
+ assertNoRuntimeCollisions(runtime, filteredSkills);
43933
43941
  return {
43934
43942
  runtime,
43935
43943
  enabledPacks: [...enabledPacks],
43936
- skills: allSkills
43944
+ skills: filteredSkills
43937
43945
  };
43938
43946
  }
43939
43947
  async function buildRuntimeTempView(runtime, skillsRoot, selectedSkills) {
@@ -44102,6 +44110,26 @@ function resolveStatuslineScript(worktreePath) {
44102
44110
  if ((0, import_node_fs.existsSync)(repoStatusline)) return repoStatusline;
44103
44111
  return null;
44104
44112
  }
44113
+ function ensureWorktreeSpecialists(worktreePath, mainRepoPath) {
44114
+ const worktreeSpecialistsRoot = import_node_path5.default.join(worktreePath, ".specialists");
44115
+ (0, import_node_fs.mkdirSync)(worktreeSpecialistsRoot, { recursive: true });
44116
+ const specialistDirs = ["default", "user"];
44117
+ for (const dirName of specialistDirs) {
44118
+ const sourceDir = import_node_path5.default.join(mainRepoPath, ".specialists", dirName);
44119
+ if (!(0, import_node_fs.existsSync)(sourceDir)) continue;
44120
+ const targetDir = import_node_path5.default.join(worktreeSpecialistsRoot, dirName);
44121
+ const symlinkTarget = import_node_path5.default.relative(import_node_path5.default.dirname(targetDir), sourceDir);
44122
+ try {
44123
+ const existing = (0, import_node_fs.lstatSync)(targetDir);
44124
+ if (existing.isSymbolicLink() && (0, import_node_fs.readlinkSync)(targetDir) === symlinkTarget) {
44125
+ continue;
44126
+ }
44127
+ (0, import_node_fs.rmSync)(targetDir, { recursive: true, force: true });
44128
+ } catch {
44129
+ }
44130
+ (0, import_node_fs.symlinkSync)(symlinkTarget, targetDir, "dir");
44131
+ }
44132
+ }
44105
44133
  function sessionMetaPath(worktreePath) {
44106
44134
  return import_node_path5.default.join(worktreePath, ".xtrm", "session-meta.json");
44107
44135
  }
@@ -44209,6 +44237,12 @@ async function launchWorktreeSession(opts) {
44209
44237
  }
44210
44238
  }
44211
44239
  }
44240
+ try {
44241
+ ensureWorktreeSpecialists(worktreePath, mainRepoRoot);
44242
+ } catch (error48) {
44243
+ const message = error48 instanceof Error ? error48.message : String(error48);
44244
+ console.log(kleur_default.dim(` warning: could not provision specialist definitions (${message})`));
44245
+ }
44212
44246
  const localSettings = {};
44213
44247
  const statuslinePath = resolveStatuslineScript(worktreePath);
44214
44248
  if (statuslinePath) {
@@ -44735,21 +44769,51 @@ var import_child_process3 = require("child_process");
44735
44769
  var import_fs_extra9 = __toESM(require_lib(), 1);
44736
44770
  var import_path4 = __toESM(require("path"), 1);
44737
44771
  var import_node_os = require("os");
44772
+ var MANAGED_PI_EXTENSION_SOURCE_CANDIDATES = [
44773
+ ["packages", "pi-extensions", "extensions"],
44774
+ [".xtrm", "extensions"]
44775
+ ];
44776
+ function resolveFirstExistingPath(rootDir, candidates) {
44777
+ for (const candidate of candidates) {
44778
+ const candidatePath = import_path4.default.join(rootDir, ...candidate);
44779
+ if (import_fs_extra9.default.existsSync(candidatePath)) {
44780
+ return candidatePath;
44781
+ }
44782
+ }
44783
+ return null;
44784
+ }
44738
44785
  function resolvePkgRoot() {
44739
44786
  const candidates = [
44740
44787
  import_path4.default.resolve(__dirname, "../.."),
44741
44788
  import_path4.default.resolve(__dirname, "../../..")
44742
44789
  ];
44743
- for (const c of candidates) {
44744
- if (import_fs_extra9.default.existsSync(import_path4.default.join(c, ".xtrm", "extensions"))) return c;
44790
+ for (const candidateRoot of candidates) {
44791
+ if (resolveFirstExistingPath(candidateRoot, MANAGED_PI_EXTENSION_SOURCE_CANDIDATES)) {
44792
+ return candidateRoot;
44793
+ }
44745
44794
  }
44746
44795
  return candidates[0];
44747
44796
  }
44797
+ function resolveManagedPiExtensionsSourceDir(pkgRoot = resolvePkgRoot()) {
44798
+ return resolveFirstExistingPath(pkgRoot, MANAGED_PI_EXTENSION_SOURCE_CANDIDATES);
44799
+ }
44800
+ function resolveManagedPiCoreSourceDir(pkgRoot = resolvePkgRoot()) {
44801
+ return resolveFirstExistingPath(pkgRoot, [
44802
+ ["packages", "pi-extensions", "src", "core"],
44803
+ [".xtrm", "extensions", "core"]
44804
+ ]);
44805
+ }
44748
44806
  var PI_AGENT_DIR = process.env.PI_AGENT_DIR || import_path4.default.join((0, import_node_os.homedir)(), ".pi", "agent");
44749
44807
  var PI_MCP_ADAPTER_OVERRIDE_DIR = import_path4.default.join(PI_AGENT_DIR, "extensions", "pi-mcp-adapter");
44750
44808
  var PI_MCP_ADAPTER_REQUIRED_ENTRY = "commands.js";
44751
44809
  var PROJECT_EXTENSIONS_ENTRY = "../.xtrm/extensions";
44752
44810
  var PROJECT_SKILLS_ENTRY = "../.xtrm/skills/active/pi";
44811
+ var PROJECT_EXTENSION_PACKAGE_ID = "npm:@jaggerxtrm/pi-extensions";
44812
+ var CONFLICTING_PI_PACKAGE_IDS = /* @__PURE__ */ new Set(["npm:pi-dex"]);
44813
+ var LEGACY_PROJECT_EXTENSION_ENTRIES = /* @__PURE__ */ new Set([
44814
+ PROJECT_EXTENSIONS_ENTRY,
44815
+ ".xtrm/extensions"
44816
+ ]);
44753
44817
  var MANAGED_EXTENSIONS = [
44754
44818
  { id: "core", displayName: "@xtrm/pi-core", isLibrary: true, required: true },
44755
44819
  { id: "auto-session-name", displayName: "auto-session-name", required: false },
@@ -44779,6 +44843,10 @@ var ALWAYS_GLOBAL_INSTALL_PACKAGE_IDS = /* @__PURE__ */ new Set([
44779
44843
  "npm:pi-gitnexus",
44780
44844
  "npm:pi-serena-tools"
44781
44845
  ]);
44846
+ var PROJECT_REQUIRED_PACKAGE_IDS = [
44847
+ PROJECT_EXTENSION_PACKAGE_ID,
44848
+ ...MANAGED_PACKAGES.map((pkg) => pkg.id)
44849
+ ];
44782
44850
  function getInstalledPiPackages() {
44783
44851
  const result = (0, import_child_process3.spawnSync)("pi", ["list"], { encoding: "utf8", stdio: "pipe" });
44784
44852
  if (result.status !== 0) return [];
@@ -44913,6 +44981,20 @@ function renderPiRuntimePlan(plan) {
44913
44981
  console.log(kleur_default.yellow(" \u26A0 Missing required items.\n"));
44914
44982
  }
44915
44983
  }
44984
+ function getProjectRequiredPackageStatuses(installedPkgIds) {
44985
+ return PROJECT_REQUIRED_PACKAGE_IDS.map((packageId) => {
44986
+ const managed = MANAGED_PACKAGES.find((pkg2) => pkg2.id === packageId);
44987
+ const pkg = managed ?? {
44988
+ id: PROJECT_EXTENSION_PACKAGE_ID,
44989
+ displayName: "@jaggerxtrm/pi-extensions",
44990
+ required: true
44991
+ };
44992
+ return {
44993
+ pkg,
44994
+ installed: installedPkgIds.includes(packageId)
44995
+ };
44996
+ });
44997
+ }
44916
44998
  function mergePiSyncResults(base, incoming) {
44917
44999
  return {
44918
45000
  extensionsAdded: [...base.extensionsAdded, ...incoming.extensionsAdded],
@@ -44933,10 +45015,63 @@ async function isPackagePresentInPiAgent(agentDir, piPackageId) {
44933
45015
  const packageDir = import_path4.default.join(agentDir, "npm", "node_modules", npmPackageName);
44934
45016
  return import_fs_extra9.default.pathExists(packageDir);
44935
45017
  }
44936
- async function ensureAlwaysGlobalPiPackages(dryRun, log, agentDir = PI_AGENT_DIR, installRunner = (piPackageId) => {
44937
- const installResult = (0, import_child_process3.spawnSync)("pi", ["install", piPackageId], { stdio: "pipe", encoding: "utf8" });
44938
- return installResult.status;
44939
- }) {
45018
+ var NPMJS_REGISTRY_URL = "https://registry.npmjs.org";
45019
+ function runPiPackageInstall(piPackageId, env2) {
45020
+ const installResult = (0, import_child_process3.spawnSync)("pi", ["install", piPackageId], {
45021
+ stdio: "pipe",
45022
+ encoding: "utf8",
45023
+ env: env2
45024
+ });
45025
+ return {
45026
+ status: installResult.status,
45027
+ stdout: installResult.stdout ?? "",
45028
+ stderr: installResult.stderr ?? ""
45029
+ };
45030
+ }
45031
+ function getPiPackageInstallOutput(result) {
45032
+ return `${result.stdout}
45033
+ ${result.stderr}`.trim();
45034
+ }
45035
+ function buildNpmjsRegistryEnv(baseEnv = process.env) {
45036
+ return {
45037
+ ...baseEnv,
45038
+ NPM_CONFIG_REGISTRY: NPMJS_REGISTRY_URL,
45039
+ npm_config_registry: NPMJS_REGISTRY_URL
45040
+ };
45041
+ }
45042
+ function shouldRetryPiInstallViaNpmjs(piPackageId, output) {
45043
+ if (piPackageId !== PROJECT_EXTENSION_PACKAGE_ID) return false;
45044
+ const normalizedOutput = output.toLowerCase();
45045
+ return normalizedOutput.includes("npmmirror") && normalizedOutput.includes("404");
45046
+ }
45047
+ function getPiPackageInstallFailureHint(piPackageId, output) {
45048
+ if (!shouldRetryPiInstallViaNpmjs(piPackageId, output)) {
45049
+ return [];
45050
+ }
45051
+ return [
45052
+ `detected registry mirror 404 for ${piPackageId}`,
45053
+ `best fix: npm config set @jaggerxtrm:registry ${NPMJS_REGISTRY_URL}`
45054
+ ];
45055
+ }
45056
+ function installPiPackageWithFallback(piPackageId, log, installRunner = runPiPackageInstall) {
45057
+ const initialResult = installRunner(piPackageId);
45058
+ const initialOutput = getPiPackageInstallOutput(initialResult);
45059
+ if ((initialResult.status ?? 1) === 0) {
45060
+ return { status: initialResult.status, output: initialOutput, retriedWithNpmjs: false };
45061
+ }
45062
+ if (!shouldRetryPiInstallViaNpmjs(piPackageId, initialOutput)) {
45063
+ return { status: initialResult.status, output: initialOutput, retriedWithNpmjs: false };
45064
+ }
45065
+ log?.(kleur_default.dim(`Detected npmmirror 404 for ${piPackageId}; retrying via ${NPMJS_REGISTRY_URL}`));
45066
+ const retriedResult = installRunner(piPackageId, buildNpmjsRegistryEnv());
45067
+ const retriedOutput = getPiPackageInstallOutput(retriedResult);
45068
+ return {
45069
+ status: retriedResult.status,
45070
+ output: [initialOutput, retriedOutput].filter(Boolean).join("\n"),
45071
+ retriedWithNpmjs: true
45072
+ };
45073
+ }
45074
+ async function ensureAlwaysGlobalPiPackages(dryRun, log, agentDir = PI_AGENT_DIR, installRunner = runPiPackageInstall) {
44940
45075
  const installed = [];
44941
45076
  const failed = [];
44942
45077
  const packagesToEnsure = MANAGED_PACKAGES.filter((pkg) => ALWAYS_GLOBAL_INSTALL_PACKAGE_IDS.has(pkg.id));
@@ -44948,14 +45083,17 @@ async function ensureAlwaysGlobalPiPackages(dryRun, log, agentDir = PI_AGENT_DIR
44948
45083
  log?.(`[DRY RUN] pi install ${pkg.id}`);
44949
45084
  continue;
44950
45085
  }
44951
- const installStatus = installRunner(pkg.id);
44952
- if (installStatus === 0) {
45086
+ const installAttempt = installPiPackageWithFallback(pkg.id, log, installRunner);
45087
+ if (installAttempt.status === 0) {
44953
45088
  installed.push(pkg.id);
44954
- log?.(`${sym.ok} ${pkg.displayName} (global)`);
45089
+ log?.(`${sym.ok} ${pkg.displayName} (global${installAttempt.retriedWithNpmjs ? ", npmjs fallback" : ""})`);
44955
45090
  continue;
44956
45091
  }
44957
45092
  failed.push(pkg.id);
44958
45093
  log?.(kleur_default.yellow(`\u26A0 ${pkg.displayName} \u2014 global install failed`));
45094
+ for (const hint of getPiPackageInstallFailureHint(pkg.id, installAttempt.output)) {
45095
+ log?.(kleur_default.yellow(` \u2192 ${hint}`));
45096
+ }
44959
45097
  }
44960
45098
  return { installed, failed };
44961
45099
  }
@@ -45095,30 +45233,90 @@ async function cleanupLegacyProjectExtensionCopies(projectRoot, dryRun, log) {
45095
45233
  }
45096
45234
  return { removed, failed };
45097
45235
  }
45236
+ function normalizeStringArray(value) {
45237
+ if (!Array.isArray(value)) return [];
45238
+ return value.filter((entry) => typeof entry === "string");
45239
+ }
45240
+ function pruneConflictingPiPackageEntries(entries) {
45241
+ const kept = [];
45242
+ const removed = [];
45243
+ for (const entry of entries) {
45244
+ if (CONFLICTING_PI_PACKAGE_IDS.has(entry)) {
45245
+ removed.push(entry);
45246
+ continue;
45247
+ }
45248
+ kept.push(entry);
45249
+ }
45250
+ return { kept, removed };
45251
+ }
45252
+ async function pruneConflictingPiPackagesFromSettings(settingsPath, scopeLabel, dryRun, log) {
45253
+ if (!await import_fs_extra9.default.pathExists(settingsPath)) {
45254
+ return [];
45255
+ }
45256
+ let existingSettings = {};
45257
+ try {
45258
+ existingSettings = await import_fs_extra9.default.readJson(settingsPath);
45259
+ } catch {
45260
+ return [];
45261
+ }
45262
+ const existingPackages = normalizeStringArray(existingSettings.packages);
45263
+ const { kept, removed } = pruneConflictingPiPackageEntries(existingPackages);
45264
+ if (removed.length === 0) {
45265
+ return [];
45266
+ }
45267
+ if (dryRun) {
45268
+ log?.(kleur_default.dim(`[DRY RUN] would remove conflicting Pi package(s) from ${scopeLabel}: ${removed.join(", ")}`));
45269
+ return removed;
45270
+ }
45271
+ await import_fs_extra9.default.writeJson(settingsPath, { ...existingSettings, packages: kept }, { spaces: 2 });
45272
+ log?.(kleur_default.dim(`Removed conflicting Pi package(s) from ${scopeLabel}: ${removed.join(", ")}`));
45273
+ return removed;
45274
+ }
45275
+ async function cleanupConflictingPiPackageSettings(projectRoot, dryRun, log) {
45276
+ await pruneConflictingPiPackagesFromSettings(
45277
+ import_path4.default.join(PI_AGENT_DIR, "settings.json"),
45278
+ "~/.pi/agent/settings.json",
45279
+ dryRun,
45280
+ log
45281
+ );
45282
+ await pruneConflictingPiPackagesFromSettings(
45283
+ import_path4.default.join(projectRoot, ".pi", "settings.json"),
45284
+ `${projectRoot}/.pi/settings.json`,
45285
+ dryRun,
45286
+ log
45287
+ );
45288
+ }
45098
45289
  async function updatePiSettings(projectRoot, dryRun, log) {
45099
- const piSettingsPath = import_path4.default.join(projectRoot, ".pi", "settings.json");
45290
+ const piDirPath = import_path4.default.join(projectRoot, ".pi");
45291
+ const piSettingsPath = import_path4.default.join(piDirPath, "settings.json");
45100
45292
  if (dryRun) {
45101
- log?.(kleur_default.dim(`[DRY RUN] would update .pi/settings.json`));
45293
+ log?.(kleur_default.dim(`[DRY RUN] would ensure .pi/settings.json with ${PROJECT_EXTENSION_PACKAGE_ID}`));
45102
45294
  return;
45103
45295
  }
45296
+ await import_fs_extra9.default.ensureDir(piDirPath);
45104
45297
  let existingSettings = {};
45105
45298
  try {
45106
45299
  existingSettings = await import_fs_extra9.default.readJson(piSettingsPath);
45107
45300
  } catch {
45108
- }
45109
- const existingPackages = (existingSettings.packages ?? []).filter(
45110
- (p) => !p.startsWith("./extensions/")
45111
- );
45112
- await import_fs_extra9.default.ensureDir(import_path4.default.join(projectRoot, ".pi"));
45113
- await import_fs_extra9.default.writeJson(piSettingsPath, {
45301
+ existingSettings = {};
45302
+ }
45303
+ const LEGACY_PACKAGE_IDS = /* @__PURE__ */ new Set(["npm:@xtrm/pi-extensions", "./extensions/"]);
45304
+ const existingProjectPackages = normalizeStringArray(existingSettings.packages).filter((entry) => !LEGACY_PACKAGE_IDS.has(entry) && !entry.startsWith("./extensions/"));
45305
+ const { kept: existingPackages } = pruneConflictingPiPackageEntries(existingProjectPackages);
45306
+ if (!existingPackages.includes(PROJECT_EXTENSION_PACKAGE_ID)) {
45307
+ existingPackages.push(PROJECT_EXTENSION_PACKAGE_ID);
45308
+ }
45309
+ const existingSkills = normalizeStringArray(existingSettings.skills).filter((entry) => entry !== PROJECT_SKILLS_ENTRY);
45310
+ existingSkills.unshift(PROJECT_SKILLS_ENTRY);
45311
+ const existingExtensions = normalizeStringArray(existingSettings.extensions).filter((entry) => !LEGACY_PROJECT_EXTENSION_ENTRIES.has(entry));
45312
+ const nextSettings = {
45114
45313
  ...existingSettings,
45115
- extensions: [],
45116
- // Empty = Pi uses global ~/.pi/agent/extensions/
45117
- skills: [PROJECT_SKILLS_ENTRY],
45118
- packages: []
45119
- // Empty = packages installed globally at ~/.pi/agent/settings.json
45120
- }, { spaces: 2 });
45121
- log?.(kleur_default.dim(`Updated .pi/settings.json \u2192 global extensions + packages + .xtrm/skills/active/pi`));
45314
+ extensions: existingExtensions,
45315
+ skills: existingSkills,
45316
+ packages: existingPackages
45317
+ };
45318
+ await import_fs_extra9.default.writeJson(piSettingsPath, nextSettings, { spaces: 2 });
45319
+ log?.(kleur_default.dim(`Updated .pi/settings.json \u2192 ${PROJECT_EXTENSION_PACKAGE_ID} + ${PROJECT_SKILLS_ENTRY}`));
45122
45320
  }
45123
45321
  async function executePiSync(plan, sourceDir, targetDir, opts = {}) {
45124
45322
  const {
@@ -45188,13 +45386,16 @@ async function executePiSync(plan, sourceDir, targetDir, opts = {}) {
45188
45386
  continue;
45189
45387
  }
45190
45388
  try {
45191
- const r = (0, import_child_process3.spawnSync)("pi", installArgs, { stdio: "pipe", encoding: "utf8" });
45192
- if (r.status === 0) {
45389
+ const installAttempt = installPiPackageWithFallback(pkg.id, log);
45390
+ if (installAttempt.status === 0) {
45193
45391
  result.packagesInstalled.push(pkg.id);
45194
- log(`${sym.ok} ${pkg.displayName}`);
45392
+ log(`${sym.ok} ${pkg.displayName}${installAttempt.retriedWithNpmjs ? " (npmjs fallback)" : ""}`);
45195
45393
  } else {
45196
45394
  result.failed.push(pkg.id);
45197
45395
  log(kleur_default.yellow(`\u26A0 ${pkg.displayName} \u2014 install failed`));
45396
+ for (const hint of getPiPackageInstallFailureHint(pkg.id, installAttempt.output)) {
45397
+ log(kleur_default.yellow(` \u2192 ${hint}`));
45398
+ }
45198
45399
  }
45199
45400
  } catch (err) {
45200
45401
  result.failed.push(pkg.id);
@@ -45203,114 +45404,20 @@ async function executePiSync(plan, sourceDir, targetDir, opts = {}) {
45203
45404
  }
45204
45405
  return result;
45205
45406
  }
45206
- var EXTENSION_SOURCE_DIR = "ext-src";
45207
- async function linkExtensionsToGlobal(repoRoot, dryRun = false, log = (msg) => console.log(kleur_default.dim(` ${msg}`))) {
45208
- const globalExtDir = import_path4.default.join(PI_AGENT_DIR, "extensions");
45209
- const repoExtDir = import_path4.default.join(repoRoot, ".xtrm", EXTENSION_SOURCE_DIR);
45210
- const linked = [];
45211
- const failed = [];
45212
- if (!await import_fs_extra9.default.pathExists(repoExtDir)) {
45213
- log("No .xtrm/ext-src/ found \u2014 skipping global link");
45214
- return { linked, failed };
45215
- }
45216
- if (dryRun) {
45217
- log("[DRY RUN] would create extension symlinks in ~/.pi/agent/extensions/");
45218
- return { linked, failed };
45219
- }
45220
- await import_fs_extra9.default.ensureDir(globalExtDir);
45221
- const coreNodeModulesDir = import_path4.default.join(globalExtDir, "node_modules", "@xtrm");
45222
- const coreSymlinkPath = import_path4.default.join(coreNodeModulesDir, "pi-core");
45223
- const coreRelativeTarget = import_path4.default.join("..", "..", "core");
45224
- try {
45225
- await import_fs_extra9.default.ensureDir(coreNodeModulesDir);
45226
- const existing = await import_fs_extra9.default.lstat(coreSymlinkPath).catch(() => null);
45227
- if (existing) await import_fs_extra9.default.remove(coreSymlinkPath);
45228
- await import_fs_extra9.default.symlink(coreRelativeTarget, coreSymlinkPath);
45229
- log("\u2713 @xtrm/pi-core \u2192 global node_modules");
45230
- } catch (err) {
45231
- log(kleur_default.yellow(`\u26A0 @xtrm/pi-core symlink: ${err}`));
45232
- }
45233
- const entries = await import_fs_extra9.default.readdir(repoExtDir, { withFileTypes: true });
45234
- for (const entry of entries) {
45235
- if (!entry.isDirectory()) continue;
45236
- if (entry.name === "node_modules") continue;
45237
- const extPath = import_path4.default.join(repoExtDir, entry.name);
45238
- const globalLink = import_path4.default.join(globalExtDir, entry.name);
45239
- try {
45240
- const existing = await import_fs_extra9.default.lstat(globalLink).catch(() => null);
45241
- if (existing) {
45242
- if (existing.isSymbolicLink()) {
45243
- const currentTarget = await import_fs_extra9.default.readlink(globalLink);
45244
- const resolvedTarget = import_path4.default.resolve(import_path4.default.dirname(globalLink), currentTarget);
45245
- if (resolvedTarget === import_path4.default.resolve(extPath)) {
45246
- continue;
45247
- }
45248
- }
45249
- await import_fs_extra9.default.remove(globalLink);
45250
- }
45251
- await import_fs_extra9.default.symlink(extPath, globalLink);
45252
- linked.push(entry.name);
45253
- log(`\u2713 ${entry.name} \u2192 .xtrm/${EXTENSION_SOURCE_DIR}/${entry.name}`);
45254
- } catch (err) {
45255
- failed.push(entry.name);
45256
- log(kleur_default.red(`\u2717 ${entry.name}: ${err}`));
45257
- }
45258
- }
45259
- return { linked, failed };
45260
- }
45261
- async function ensureNpmPackageExtensionSymlinks(log) {
45262
- const os9 = require("os");
45263
- const homeDir = os9.homedir();
45264
- const extensionsDir = import_path4.default.join(homeDir, ".pi", "agent", "extensions");
45265
- await import_fs_extra9.default.ensureDir(extensionsDir);
45266
- const npmPrefix = (0, import_child_process3.spawnSync)("npm", ["prefix", "-g"], { encoding: "utf8" }).stdout.trim();
45267
- const globalNodeModules = import_path4.default.join(npmPrefix, "lib", "node_modules");
45268
- const npmPackages = [
45269
- { packageName: "pi-gitnexus", symlinkName: "gitnexus" },
45270
- { packageName: "pi-serena-tools", symlinkName: "serena" }
45271
- ];
45272
- for (const { packageName, symlinkName } of npmPackages) {
45273
- const packagePath = import_path4.default.join(globalNodeModules, packageName);
45274
- const symlinkPath = import_path4.default.join(extensionsDir, symlinkName);
45275
- const packageExists = await import_fs_extra9.default.pathExists(packagePath);
45276
- if (!packageExists) {
45277
- log?.(kleur_default.yellow(` \u26A0 ${packageName} not found in ${globalNodeModules}, skipping symlink`));
45278
- continue;
45279
- }
45280
- const symlinkExists = await import_fs_extra9.default.lstat(symlinkPath).catch(() => null);
45281
- if (symlinkExists?.isSymbolicLink()) {
45282
- const currentTarget = await import_fs_extra9.default.readlink(symlinkPath);
45283
- const resolvedTarget = import_path4.default.resolve(extensionsDir, currentTarget);
45284
- if (resolvedTarget === packagePath) {
45285
- log?.(kleur_default.dim(` \u2713 ${symlinkName} symlink already correct`));
45286
- continue;
45287
- }
45288
- log?.(kleur_default.dim(` Removing stale ${symlinkName} symlink`));
45289
- await import_fs_extra9.default.remove(symlinkPath);
45290
- } else if (symlinkExists) {
45291
- log?.(kleur_default.dim(` Removing stale ${symlinkName} (not a symlink)`));
45292
- await import_fs_extra9.default.remove(symlinkPath);
45293
- }
45294
- const relativeTarget = import_path4.default.relative(extensionsDir, packagePath);
45295
- await import_fs_extra9.default.symlink(relativeTarget, symlinkPath);
45296
- log?.(kleur_default.dim(` Created ${symlinkName} symlink \u2192 ${relativeTarget}`));
45297
- }
45298
- }
45299
45407
  async function runPiRuntimeSync(opts = {}) {
45300
45408
  const { dryRun = false, isGlobal = false, projectRoot } = opts;
45301
45409
  const pkgRoot = resolvePkgRoot();
45302
- const sourceDir = import_path4.default.join(pkgRoot, ".xtrm", "extensions");
45410
+ const sourceDir = resolveManagedPiExtensionsSourceDir(pkgRoot);
45303
45411
  const resolvedProjectRoot = projectRoot || process.cwd();
45304
45412
  const log = (msg) => console.log(kleur_default.dim(` ${msg}`));
45305
- const emptyResult = {
45413
+ const result = {
45306
45414
  extensionsAdded: [],
45307
45415
  extensionsUpdated: [],
45308
45416
  extensionsRemoved: [],
45309
45417
  packagesInstalled: [],
45310
45418
  failed: []
45311
45419
  };
45312
- const result = { ...emptyResult };
45313
- if (!await import_fs_extra9.default.pathExists(sourceDir)) {
45420
+ if (!sourceDir || !await import_fs_extra9.default.pathExists(sourceDir)) {
45314
45421
  console.log(kleur_default.dim("\n Managed extensions: skipped (not bundled in npm package)\n"));
45315
45422
  return result;
45316
45423
  }
@@ -45318,6 +45425,7 @@ async function runPiRuntimeSync(opts = {}) {
45318
45425
  if (preflight.staleOverride.remediated) {
45319
45426
  result.extensionsRemoved.push("pi-mcp-adapter");
45320
45427
  }
45428
+ await cleanupConflictingPiPackageSettings(resolvedProjectRoot, dryRun, log);
45321
45429
  if (isGlobal) {
45322
45430
  const targetDir = import_path4.default.join(PI_AGENT_DIR, "extensions");
45323
45431
  const plan = await inventoryPiRuntime(sourceDir, targetDir);
@@ -45330,32 +45438,47 @@ async function runPiRuntimeSync(opts = {}) {
45330
45438
  });
45331
45439
  return mergePiSyncResults(result, synced);
45332
45440
  }
45333
- const linkResult = await linkExtensionsToGlobal(resolvedProjectRoot, dryRun, log);
45334
- result.extensionsAdded.push(...linkResult.linked);
45335
- result.failed.push(...linkResult.failed);
45336
- await ensureNpmPackageExtensionSymlinks(log);
45337
45441
  const installedPkgIds = getInstalledPiPackages();
45338
- const packageStatuses = [];
45339
- const missingPackages = [];
45340
- for (const pkg of MANAGED_PACKAGES) {
45341
- const isInstalled = installedPkgIds.includes(pkg.id);
45342
- const status = { pkg, installed: isInstalled };
45343
- packageStatuses.push(status);
45344
- if (!isInstalled) missingPackages.push(status);
45345
- }
45442
+ const packageStatuses = getProjectRequiredPackageStatuses(installedPkgIds);
45443
+ const missingPackages = packageStatuses.filter((status) => !status.installed);
45346
45444
  console.log(kleur_default.bold("\n Pi Runtime"));
45347
45445
  console.log(kleur_default.dim(" " + "-".repeat(50)));
45348
- console.log(kleur_default.dim(` Extensions: ~/.pi/agent/extensions/ \u2192 .xtrm/${EXTENSION_SOURCE_DIR}/`));
45349
- const pkgOk = packageStatuses.filter((s) => s.installed).length;
45446
+ const extensionPackageInstalled = packageStatuses.some(
45447
+ (status) => status.pkg.id === PROJECT_EXTENSION_PACKAGE_ID && status.installed
45448
+ );
45449
+ console.log(kleur_default.dim(` Extensions: ${extensionPackageInstalled ? "package installed" : "package missing"} (${PROJECT_EXTENSION_PACKAGE_ID})`));
45450
+ const pkgOk = packageStatuses.filter((status) => status.installed).length;
45350
45451
  console.log(kleur_default.dim(` Packages: ${pkgOk}/${packageStatuses.length} installed`));
45351
45452
  if (missingPackages.length > 0) {
45352
- const names = missingPackages.map((s) => s.pkg.displayName).join(", ");
45453
+ const names = missingPackages.map((status) => status.pkg.displayName).join(", ");
45353
45454
  console.log(kleur_default.yellow(` Missing: ${names}`));
45354
45455
  }
45355
45456
  console.log(kleur_default.dim(" " + "-".repeat(50)));
45356
45457
  const legacyCleanup = await cleanupLegacyProjectExtensionCopies(resolvedProjectRoot, dryRun, log);
45357
45458
  result.extensionsRemoved.push(...legacyCleanup.removed);
45358
45459
  result.failed.push(...legacyCleanup.failed);
45460
+ const globalExtDir = import_path4.default.join(PI_AGENT_DIR, "extensions");
45461
+ if (await import_fs_extra9.default.pathExists(globalExtDir)) {
45462
+ const MANAGED_EXT_IDS = new Set(MANAGED_EXTENSIONS.map((e) => e.id));
45463
+ const STALE_SYMLINKS = /* @__PURE__ */ new Set([...MANAGED_EXT_IDS, "core", "gitnexus", "serena"]);
45464
+ const globalEntries = await import_fs_extra9.default.readdir(globalExtDir, { withFileTypes: true });
45465
+ for (const entry of globalEntries) {
45466
+ if (entry.isSymbolicLink() && STALE_SYMLINKS.has(entry.name)) {
45467
+ if (!dryRun) {
45468
+ await import_fs_extra9.default.remove(import_path4.default.join(globalExtDir, entry.name));
45469
+ }
45470
+ result.extensionsRemoved.push(entry.name);
45471
+ log(`Removed stale global symlink: ${entry.name}`);
45472
+ }
45473
+ }
45474
+ const staleNodeModules = import_path4.default.join(globalExtDir, "node_modules");
45475
+ if (await import_fs_extra9.default.pathExists(staleNodeModules)) {
45476
+ if (!dryRun) {
45477
+ await import_fs_extra9.default.remove(staleNodeModules);
45478
+ }
45479
+ log("Removed stale global extensions/node_modules");
45480
+ }
45481
+ }
45359
45482
  for (const status of missingPackages) {
45360
45483
  const { pkg } = status;
45361
45484
  if (dryRun) {
@@ -45363,13 +45486,16 @@ async function runPiRuntimeSync(opts = {}) {
45363
45486
  continue;
45364
45487
  }
45365
45488
  try {
45366
- const r = (0, import_child_process3.spawnSync)("pi", ["install", pkg.id], { stdio: "pipe", encoding: "utf8" });
45367
- if (r.status === 0) {
45489
+ const installAttempt = installPiPackageWithFallback(pkg.id, log);
45490
+ if (installAttempt.status === 0) {
45368
45491
  result.packagesInstalled.push(pkg.id);
45369
- log(`${sym.ok} ${pkg.displayName}`);
45370
- } else {
45371
- result.failed.push(pkg.id);
45372
- log(kleur_default.yellow(`\u26A0 ${pkg.displayName} \u2014 install failed`));
45492
+ log(`${sym.ok} ${pkg.displayName}${installAttempt.retriedWithNpmjs ? " (npmjs fallback)" : ""}`);
45493
+ continue;
45494
+ }
45495
+ result.failed.push(pkg.id);
45496
+ log(kleur_default.yellow(`\u26A0 ${pkg.displayName} \u2014 install failed`));
45497
+ for (const hint of getPiPackageInstallFailureHint(pkg.id, installAttempt.output)) {
45498
+ log(kleur_default.yellow(` \u2192 ${hint}`));
45373
45499
  }
45374
45500
  } catch (err) {
45375
45501
  result.failed.push(pkg.id);
@@ -45391,12 +45517,8 @@ async function runPiRuntimeSync(opts = {}) {
45391
45517
  }
45392
45518
  }
45393
45519
  await updatePiSettings(resolvedProjectRoot, dryRun, log);
45394
- const requiredFailed = missingPackages.filter(
45395
- (s) => s.pkg.required && result.failed.includes(s.pkg.id)
45396
- );
45397
- if (missingPackages.length === 0 || result.failed.length === 0) {
45398
- console.log(t.success(" \u2713 All required items present.\n"));
45399
- } else if (requiredFailed.length === 0) {
45520
+ const requiredFailed = missingPackages.filter((status) => status.pkg.required && result.failed.includes(status.pkg.id));
45521
+ if (requiredFailed.length === 0) {
45400
45522
  console.log(t.success(" \u2713 All required items present.\n"));
45401
45523
  } else {
45402
45524
  console.log(kleur_default.yellow(" \u26A0 Missing required items.\n"));
@@ -45633,21 +45755,21 @@ function hasSettingsEntry(entries, expectedEntry) {
45633
45755
  });
45634
45756
  }
45635
45757
  async function getPiProjectPointer(projectRoot) {
45636
- const projectExtensionsDir = import_path7.default.join(projectRoot, ".xtrm", "extensions");
45637
45758
  const settingsPath = import_path7.default.join(projectRoot, ".pi", "settings.json");
45638
- const hasProjectExtensions = await import_fs_extra11.default.pathExists(projectExtensionsDir);
45639
45759
  const hasSettingsFile = await import_fs_extra11.default.pathExists(settingsPath);
45640
45760
  if (!hasSettingsFile) {
45641
- return { hasProjectExtensions, pointsToXtrmExtensions: false };
45761
+ return { hasProjectSettings: false, hasProjectExtensionPackage: false, pointsToXtrmExtensions: false };
45642
45762
  }
45643
45763
  try {
45644
45764
  const settings = await import_fs_extra11.default.readJson(settingsPath);
45765
+ const packageEntries = Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
45645
45766
  return {
45646
- hasProjectExtensions,
45767
+ hasProjectSettings: true,
45768
+ hasProjectExtensionPackage: packageEntries.includes("npm:@jaggerxtrm/pi-extensions"),
45647
45769
  pointsToXtrmExtensions: hasSettingsEntry(settings.extensions, "../.xtrm/extensions")
45648
45770
  };
45649
45771
  } catch {
45650
- return { hasProjectExtensions, pointsToXtrmExtensions: false };
45772
+ return { hasProjectSettings: true, hasProjectExtensionPackage: false, pointsToXtrmExtensions: false };
45651
45773
  }
45652
45774
  }
45653
45775
  function createPiCommand() {
@@ -45671,31 +45793,29 @@ function createPiCommand() {
45671
45793
  const projectRoot = resolveProjectRoot();
45672
45794
  const pointer = await getPiProjectPointer(projectRoot);
45673
45795
  const bundleRoot = await findRepoRoot();
45674
- const sourceDir = import_path7.default.join(bundleRoot, ".xtrm", "extensions");
45796
+ const sourceDir = resolveManagedPiExtensionsSourceDir(bundleRoot);
45675
45797
  const globalTargetDir = import_path7.default.join(PI_AGENT_DIR4, "extensions");
45676
- if (!await import_fs_extra11.default.pathExists(sourceDir)) {
45798
+ if (!sourceDir || !await import_fs_extra11.default.pathExists(sourceDir)) {
45677
45799
  console.log(kleur_default.dim(" \u25CB managed extensions not bundled in this install\n"));
45678
45800
  return;
45679
45801
  }
45680
45802
  const plan = await inventoryPiRuntime(sourceDir, globalTargetDir);
45681
45803
  const pkgOk = plan.packages.filter((s) => s.installed).length;
45682
- if (pointer.hasProjectExtensions && pointer.pointsToXtrmExtensions) {
45804
+ const projectScoped = pointer.hasProjectExtensionPackage || pointer.pointsToXtrmExtensions;
45805
+ if (projectScoped) {
45683
45806
  console.log(kleur_default.dim(" Scope: project"));
45684
- console.log(kleur_default.dim(" Extensions: via .xtrm/extensions (.pi/settings.json)"));
45807
+ console.log(kleur_default.dim(` Extensions: package mode (npm:@jaggerxtrm/pi-extensions${pointer.hasProjectExtensionPackage ? "" : " missing"})`));
45685
45808
  } else {
45686
45809
  console.log(kleur_default.dim(" Scope: global"));
45687
45810
  const extOk = plan.extensions.filter((s) => s.installed && !s.stale).length;
45688
45811
  console.log(kleur_default.dim(` Extensions: ${extOk}/${plan.extensions.length} up-to-date`));
45689
45812
  }
45690
45813
  console.log(kleur_default.dim(` Packages: ${pkgOk}/${plan.packages.length} installed`));
45691
- if (pointer.hasProjectExtensions && !pointer.pointsToXtrmExtensions) {
45692
- console.log(kleur_default.yellow(" Settings: .pi/settings.json is not pointing to ../.xtrm/extensions"));
45693
- }
45694
45814
  if (plan.missingPackages.length > 0) {
45695
45815
  const names = plan.missingPackages.map((s) => s.pkg.displayName).join(", ");
45696
45816
  console.log(kleur_default.yellow(` Packages: ${names}`));
45697
45817
  }
45698
- if (!pointer.pointsToXtrmExtensions) {
45818
+ if (!projectScoped) {
45699
45819
  if (plan.missingExtensions.length > 0) {
45700
45820
  const names = plan.missingExtensions.map((s) => s.ext.displayName).join(", ");
45701
45821
  console.log(kleur_default.yellow(` Missing: ${names}`));
@@ -45708,13 +45828,16 @@ function createPiCommand() {
45708
45828
  console.log(kleur_default.red(` Orphaned: ${plan.orphanedExtensions.join(", ")}`));
45709
45829
  }
45710
45830
  }
45711
- const projectModeOk = pointer.hasProjectExtensions ? pointer.pointsToXtrmExtensions : true;
45712
- const hasGlobalDrift = !pointer.pointsToXtrmExtensions && !plan.allPresent;
45831
+ const hasProjectSettingsDrift = !pointer.hasProjectSettings || !pointer.hasProjectExtensionPackage;
45832
+ const hasGlobalDrift = !projectScoped && !plan.allPresent;
45713
45833
  const hasPackageDrift = plan.missingPackages.length > 0;
45714
- if (projectModeOk && !hasGlobalDrift && !hasPackageDrift) {
45834
+ if (!hasProjectSettingsDrift && !hasGlobalDrift && !hasPackageDrift) {
45715
45835
  console.log(t.success("\n \u2713 Pi runtime configuration looks healthy\n"));
45716
45836
  return;
45717
45837
  }
45838
+ if (hasProjectSettingsDrift) {
45839
+ console.log(kleur_default.yellow(" Settings: .pi/settings.json missing managed npm:@jaggerxtrm/pi-extensions entry"));
45840
+ }
45718
45841
  console.log(kleur_default.dim("\n \u2192 run: xt pi reload\n"));
45719
45842
  });
45720
45843
  cmd.command("doctor").description("Diagnostic checks: pi installed, extensions deployed, packages present, orphaned extensions").action(async () => {
@@ -45745,7 +45868,8 @@ function createPiCommand() {
45745
45868
  const projectRoot = resolveProjectRoot();
45746
45869
  const pointer = await getPiProjectPointer(projectRoot);
45747
45870
  const bundleRoot = await findRepoRoot();
45748
- const sourceDir = import_path7.default.join(bundleRoot, ".xtrm", "extensions");
45871
+ const sourceDir = resolveManagedPiExtensionsSourceDir(bundleRoot);
45872
+ const coreSourceDir = resolveManagedPiCoreSourceDir(bundleRoot);
45749
45873
  const globalTargetDir = import_path7.default.join(PI_AGENT_DIR4, "extensions");
45750
45874
  try {
45751
45875
  const staleOverride = await remediateStalePiMcpAdapterOverride(false);
@@ -45762,7 +45886,7 @@ function createPiCommand() {
45762
45886
  allOk = false;
45763
45887
  }
45764
45888
  try {
45765
- const coreStatus = await ensureCorePackageSymlink(import_path7.default.join(sourceDir, "core"), projectRoot, false);
45889
+ const coreStatus = coreSourceDir ? await ensureCorePackageSymlink(coreSourceDir, projectRoot, false) : "missing-source";
45766
45890
  if (coreStatus === "repaired" || coreStatus === "created") {
45767
45891
  console.log(t.success(" \u2713 repaired .xtrm/extensions/node_modules/@xtrm/pi-core symlink"));
45768
45892
  } else if (coreStatus === "ok") {
@@ -45774,15 +45898,21 @@ function createPiCommand() {
45774
45898
  console.log(kleur_default.yellow(` \u26A0 failed to ensure @xtrm/pi-core symlink: ${error48}`));
45775
45899
  allOk = false;
45776
45900
  }
45777
- if (!await import_fs_extra11.default.pathExists(sourceDir)) {
45901
+ if (!sourceDir || !await import_fs_extra11.default.pathExists(sourceDir)) {
45778
45902
  console.log(kleur_default.dim(" \u25CB managed extensions not bundled in this install"));
45779
45903
  } else {
45780
45904
  const plan = await inventoryPiRuntime(sourceDir, globalTargetDir);
45781
- if (pointer.hasProjectExtensions && pointer.pointsToXtrmExtensions) {
45782
- console.log(t.success(" \u2713 project runtime points to .xtrm/extensions"));
45783
- } else if (pointer.hasProjectExtensions) {
45784
- console.log(kleur_default.yellow(" \u26A0 .pi/settings.json does not include ../.xtrm/extensions"));
45905
+ const projectScoped = pointer.hasProjectExtensionPackage || pointer.pointsToXtrmExtensions;
45906
+ if (!pointer.hasProjectSettings) {
45907
+ console.log(kleur_default.yellow(" \u26A0 missing .pi/settings.json; run xt pi reload to bootstrap project Pi settings"));
45785
45908
  allOk = false;
45909
+ } else if (projectScoped) {
45910
+ if (pointer.hasProjectExtensionPackage) {
45911
+ console.log(t.success(" \u2713 project runtime uses npm:@jaggerxtrm/pi-extensions"));
45912
+ } else {
45913
+ console.log(kleur_default.yellow(" \u26A0 legacy project extension pointer detected; run xt pi reload to migrate"));
45914
+ allOk = false;
45915
+ }
45786
45916
  } else if (plan.missingExtensions.length === 0 && plan.staleExtensions.length === 0 && plan.orphanedExtensions.length === 0) {
45787
45917
  console.log(t.success(` \u2713 global extensions deployed (${plan.extensions.length})`));
45788
45918
  } else {
@@ -45997,12 +46127,16 @@ async function installFromRegistry(params) {
45997
46127
  installed: 0,
45998
46128
  upToDate: drift.upToDate.length,
45999
46129
  driftedSkipped: drift.drifted.length,
46000
- forced: 0
46130
+ forced: 0,
46131
+ expectedInstalls: 0,
46132
+ missingSourceSkipped: 0
46001
46133
  };
46002
46134
  }
46003
46135
  }
46004
46136
  let installed = 0;
46005
46137
  let forced = 0;
46138
+ let expectedInstalls = 0;
46139
+ let missingSourceSkipped = 0;
46006
46140
  for (const asset of Object.values(registry2.assets)) {
46007
46141
  for (const [filePath] of Object.entries(asset.files)) {
46008
46142
  const relativePath = toUserRelativePath(asset.source_dir, filePath);
@@ -46025,6 +46159,13 @@ async function installFromRegistry(params) {
46025
46159
  if (isDrifted && force) {
46026
46160
  forced += 1;
46027
46161
  }
46162
+ expectedInstalls += 1;
46163
+ const sourceExists = await import_fs_extra13.default.pathExists(sourcePath);
46164
+ if (!sourceExists) {
46165
+ missingSourceSkipped += 1;
46166
+ console.log(kleur_default.yellow(` \u26A0 Skipping missing source file: ${toPosix2(import_path8.default.relative(packageRoot, sourcePath))}`));
46167
+ continue;
46168
+ }
46028
46169
  if (dryRun) {
46029
46170
  const action = isDrifted ? "overwrite" : "install";
46030
46171
  console.log(kleur_default.dim(` [DRY RUN] would ${action} ${relativePath}`));
@@ -46040,7 +46181,9 @@ async function installFromRegistry(params) {
46040
46181
  installed,
46041
46182
  upToDate: upToDateSet.size,
46042
46183
  driftedSkipped: force ? 0 : driftedSet.size,
46043
- forced
46184
+ forced,
46185
+ expectedInstalls,
46186
+ missingSourceSkipped
46044
46187
  };
46045
46188
  }
46046
46189
 
@@ -46558,9 +46701,8 @@ function verifyClaudeRuntime(projectRoot) {
46558
46701
  return fallbackResult;
46559
46702
  }
46560
46703
  async function verifyPiRuntime(projectRoot) {
46561
- const pkgRoot = resolvePkgRoot2();
46562
- const sourceDir = import_path10.default.join(pkgRoot, ".xtrm", "extensions");
46563
- if (!await import_fs_extra16.default.pathExists(sourceDir)) {
46704
+ const sourceDir = resolveManagedPiExtensionsSourceDir();
46705
+ if (!sourceDir || !await import_fs_extra16.default.pathExists(sourceDir)) {
46564
46706
  return {
46565
46707
  extensions: [],
46566
46708
  packages: [],
@@ -46586,16 +46728,6 @@ ${gnStatus.stderr ?? ""}`.toLowerCase();
46586
46728
  const instructionHeaders = agentsMd || claudeMd;
46587
46729
  return { beadsInitialized, gitnexusIndexed, instructionHeaders };
46588
46730
  }
46589
- function resolvePkgRoot2() {
46590
- const candidates = [
46591
- import_path10.default.resolve(__dirname, "../.."),
46592
- import_path10.default.resolve(__dirname, "../../..")
46593
- ];
46594
- for (const c of candidates) {
46595
- if (import_fs_extra16.default.existsSync(import_path10.default.join(c, ".xtrm", "extensions"))) return c;
46596
- }
46597
- return candidates[0];
46598
- }
46599
46731
  async function runInitVerification(projectRoot) {
46600
46732
  const machinePlan = verifyMachineBootstrap();
46601
46733
  const claudeResult = verifyClaudeRuntime(projectRoot);
@@ -48617,7 +48749,7 @@ async function runProjectInit(opts = {}) {
48617
48749
  const userXtrmDir = ctx.targets[0];
48618
48750
  const registryPath = import_path15.default.join(packageRoot, ".xtrm", "registry.json");
48619
48751
  const registry2 = await import_fs_extra21.default.readJson(registryPath);
48620
- await installFromRegistry({
48752
+ const registryInstallStats = await installFromRegistry({
48621
48753
  packageRoot,
48622
48754
  registry: registry2,
48623
48755
  userXtrmDir,
@@ -48625,6 +48757,10 @@ async function runProjectInit(opts = {}) {
48625
48757
  force: false,
48626
48758
  yes: true
48627
48759
  });
48760
+ if (registryInstallStats.missingSourceSkipped > 0) {
48761
+ console.log(kleur_default.yellow(` \u26A0 Registry/source mismatch: skipped ${registryInstallStats.missingSourceSkipped} missing source file${registryInstallStats.missingSourceSkipped === 1 ? "" : "s"}.`));
48762
+ console.log(kleur_default.yellow(" Init continued, but some skills/files may be absent until registry payload is corrected."));
48763
+ }
48628
48764
  await scaffoldSkillsDefaultFromPackage({ packageRoot, userXtrmDir, dryRun: false });
48629
48765
  const mcpSync = await syncProjectMcpConfig(projectRoot, { preserveExistingFile: true });
48630
48766
  if (mcpSync.wroteFile) {