@cubis/foundry 0.3.55 → 0.3.56

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.3.56] - 2026-03-05
6
+
7
+ ### Fixed
8
+
9
+ - Improved `cbx remove all` cleanup coverage:
10
+ - removes CBX-managed skills from all configured profile path candidates (not just a single preferred path)
11
+ - includes legacy Codex skill path cleanup (`~/.codex/skills`) for managed artifacts
12
+ - removes empty global CBX roots (`~/.cbx`, `~/.agents`) after cleanup when eligible.
13
+ - Ensured global remove flow clears CBX-managed profile/config artifacts consistently (`~/.cbx/cbx_config.json`, `~/.cbx/state.json`, `~/.cbx/credentials.env` when present).
14
+
5
15
  ## [0.3.55] - 2026-03-05
6
16
 
7
17
  ### Added
package/dist/cli/core.js CHANGED
@@ -2246,6 +2246,45 @@ async function resolveProfilePaths(profileId, scope, cwd = process.cwd()) {
2246
2246
  ruleFilesByPriority: cfg.ruleFilesByPriority.map((filePath) => expandPath(filePath, cwd)),
2247
2247
  };
2248
2248
  }
2249
+ function expandUniquePaths(pathList, cwd = process.cwd()) {
2250
+ const seen = new Set();
2251
+ const output = [];
2252
+ for (const value of pathList || []) {
2253
+ const normalized = String(value || "").trim();
2254
+ if (!normalized)
2255
+ continue;
2256
+ const expanded = expandPath(normalized, cwd);
2257
+ const key = path.resolve(expanded);
2258
+ if (seen.has(key))
2259
+ continue;
2260
+ seen.add(key);
2261
+ output.push(expanded);
2262
+ }
2263
+ return output;
2264
+ }
2265
+ function resolveProfilePathCandidates(profileId, scope, cwd = process.cwd()) {
2266
+ const profile = WORKFLOW_PROFILES[profileId];
2267
+ if (!profile)
2268
+ throw new Error(`Unknown platform '${profileId}'.`);
2269
+ const cfg = profile[scope];
2270
+ return {
2271
+ workflowsDirs: expandUniquePaths(cfg.workflowDirs, cwd),
2272
+ agentsDirs: expandUniquePaths(cfg.agentDirs, cwd),
2273
+ skillsDirs: expandUniquePaths(cfg.skillDirs, cwd),
2274
+ commandsDirs: expandUniquePaths(cfg.commandDirs, cwd),
2275
+ promptsDirs: expandUniquePaths(cfg.promptDirs, cwd),
2276
+ ruleFilesByPriority: expandUniquePaths(cfg.ruleFilesByPriority, cwd),
2277
+ };
2278
+ }
2279
+ function resolveLegacySkillDirsForCleanup(platform, scope, cwd = process.cwd()) {
2280
+ if (platform !== "codex")
2281
+ return [];
2282
+ if (scope === "global") {
2283
+ return [path.join(os.homedir(), ".codex", "skills")];
2284
+ }
2285
+ const workspaceRoot = findWorkspaceRoot(cwd);
2286
+ return [path.join(workspaceRoot, ".codex", "skills")];
2287
+ }
2249
2288
  async function resolveArtifactProfilePaths(profileId, scope, cwd = process.cwd()) {
2250
2289
  const scopedPaths = await resolveProfilePaths(profileId, scope, cwd);
2251
2290
  if (scope !== "global")
@@ -6480,6 +6519,35 @@ async function removePathRecord({ targetPath, category, dryRun = false, records,
6480
6519
  });
6481
6520
  }
6482
6521
  }
6522
+ async function removeEmptyDirectoryRecord({ dirPath, category, dryRun = false, records, }) {
6523
+ if (!dirPath)
6524
+ return;
6525
+ if (!(await pathExists(dirPath)))
6526
+ return;
6527
+ let entries = [];
6528
+ try {
6529
+ entries = await readdir(dirPath);
6530
+ }
6531
+ catch {
6532
+ return;
6533
+ }
6534
+ if (entries.length > 0)
6535
+ return;
6536
+ if (dryRun) {
6537
+ records.push({
6538
+ path: dirPath,
6539
+ category,
6540
+ action: "would-remove",
6541
+ });
6542
+ return;
6543
+ }
6544
+ await rm(dirPath, { recursive: true, force: true });
6545
+ records.push({
6546
+ path: dirPath,
6547
+ category,
6548
+ action: "removed",
6549
+ });
6550
+ }
6483
6551
  async function removeMcpRuntimeEntriesJson({ filePath, keyName, serverIds, dryRun = false, }) {
6484
6552
  if (!(await pathExists(filePath))) {
6485
6553
  return { path: filePath, action: "missing", warnings: [] };
@@ -6617,6 +6685,7 @@ async function runWorkflowRemoveAll(options) {
6617
6685
  const scopes = resolveRemoveAllScopes(opts.scope);
6618
6686
  const platforms = resolveRemoveAllPlatforms(opts.platform);
6619
6687
  const bundleIds = await listBundleIds();
6688
+ const knownTopLevelSkillIds = await listTopLevelSkillIdsFromRoot(workflowSkillsRoot());
6620
6689
  if (!dryRun && !opts.yes && process.stdin.isTTY) {
6621
6690
  const proceed = await confirm({
6622
6691
  message: `Remove ALL CBX-managed artifacts for platforms [${platforms.join(", ")}] and scopes [${scopes.join(", ")}]?`,
@@ -6633,7 +6702,20 @@ async function runWorkflowRemoveAll(options) {
6633
6702
  const warnings = [];
6634
6703
  for (const scope of scopes) {
6635
6704
  for (const platform of platforms) {
6636
- const artifactProfilePaths = await resolveArtifactProfilePaths(platform, scope, cwd);
6705
+ const artifactProfilePaths = await resolveProfilePaths(platform, scope, cwd);
6706
+ const profileCandidates = resolveProfilePathCandidates(platform, scope, cwd);
6707
+ const legacySkillDirs = resolveLegacySkillDirsForCleanup(platform, scope, cwd);
6708
+ const allSkillDirs = expandUniquePaths([...profileCandidates.skillsDirs, ...legacySkillDirs], cwd);
6709
+ const isPrimaryDir = (candidateDir, primaryDir) => {
6710
+ if (!candidateDir || !primaryDir)
6711
+ return false;
6712
+ return path.resolve(candidateDir) === path.resolve(primaryDir);
6713
+ };
6714
+ const alternateWorkflowsDirs = profileCandidates.workflowsDirs.filter((dirPath) => !isPrimaryDir(dirPath, artifactProfilePaths.workflowsDir));
6715
+ const alternateAgentsDirs = profileCandidates.agentsDirs.filter((dirPath) => !isPrimaryDir(dirPath, artifactProfilePaths.agentsDir));
6716
+ const alternateCommandsDirs = profileCandidates.commandsDirs.filter((dirPath) => !isPrimaryDir(dirPath, artifactProfilePaths.commandsDir));
6717
+ const alternatePromptsDirs = profileCandidates.promptsDirs.filter((dirPath) => !isPrimaryDir(dirPath, artifactProfilePaths.promptsDir));
6718
+ const alternateSkillsDirs = allSkillDirs.filter((dirPath) => !isPrimaryDir(dirPath, artifactProfilePaths.skillsDir));
6637
6719
  for (const bundleId of bundleIds) {
6638
6720
  const manifest = await readBundleManifest(bundleId);
6639
6721
  const removedBundle = await removeBundleArtifacts({
@@ -6652,19 +6734,91 @@ async function runWorkflowRemoveAll(options) {
6652
6734
  action: dryRun ? "would-remove" : "removed",
6653
6735
  });
6654
6736
  }
6655
- }
6656
- const extraSkillPaths = [
6657
- path.join(artifactProfilePaths.skillsDir || "", POSTMAN_SKILL_ID),
6658
- path.join(artifactProfilePaths.skillsDir || "", STITCH_SKILL_ID),
6659
- path.join(artifactProfilePaths.skillsDir || "", "skills_index.json"),
6660
- ];
6661
- for (const extraPath of extraSkillPaths) {
6662
- await removePathRecord({
6663
- targetPath: extraPath,
6664
- category: `${platform}/${scope}/extra-skill`,
6665
- dryRun,
6666
- records: removedRecords,
6737
+ const platformSpec = manifest.platforms?.[platform];
6738
+ if (!platformSpec)
6739
+ continue;
6740
+ const workflowFiles = (platformSpec.workflows || []).map((entry) => path.basename(entry));
6741
+ const agentFiles = (platformSpec.agents || []).map((entry) => path.basename(entry));
6742
+ const commandFiles = (platformSpec.commands || []).map((entry) => path.basename(entry));
6743
+ const promptFiles = (platformSpec.prompts || []).map((entry) => path.basename(entry));
6744
+ const skillIds = await resolveInstallSkillIds({
6745
+ platformSpec,
6746
+ extraSkillIds: [],
6667
6747
  });
6748
+ const wrapperSkillIds = platform === "codex" ? buildCodexWrapperSkillIds(platformSpec) : [];
6749
+ const bundleSkillIds = [...new Set([...skillIds, ...wrapperSkillIds])];
6750
+ for (const workflowsDir of alternateWorkflowsDirs) {
6751
+ for (const workflowFile of workflowFiles) {
6752
+ await removePathRecord({
6753
+ targetPath: path.join(workflowsDir, workflowFile),
6754
+ category: `${platform}/${scope}/bundle-alt`,
6755
+ dryRun,
6756
+ records: removedRecords,
6757
+ });
6758
+ }
6759
+ }
6760
+ for (const agentsDir of alternateAgentsDirs) {
6761
+ for (const agentFile of agentFiles) {
6762
+ await removePathRecord({
6763
+ targetPath: path.join(agentsDir, agentFile),
6764
+ category: `${platform}/${scope}/bundle-alt`,
6765
+ dryRun,
6766
+ records: removedRecords,
6767
+ });
6768
+ }
6769
+ }
6770
+ for (const commandsDir of alternateCommandsDirs) {
6771
+ for (const commandFile of commandFiles) {
6772
+ await removePathRecord({
6773
+ targetPath: path.join(commandsDir, commandFile),
6774
+ category: `${platform}/${scope}/bundle-alt`,
6775
+ dryRun,
6776
+ records: removedRecords,
6777
+ });
6778
+ }
6779
+ }
6780
+ for (const promptsDir of alternatePromptsDirs) {
6781
+ for (const promptFile of promptFiles) {
6782
+ await removePathRecord({
6783
+ targetPath: path.join(promptsDir, promptFile),
6784
+ category: `${platform}/${scope}/bundle-alt`,
6785
+ dryRun,
6786
+ records: removedRecords,
6787
+ });
6788
+ }
6789
+ }
6790
+ for (const skillsDir of alternateSkillsDirs) {
6791
+ for (const skillId of bundleSkillIds) {
6792
+ await removePathRecord({
6793
+ targetPath: path.join(skillsDir, skillId),
6794
+ category: `${platform}/${scope}/bundle-alt`,
6795
+ dryRun,
6796
+ records: removedRecords,
6797
+ });
6798
+ }
6799
+ }
6800
+ }
6801
+ for (const skillsDir of allSkillDirs) {
6802
+ for (const entry of [
6803
+ POSTMAN_SKILL_ID,
6804
+ STITCH_SKILL_ID,
6805
+ "skills_index.json",
6806
+ ]) {
6807
+ await removePathRecord({
6808
+ targetPath: path.join(skillsDir, entry),
6809
+ category: `${platform}/${scope}/extra-skill`,
6810
+ dryRun,
6811
+ records: removedRecords,
6812
+ });
6813
+ }
6814
+ for (const skillId of knownTopLevelSkillIds) {
6815
+ await removePathRecord({
6816
+ targetPath: path.join(skillsDir, skillId),
6817
+ category: `${platform}/${scope}/known-skill`,
6818
+ dryRun,
6819
+ records: removedRecords,
6820
+ });
6821
+ }
6668
6822
  }
6669
6823
  if (platform === "antigravity") {
6670
6824
  const terminalCleanup = await cleanupAntigravityTerminalIntegration({
@@ -6710,6 +6864,21 @@ async function runWorkflowRemoveAll(options) {
6710
6864
  warnings.push(...runtimeResult.warnings);
6711
6865
  }
6712
6866
  }
6867
+ for (const managedDir of expandUniquePaths([
6868
+ ...profileCandidates.skillsDirs,
6869
+ ...profileCandidates.agentsDirs,
6870
+ ...profileCandidates.workflowsDirs,
6871
+ ...profileCandidates.commandsDirs,
6872
+ ...profileCandidates.promptsDirs,
6873
+ ...legacySkillDirs,
6874
+ ], cwd)) {
6875
+ await removeEmptyDirectoryRecord({
6876
+ dirPath: managedDir,
6877
+ category: `${platform}/${scope}/managed-dir-empty`,
6878
+ dryRun,
6879
+ records: removedRecords,
6880
+ });
6881
+ }
6713
6882
  const scopedProfilePaths = await resolveProfilePaths(platform, scope, cwd);
6714
6883
  const ruleCandidates = [...scopedProfilePaths.ruleFilesByPriority];
6715
6884
  if (scope === "global") {
@@ -6772,7 +6941,7 @@ async function runWorkflowRemoveAll(options) {
6772
6941
  records: removedRecords,
6773
6942
  });
6774
6943
  }
6775
- else if (includeCredentials) {
6944
+ else if (scope === "global" || includeCredentials) {
6776
6945
  await removePathRecord({
6777
6946
  targetPath: resolveManagedCredentialsEnvPath(),
6778
6947
  category: "global/credentials",
@@ -6780,6 +6949,19 @@ async function runWorkflowRemoveAll(options) {
6780
6949
  records: removedRecords,
6781
6950
  });
6782
6951
  }
6952
+ if (scope === "global") {
6953
+ for (const globalRootDir of [
6954
+ path.join(os.homedir(), ".cbx"),
6955
+ path.join(os.homedir(), ".agents"),
6956
+ ]) {
6957
+ await removeEmptyDirectoryRecord({
6958
+ dirPath: globalRootDir,
6959
+ category: "global/root-dir-empty",
6960
+ dryRun,
6961
+ records: removedRecords,
6962
+ });
6963
+ }
6964
+ }
6783
6965
  }
6784
6966
  if (await checkDockerAvailable({ cwd })) {
6785
6967
  const containerName = DEFAULT_MCP_DOCKER_CONTAINER_NAME;