agents-inc 0.165.0 → 0.167.0
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 +23 -0
- package/dist/{chunk-B5UUHBAM.js → chunk-25MPBIK3.js} +3 -3
- package/dist/{chunk-VPJUCVU7.js → chunk-2VUYFTUB.js} +6 -6
- package/dist/{chunk-BGU25XAY.js → chunk-4NDBUJHK.js} +8 -8
- package/dist/{chunk-5M3PQNXI.js → chunk-6L75SQZU.js} +2 -2
- package/dist/{chunk-YNOOZFIB.js → chunk-6LF4CQBY.js} +2 -2
- package/dist/{chunk-UBSSIIWD.js → chunk-7JNJ52CD.js} +4 -4
- package/dist/{chunk-IM45LLQX.js → chunk-A4KDXNOT.js} +47 -137
- package/dist/chunk-A4KDXNOT.js.map +1 -0
- package/dist/{chunk-6SW6SB7H.js → chunk-DAIEQJIF.js} +3 -3
- package/dist/{chunk-AQXKAPCD.js → chunk-ENGYJCTF.js} +1 -3
- package/dist/{chunk-AQXKAPCD.js.map → chunk-ENGYJCTF.js.map} +1 -1
- package/dist/{chunk-6AIZPSEI.js → chunk-G4JK7WVO.js} +15 -16
- package/dist/chunk-G4JK7WVO.js.map +1 -0
- package/dist/{chunk-WTANOSXK.js → chunk-G6UREQ4V.js} +9 -9
- package/dist/chunk-G6UREQ4V.js.map +1 -0
- package/dist/{chunk-V5CWACR5.js → chunk-GE3WKJ6Q.js} +4 -4
- package/dist/{chunk-V5CWACR5.js.map → chunk-GE3WKJ6Q.js.map} +1 -1
- package/dist/{chunk-JV4TKXEN.js → chunk-HHIYT3PI.js} +4 -4
- package/dist/{chunk-WIFIAQZU.js → chunk-HWEKHVLE.js} +6 -6
- package/dist/{chunk-5P26XBZO.js → chunk-JQRJQ4SS.js} +3 -3
- package/dist/{chunk-Y5SNGMB7.js → chunk-LFWLEDUG.js} +6 -6
- package/dist/{chunk-TRTVOVHR.js → chunk-MAWNVT2C.js} +4 -4
- package/dist/{chunk-57KU6SPH.js → chunk-MPTNC32T.js} +3 -3
- package/dist/{chunk-MBUGQI2M.js → chunk-NBXXFIIW.js} +3 -3
- package/dist/{chunk-42TNV2TK.js → chunk-PMJWD27C.js} +3 -3
- package/dist/{chunk-X2KUI7V3.js → chunk-PNB3524H.js} +5 -5
- package/dist/{chunk-IVNKU4DM.js → chunk-PXWG7KFO.js} +2 -2
- package/dist/chunk-PXWG7KFO.js.map +1 -0
- package/dist/{chunk-LXXGZAD3.js → chunk-QHDPLLK6.js} +3 -3
- package/dist/{chunk-TV5Y53R2.js → chunk-RAHSOJQ2.js} +2 -2
- package/dist/{chunk-CTUJO23Z.js → chunk-RZDXDMT6.js} +2 -2
- package/dist/{chunk-PSX7CT6V.js → chunk-SAYZWCH6.js} +2 -2
- package/dist/{chunk-MWDY76MT.js → chunk-SCOQ34YM.js} +2 -2
- package/dist/{chunk-4C7GL4F7.js → chunk-SXVHSOTH.js} +4 -4
- package/dist/{chunk-E6CHHXGY.js → chunk-T6UWJAB3.js} +2 -2
- package/dist/{chunk-F2ZWFWTP.js → chunk-TODCQFRB.js} +2 -2
- package/dist/{chunk-CEBDGZID.js → chunk-TTY6DPJN.js} +3 -3
- package/dist/{chunk-CBMAO6OP.js → chunk-TZ2YJJKK.js} +5 -5
- package/dist/{chunk-WSGX4IWC.js → chunk-V3I7VQ7N.js} +4 -4
- package/dist/{chunk-GKPW33FP.js → chunk-V5WXIM2F.js} +2 -2
- package/dist/{chunk-6CY6PW5X.js → chunk-VAPTVR3J.js} +3 -3
- package/dist/{chunk-CK5RM3DJ.js → chunk-WVUDLKIO.js} +2 -2
- package/dist/{chunk-HR4YDSJK.js → chunk-WWIFNPJG.js} +12 -12
- package/dist/{chunk-P3KJCYSR.js → chunk-XZEVWPX6.js} +7 -7
- package/dist/{chunk-AAWZED5M.js → chunk-Y3VIRVVE.js} +10 -58
- package/dist/chunk-Y3VIRVVE.js.map +1 -0
- package/dist/{chunk-XATIKM3P.js → chunk-Y47EV74W.js} +7 -7
- package/dist/{chunk-UR3RRTPO.js → chunk-ZR4YPKXQ.js} +4 -4
- package/dist/{chunk-UCORXS4D.js → chunk-ZXKBERQU.js} +2 -2
- package/dist/commands/build/marketplace.js +6 -6
- package/dist/commands/build/plugins.js +8 -8
- package/dist/commands/compile.js +14 -15
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +59 -156
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +40 -41
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +12 -13
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +39 -39
- package/dist/commands/list.js +9 -9
- package/dist/commands/new/marketplace.js +6 -6
- package/dist/commands/search.js +11 -11
- package/dist/commands/share.js +11 -12
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +39 -69
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +8 -9
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/removal-plan-confirm.js +2 -2
- package/dist/components/common/select-list.js +2 -2
- package/dist/components/wizard/category-grid.js +4 -4
- package/dist/components/wizard/checkbox-grid.js +4 -4
- package/dist/components/wizard/domain-selection.js +9 -9
- package/dist/components/wizard/run-wizard-session.js +26 -26
- package/dist/components/wizard/scroll-affordance.js +2 -2
- package/dist/components/wizard/skill-agent-summary.js +5 -5
- package/dist/components/wizard/source-grid.js +5 -5
- package/dist/components/wizard/stack-selection.js +7 -7
- package/dist/components/wizard/step-agents.js +8 -8
- package/dist/components/wizard/step-build.js +7 -7
- package/dist/components/wizard/step-confirm.js +9 -9
- package/dist/components/wizard/step-sources.js +8 -8
- package/dist/components/wizard/step-stack.js +8 -8
- package/dist/components/wizard/summary-panel.js +8 -8
- package/dist/components/wizard/toast.js +2 -2
- package/dist/components/wizard/wizard-layout.js +12 -12
- package/dist/components/wizard/wizard-tabs.js +2 -2
- package/dist/components/wizard/wizard.js +25 -25
- package/dist/hooks/init.js +50 -40
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-3C27ENC7.js +14 -0
- package/dist/recompile-project-agents-DGNBCU3N.js +18 -0
- package/dist/{source-loader-RWZAYJ7E.js → source-loader-7KZ3MD5O.js} +4 -4
- package/dist/stores/wizard-store.js +4 -4
- package/package.json +1 -1
- package/src/schemas/project-config.schema.json +1 -1
- package/src/schemas/project-source-config.schema.json +1 -1
- package/dist/chunk-6AIZPSEI.js.map +0 -1
- package/dist/chunk-AAWZED5M.js.map +0 -1
- package/dist/chunk-IM45LLQX.js.map +0 -1
- package/dist/chunk-IVNKU4DM.js.map +0 -1
- package/dist/chunk-WTANOSXK.js.map +0 -1
- package/dist/load-agent-defs-JHLE6MAE.js +0 -14
- package/dist/recompile-project-agents-ZPP5NOLZ.js +0 -18
- /package/dist/{chunk-B5UUHBAM.js.map → chunk-25MPBIK3.js.map} +0 -0
- /package/dist/{chunk-VPJUCVU7.js.map → chunk-2VUYFTUB.js.map} +0 -0
- /package/dist/{chunk-BGU25XAY.js.map → chunk-4NDBUJHK.js.map} +0 -0
- /package/dist/{chunk-5M3PQNXI.js.map → chunk-6L75SQZU.js.map} +0 -0
- /package/dist/{chunk-YNOOZFIB.js.map → chunk-6LF4CQBY.js.map} +0 -0
- /package/dist/{chunk-UBSSIIWD.js.map → chunk-7JNJ52CD.js.map} +0 -0
- /package/dist/{chunk-6SW6SB7H.js.map → chunk-DAIEQJIF.js.map} +0 -0
- /package/dist/{chunk-JV4TKXEN.js.map → chunk-HHIYT3PI.js.map} +0 -0
- /package/dist/{chunk-WIFIAQZU.js.map → chunk-HWEKHVLE.js.map} +0 -0
- /package/dist/{chunk-5P26XBZO.js.map → chunk-JQRJQ4SS.js.map} +0 -0
- /package/dist/{chunk-Y5SNGMB7.js.map → chunk-LFWLEDUG.js.map} +0 -0
- /package/dist/{chunk-TRTVOVHR.js.map → chunk-MAWNVT2C.js.map} +0 -0
- /package/dist/{chunk-57KU6SPH.js.map → chunk-MPTNC32T.js.map} +0 -0
- /package/dist/{chunk-MBUGQI2M.js.map → chunk-NBXXFIIW.js.map} +0 -0
- /package/dist/{chunk-42TNV2TK.js.map → chunk-PMJWD27C.js.map} +0 -0
- /package/dist/{chunk-X2KUI7V3.js.map → chunk-PNB3524H.js.map} +0 -0
- /package/dist/{chunk-LXXGZAD3.js.map → chunk-QHDPLLK6.js.map} +0 -0
- /package/dist/{chunk-TV5Y53R2.js.map → chunk-RAHSOJQ2.js.map} +0 -0
- /package/dist/{chunk-CTUJO23Z.js.map → chunk-RZDXDMT6.js.map} +0 -0
- /package/dist/{chunk-PSX7CT6V.js.map → chunk-SAYZWCH6.js.map} +0 -0
- /package/dist/{chunk-MWDY76MT.js.map → chunk-SCOQ34YM.js.map} +0 -0
- /package/dist/{chunk-4C7GL4F7.js.map → chunk-SXVHSOTH.js.map} +0 -0
- /package/dist/{chunk-E6CHHXGY.js.map → chunk-T6UWJAB3.js.map} +0 -0
- /package/dist/{chunk-F2ZWFWTP.js.map → chunk-TODCQFRB.js.map} +0 -0
- /package/dist/{chunk-CEBDGZID.js.map → chunk-TTY6DPJN.js.map} +0 -0
- /package/dist/{chunk-CBMAO6OP.js.map → chunk-TZ2YJJKK.js.map} +0 -0
- /package/dist/{chunk-WSGX4IWC.js.map → chunk-V3I7VQ7N.js.map} +0 -0
- /package/dist/{chunk-GKPW33FP.js.map → chunk-V5WXIM2F.js.map} +0 -0
- /package/dist/{chunk-6CY6PW5X.js.map → chunk-VAPTVR3J.js.map} +0 -0
- /package/dist/{chunk-CK5RM3DJ.js.map → chunk-WVUDLKIO.js.map} +0 -0
- /package/dist/{chunk-HR4YDSJK.js.map → chunk-WWIFNPJG.js.map} +0 -0
- /package/dist/{chunk-P3KJCYSR.js.map → chunk-XZEVWPX6.js.map} +0 -0
- /package/dist/{chunk-XATIKM3P.js.map → chunk-Y47EV74W.js.map} +0 -0
- /package/dist/{chunk-UR3RRTPO.js.map → chunk-ZR4YPKXQ.js.map} +0 -0
- /package/dist/{chunk-UCORXS4D.js.map → chunk-ZXKBERQU.js.map} +0 -0
- /package/dist/{load-agent-defs-JHLE6MAE.js.map → load-agent-defs-3C27ENC7.js.map} +0 -0
- /package/dist/{recompile-project-agents-ZPP5NOLZ.js.map → recompile-project-agents-DGNBCU3N.js.map} +0 -0
- /package/dist/{source-loader-RWZAYJ7E.js.map → source-loader-7KZ3MD5O.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
RemovalPlanConfirm
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-TODCQFRB.js";
|
|
5
5
|
import {
|
|
6
6
|
promptConfirm
|
|
7
7
|
} from "../chunk-4AAOOAIN.js";
|
|
@@ -10,28 +10,28 @@ import {
|
|
|
10
10
|
lazyGateDeps,
|
|
11
11
|
mutateGlobal,
|
|
12
12
|
propagateGlobalRemoval
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-
|
|
13
|
+
} from "../chunk-G6UREQ4V.js";
|
|
14
|
+
import "../chunk-HWEKHVLE.js";
|
|
15
15
|
import {
|
|
16
16
|
loadAgentDefs
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-PMJWD27C.js";
|
|
18
|
+
import "../chunk-ZR4YPKXQ.js";
|
|
19
19
|
import {
|
|
20
20
|
listAgentFilesOf,
|
|
21
21
|
splitAgentsByProvenance
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-GE3WKJ6Q.js";
|
|
23
23
|
import "../chunk-JFLVEFFP.js";
|
|
24
24
|
import {
|
|
25
25
|
providerFlag,
|
|
26
26
|
providerNamedBy,
|
|
27
27
|
refuseAnAmbiguousInstallation
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-NBXXFIIW.js";
|
|
29
29
|
import {
|
|
30
30
|
BaseCommand,
|
|
31
31
|
EXIT_CODES
|
|
32
|
-
} from "../chunk-
|
|
33
|
-
import "../chunk-
|
|
34
|
-
import "../chunk-
|
|
32
|
+
} from "../chunk-Y3VIRVVE.js";
|
|
33
|
+
import "../chunk-VAPTVR3J.js";
|
|
34
|
+
import "../chunk-RZDXDMT6.js";
|
|
35
35
|
import {
|
|
36
36
|
ConfigLoadError,
|
|
37
37
|
INFO_MESSAGES,
|
|
@@ -53,7 +53,6 @@ import {
|
|
|
53
53
|
localSkillsRemoval,
|
|
54
54
|
notInstalledHere,
|
|
55
55
|
parseMarketplacePluginRef,
|
|
56
|
-
plannedSourceFolderMove,
|
|
57
56
|
providerInUse,
|
|
58
57
|
readForkedFromMetadata,
|
|
59
58
|
registeredProjectUpdateSkipped,
|
|
@@ -67,19 +66,19 @@ import {
|
|
|
67
66
|
uninstallConfigUnreadable,
|
|
68
67
|
uninstallNeedsTerminal,
|
|
69
68
|
unmarkedAgentsKept
|
|
70
|
-
} from "../chunk-
|
|
71
|
-
import "../chunk-
|
|
69
|
+
} from "../chunk-A4KDXNOT.js";
|
|
70
|
+
import "../chunk-V5WXIM2F.js";
|
|
72
71
|
import {
|
|
73
72
|
CLAUDE_DIR,
|
|
74
73
|
DEFAULT_BRANDING,
|
|
75
74
|
EJECT_SOURCE,
|
|
76
75
|
STANDARD_FILES
|
|
77
|
-
} from "../chunk-
|
|
76
|
+
} from "../chunk-ENGYJCTF.js";
|
|
78
77
|
import "../chunk-OI2H6M5I.js";
|
|
79
78
|
|
|
80
79
|
// src/cli/commands/uninstall.tsx
|
|
81
80
|
import "react";
|
|
82
|
-
import { partition
|
|
81
|
+
import { partition } from "remeda";
|
|
83
82
|
import path from "path";
|
|
84
83
|
import { Flags } from "@oclif/core";
|
|
85
84
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -117,7 +116,9 @@ function identifiableAgents(target) {
|
|
|
117
116
|
return target.markedAgents;
|
|
118
117
|
}
|
|
119
118
|
function configManifestEntries(target) {
|
|
120
|
-
|
|
119
|
+
const { dir, manifestFiles } = target.sourceFolder;
|
|
120
|
+
if (manifestFiles.length === 0) return [];
|
|
121
|
+
return [{ kind: "config", sourceDir: dir, fileNames: manifestFiles }];
|
|
121
122
|
}
|
|
122
123
|
function keptStatements(target, agentRemovals) {
|
|
123
124
|
const removing = new Set(agentRemovals.flatMap((entry) => entry.agentNames));
|
|
@@ -334,7 +335,7 @@ var Uninstall = class _Uninstall extends BaseCommand {
|
|
|
334
335
|
*/
|
|
335
336
|
async prepareGlobalPropagation(target, projectDir) {
|
|
336
337
|
if (target.config === null || target.registeredProjects.length === 0) return null;
|
|
337
|
-
const globalConfig =
|
|
338
|
+
const globalConfig = target.config;
|
|
338
339
|
try {
|
|
339
340
|
const [sourceResult, agentDefs] = await Promise.all([
|
|
340
341
|
loadSkillsMatrixFromSource({
|
|
@@ -401,9 +402,7 @@ var Uninstall = class _Uninstall extends BaseCommand {
|
|
|
401
402
|
this.log(statement);
|
|
402
403
|
}
|
|
403
404
|
const cleanup = await cleanupEmptyDirs(target, planEntries(plan, "config"));
|
|
404
|
-
|
|
405
|
-
this.reportSourceFolderCleanup(folder);
|
|
406
|
-
}
|
|
405
|
+
this.reportSourceFolderCleanup(cleanup.sourceFolder);
|
|
407
406
|
this.reportSourceRootCleanup(target.sourceRoot, cleanup);
|
|
408
407
|
if (cleanup.claudeDirRemoved) {
|
|
409
408
|
this.logSuccess(`Removed ${CLAUDE_DIR}/`);
|
|
@@ -421,14 +420,8 @@ var Uninstall = class _Uninstall extends BaseCommand {
|
|
|
421
420
|
this.log(`Kept ${folder.relName}/ (contains user content)`);
|
|
422
421
|
}
|
|
423
422
|
}
|
|
424
|
-
/**
|
|
425
|
-
* What became of the `.agents-inc/` parent, said out loud in both directions.
|
|
426
|
-
*
|
|
427
|
-
* A scope still on the legacy name has no such parent and gets no line — the folder it kept or
|
|
428
|
-
* lost was reported one statement above this one.
|
|
429
|
-
*/
|
|
423
|
+
/** What became of the `.agents-inc/` parent, said out loud in both directions. */
|
|
430
424
|
reportSourceRootCleanup(sourceRoot, cleanup) {
|
|
431
|
-
if (sourceRoot === null) return;
|
|
432
425
|
if (cleanup.sourceRootRemoved) {
|
|
433
426
|
this.logSuccess(`Removed ${sourceRoot.relName}/`);
|
|
434
427
|
return;
|
|
@@ -510,13 +503,6 @@ async function loadUninstallConfig(configPath, projectDir, provider, onLoadFaile
|
|
|
510
503
|
return null;
|
|
511
504
|
}
|
|
512
505
|
}
|
|
513
|
-
function sourceFoldersOf(projectDir, provider) {
|
|
514
|
-
const read = sourceFolderInUse(projectDir, provider);
|
|
515
|
-
const move = plannedSourceFolderMove(projectDir, provider);
|
|
516
|
-
if (!read.both || move === null) return [read];
|
|
517
|
-
const rival = read.legacy ? move.to : move.from;
|
|
518
|
-
return [read, { dir: rival.dir, relName: rival.relName, legacy: !read.legacy }];
|
|
519
|
-
}
|
|
520
506
|
async function readSourceFolder(folder, projectDir, provider, onConfigLoadFailed) {
|
|
521
507
|
const [manifestFiles, config] = await Promise.all([
|
|
522
508
|
manifestFilesIn(folder.dir),
|
|
@@ -536,16 +522,6 @@ async function manifestFilesIn(dir) {
|
|
|
536
522
|
);
|
|
537
523
|
return manifest.filter((_, index) => present[index]);
|
|
538
524
|
}
|
|
539
|
-
function sourceRootFor(projectDir, provider, folders) {
|
|
540
|
-
const ofTheFolderBeingRead = sourceRootOf(projectDir, provider);
|
|
541
|
-
if (ofTheFolderBeingRead !== null) return ofTheFolderBeingRead;
|
|
542
|
-
const move = plannedSourceFolderMove(projectDir, provider);
|
|
543
|
-
const rivalOnTheNewName = folders.some((folder) => !folder.legacy);
|
|
544
|
-
return rivalOnTheNewName && move !== null ? move.parent : null;
|
|
545
|
-
}
|
|
546
|
-
function cliInstalledPluginKeysOf(config) {
|
|
547
|
-
return [...getCliInstalledPluginKeys(config)];
|
|
548
|
-
}
|
|
549
525
|
function activeConfigOf(config) {
|
|
550
526
|
return {
|
|
551
527
|
...config,
|
|
@@ -558,27 +534,27 @@ async function detectUninstallTarget(projectDir, onConfigLoadFailed) {
|
|
|
558
534
|
const { skillsDir, agentsDir } = resolveInstallPaths(projectDir);
|
|
559
535
|
const claudeDir = path.join(projectDir, CLAUDE_DIR);
|
|
560
536
|
const provider = providerInUse(projectDir);
|
|
561
|
-
const
|
|
562
|
-
const sourceRoot = sourceRootFor(projectDir, provider, folders);
|
|
563
|
-
const [hasLocalSkills, hasLocalAgents, hasClaudeDir, sourceFolders, agentProvenance] = await Promise.all([
|
|
537
|
+
const [hasLocalSkills, hasLocalAgents, hasClaudeDir, sourceFolder, agentProvenance] = await Promise.all([
|
|
564
538
|
directoryExists(skillsDir),
|
|
565
539
|
directoryExists(agentsDir),
|
|
566
540
|
directoryExists(claudeDir),
|
|
567
|
-
|
|
568
|
-
|
|
541
|
+
readSourceFolder(
|
|
542
|
+
sourceFolderInUse(projectDir, provider),
|
|
543
|
+
projectDir,
|
|
544
|
+
provider,
|
|
545
|
+
onConfigLoadFailed
|
|
569
546
|
),
|
|
570
547
|
splitAgentsByProvenance(agentsDir, agentCodec(provider))
|
|
571
548
|
]);
|
|
572
|
-
const
|
|
573
|
-
const config = readable[0] ?? null;
|
|
549
|
+
const { config } = sourceFolder;
|
|
574
550
|
let pluginNames = [];
|
|
575
551
|
try {
|
|
576
552
|
pluginNames = await listPluginNames(projectDir);
|
|
577
553
|
} catch {
|
|
578
554
|
}
|
|
579
|
-
const
|
|
580
|
-
const configuredAgents =
|
|
581
|
-
const cliInstalledKeys =
|
|
555
|
+
const activeConfig = config === null ? null : activeConfigOf(config);
|
|
556
|
+
const configuredAgents = collectConfiguredAgents(activeConfig);
|
|
557
|
+
const cliInstalledKeys = getCliInstalledPluginKeys(activeConfig);
|
|
582
558
|
const cliPluginNames = thisRunOwnsAnyPlugin(projectDir) ? pluginNames.filter((name) => cliInstalledKeys.has(name)) : [];
|
|
583
559
|
return {
|
|
584
560
|
hasPlugins: cliPluginNames.length > 0,
|
|
@@ -591,10 +567,10 @@ async function detectUninstallTarget(projectDir, onConfigLoadFailed) {
|
|
|
591
567
|
skillsDir,
|
|
592
568
|
agentsDir,
|
|
593
569
|
claudeDir,
|
|
594
|
-
|
|
595
|
-
sourceRoot,
|
|
570
|
+
sourceFolder,
|
|
571
|
+
sourceRoot: sourceRootOf(projectDir),
|
|
596
572
|
config,
|
|
597
|
-
registeredProjects:
|
|
573
|
+
registeredProjects: config?.projects ?? [],
|
|
598
574
|
configuredAgents,
|
|
599
575
|
agentCodec: agentCodec(provider),
|
|
600
576
|
markedAgents: agentProvenance.marked,
|
|
@@ -715,21 +691,16 @@ async function removeConfigManifest(entry) {
|
|
|
715
691
|
return { manifestRemoved: true, dirRemoved: await removeDirIfEmpty(entry.sourceDir) };
|
|
716
692
|
}
|
|
717
693
|
async function cleanupEmptyDirs(target, manifestEntries) {
|
|
718
|
-
const
|
|
719
|
-
target.
|
|
720
|
-
|
|
721
|
-
folder,
|
|
722
|
-
manifestEntries.find((entry) => entry.sourceDir === folder.dir)
|
|
723
|
-
)
|
|
724
|
-
)
|
|
694
|
+
const sourceFolder = await cleanupSourceFolder(
|
|
695
|
+
target.sourceFolder,
|
|
696
|
+
manifestEntries.find((entry) => entry.sourceDir === target.sourceFolder.dir)
|
|
725
697
|
);
|
|
726
|
-
const
|
|
727
|
-
const sourceRoot = await cleanupSourceRoot(target.sourceRoot, providerDirRemoved);
|
|
698
|
+
const sourceRoot = await cleanupSourceRoot(target.sourceRoot, sourceFolder.dirRemoved);
|
|
728
699
|
const claudeDirRemoved = target.hasClaudeDir && await removeDirIfEmpty(target.claudeDir);
|
|
729
700
|
const claudeDirKept = !claudeDirRemoved && target.hasClaudeDir && await directoryExists(target.claudeDir);
|
|
730
701
|
return {
|
|
731
702
|
claudeDirRemoved,
|
|
732
|
-
|
|
703
|
+
sourceFolder,
|
|
733
704
|
sourceRootRemoved: sourceRoot.removed,
|
|
734
705
|
sourceRootKept: sourceRoot.kept,
|
|
735
706
|
claudeDirKept
|
|
@@ -741,14 +712,13 @@ async function cleanupSourceFolder(folder, entry) {
|
|
|
741
712
|
const kept = !dirRemoved && await directoryExists(folder.dir);
|
|
742
713
|
return {
|
|
743
714
|
relName: folder.relName,
|
|
744
|
-
legacy: folder.legacy,
|
|
745
715
|
manifestRemoved: manifest.manifestRemoved,
|
|
746
716
|
dirRemoved,
|
|
747
717
|
kept
|
|
748
718
|
};
|
|
749
719
|
}
|
|
750
720
|
async function cleanupSourceRoot(sourceRoot, providerDirRemoved) {
|
|
751
|
-
if (
|
|
721
|
+
if (!providerDirRemoved) return { removed: false, kept: false };
|
|
752
722
|
const removed = await removeDirIfEmpty(sourceRoot.dir);
|
|
753
723
|
return { removed, kept: !removed && await directoryExists(sourceRoot.dir) };
|
|
754
724
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/commands/uninstall.tsx"],"sourcesContent":["import React from \"react\";\nimport { partition, unique } from \"remeda\";\nimport path from \"path\";\n\nimport { Flags } from \"@oclif/core\";\n\nimport { BaseCommand } from \"../base-command\";\nimport {\n RemovalPlanConfirm,\n type RemovalPlanSection,\n} from \"../components/common/removal-plan-confirm.js\";\nimport { promptConfirm } from \"../components/common/prompt-confirm.js\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport {\n directoryExists,\n fileExists,\n listDirectories,\n remove,\n removeDirIfEmpty,\n} from \"../utils/fs\";\nimport { listAgentFilesOf, splitAgentsByProvenance } from \"../lib/agents\";\nimport { hostAt } from \"../lib/hosts/host-for.js\";\nimport type { PluginHost, PluginRemovalOutcome } from \"../lib/hosts/plugin-host.js\";\nimport {\n listPluginNames,\n getProjectPluginsDir,\n buildMarketplacePluginRef,\n parseMarketplacePluginRef,\n} from \"../lib/plugins/index\";\nimport { readForkedFromMetadata } from \"../lib/skills/index\";\nimport { isHomeDirectory, resolveInstallPaths } from \"../lib/installation/index\";\nimport { lazyGateDeps, mutateGlobal, propagateGlobalRemoval } from \"../lib/config-gate/index.js\";\nimport { loadSkillsMatrixFromSource } from \"../lib/loading\";\nimport { loadAgentDefs } from \"../lib/operations\";\nimport { ConfigLoadError, loadProjectConfigAt } from \"../lib/configuration/project-config\";\nimport type { Provider } from \"../consts\";\nimport { CLAUDE_DIR, DEFAULT_BRANDING, EJECT_SOURCE, STANDARD_FILES } from \"../consts\";\nimport {\n providerInUse,\n agentCodec,\n type AgentCodec,\n plannedSourceFolderMove,\n sourceFolderInUse,\n sourceRootOf,\n type SourceRoot,\n} from \"../lib/installation/install-layout.js\";\nimport {\n providerFlag,\n providerNamedBy,\n refuseAnAmbiguousInstallation,\n} from \"../lib/installation/provider-flag.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes\";\nimport {\n SUCCESS_MESSAGES,\n INFO_MESSAGES,\n UNINSTALL_PLAN,\n compiledAgentsRemoval,\n localSkillsRemoval,\n notInstalledHere,\n unmarkedAgentsKept,\n registeredProjectsUpdated,\n registeredProjectUpdateSkipped,\n registeredProjectsUpdateFailed,\n uninstallConfigUnreadable,\n uninstallNeedsTerminal,\n} from \"../utils/messages\";\nimport type { AgentDefinition, AgentName, MergedSkillsMatrix, ProjectConfig } from \"../types/index\";\nimport type { SkillScope } from \"../types/config\";\n\n/**\n * One removal this run promises AND makes. Each entry carries what its remover needs\n * — paths and names, never display strings — so the preview and the removal are two\n * readings of one value rather than two derivations of the same target.\n */\ntype RemovalEntry =\n | { kind: \"plugins\"; pluginsDir: string; names: string[] }\n | { kind: \"skills\"; skillsDir: string }\n | { kind: \"agents\"; agentsDir: string; agentNames: string[]; codec: AgentCodec }\n | { kind: \"config\"; sourceDir: string; fileNames: string[] };\n\n/** The single entry of a kind, for the remover that consumes it. */\ntype PluginRemoval = Extract<RemovalEntry, { kind: \"plugins\" }>;\ntype SkillsRemoval = Extract<RemovalEntry, { kind: \"skills\" }>;\ntype AgentsRemoval = Extract<RemovalEntry, { kind: \"agents\" }>;\ntype ConfigManifestRemoval = Extract<RemovalEntry, { kind: \"config\" }>;\n\n/**\n * The uninstall removal plan: what this run removes, and what it deliberately leaves\n * behind. Built once per run — the confirm UI renders it, the executor consumes the\n * same value, so the plan cannot name a removal the run declines to make.\n */\ntype RemovalPlan = {\n entries: RemovalEntry[];\n /** Statements naming content that stays, and why — see {@link keptStatements}. */\n kept: string[];\n};\n\n/**\n * Pure builder for the uninstall removal plan — the one place that decides what this\n * run removes. Every removal downstream is driven by an entry from here, and nothing\n * downstream re-reads the target to decide whether to act.\n */\nfunction buildRemovalPlan(target: UninstallTarget): RemovalPlan {\n const agents = compiledAgentsEntry(target);\n\n return {\n entries: [\n ...pluginsEntry(target),\n ...localSkillsEntry(target),\n ...agents,\n ...configManifestEntries(target),\n ],\n kept: keptStatements(target, agents),\n };\n}\n\n/** The plan carries at least one removal — the only thing that makes this run an uninstall. */\nfunction hasAnythingToRemove(plan: RemovalPlan): boolean {\n return plan.entries.length > 0;\n}\n\nfunction pluginsEntry(target: UninstallTarget): PluginRemoval[] {\n if (!target.hasPlugins) return [];\n return [{ kind: \"plugins\", pluginsDir: target.pluginsDir, names: [...target.cliPluginNames] }];\n}\n\nfunction localSkillsEntry(target: UninstallTarget): SkillsRemoval[] {\n if (!target.hasLocalSkills) return [];\n return [{ kind: \"skills\", skillsDir: target.skillsDir }];\n}\n\nfunction compiledAgentsEntry(target: UninstallTarget): AgentsRemoval[] {\n if (!target.hasLocalAgents) return [];\n\n const agentNames = identifiableAgents(target);\n if (agentNames.length === 0) return [];\n\n return [{ kind: \"agents\", agentsDir: target.agentsDir, agentNames, codec: target.agentCodec }];\n}\n\n/**\n * The agent files this run can say are its own, and on whose authority.\n *\n * The configuration is the first: it names the agents this install compiled, and a marker-less\n * file it names is still this CLI's — every install predating the marker is in exactly that\n * state. The provenance marker the compiler stamps into every agent is the second, reached\n * when there is no configuration left to read: provably ours, whoever lost the config. With\n * neither, nothing here is identifiable and the plan carries no removal for it.\n */\nfunction identifiableAgents(target: UninstallTarget): string[] {\n if (target.configuredAgents.length > 0) return target.configuredAgents;\n return target.markedAgents;\n}\n\n/**\n * The CLI config manifest (config.ts + config-types.ts) is always removed — from EVERY source\n * folder this scope holds. Over both of a provider's folders that is two entries: removing only the\n * one being read leaves the other read as the installation this run reported gone.\n */\nfunction configManifestEntries(target: UninstallTarget): ConfigManifestRemoval[] {\n return target.sourceFolders\n .filter((folder) => folder.manifestFiles.length > 0)\n .map((folder) => ({ kind: \"config\", sourceDir: folder.dir, fileNames: folder.manifestFiles }));\n}\n\n/**\n * The plan's kept half: the agent files carrying no provenance marker that the removal above\n * does not claim anyway. Derived FROM the removal rather than from a second reading of the\n * target, so the two halves of the plan cannot contradict each other — an agent named in both\n * lists would be a promise to remove a file the same plan calls kept.\n */\nfunction keptStatements(target: UninstallTarget, agentRemovals: AgentsRemoval[]): string[] {\n const removing = new Set(agentRemovals.flatMap((entry) => entry.agentNames));\n const kept = target.unmarkedAgents.filter((name) => !removing.has(name));\n if (kept.length === 0) return [];\n return [unmarkedAgentsKept(target.agentsDir, kept.length)];\n}\n\n/** Every entry of a kind — the config kind has one per source folder. */\nfunction planEntries<K extends RemovalEntry[\"kind\"]>(\n plan: RemovalPlan,\n kind: K,\n): Extract<RemovalEntry, { kind: K }>[] {\n return plan.entries.filter(\n (entry): entry is Extract<RemovalEntry, { kind: K }> => entry.kind === kind,\n );\n}\n\n/** The plan's entry of a kind, for the remover that consumes it. At most one exists. */\nfunction planEntry<K extends RemovalEntry[\"kind\"]>(\n plan: RemovalPlan,\n kind: K,\n): Extract<RemovalEntry, { kind: K }> | undefined {\n return plan.entries.find(\n (entry): entry is Extract<RemovalEntry, { kind: K }> => entry.kind === kind,\n );\n}\n\n/** The heading an entry prints under, and the lines it contributes beneath it. */\nfunction describeEntry(entry: RemovalEntry): RemovalPlanSection {\n switch (entry.kind) {\n case \"plugins\":\n return { label: UNINSTALL_PLAN.PLUGINS_HEADING, items: entry.names };\n case \"skills\":\n return {\n label: UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n items: [localSkillsRemoval(entry.skillsDir)],\n };\n case \"agents\":\n return {\n label: UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n items: [compiledAgentsRemoval(entry.agentsDir)],\n };\n case \"config\":\n return {\n label: UNINSTALL_PLAN.CONFIG_HEADING,\n items: entry.fileNames.map((fileName) => `${entry.sourceDir}/${fileName}`),\n };\n default: {\n const _exhaustive: never = entry;\n return _exhaustive;\n }\n }\n}\n\nconst PLAN_SECTION_ORDER = [\n UNINSTALL_PLAN.PLUGINS_HEADING,\n UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n UNINSTALL_PLAN.CONFIG_HEADING,\n] as const;\n\n/**\n * The plan's display half: entries grouped under their heading, in printing order —\n * skills and compiled agents share one. A heading is a promise about the lines beneath\n * it, so a heading no entry contributed to is not printed at all. The single source of\n * the strings shared by printRemovalPlan (plain text) and the UninstallConfirm Ink\n * component; each renderer only adds its own indentation and styling.\n */\nfunction planSections(entries: readonly RemovalEntry[]): RemovalPlanSection[] {\n const described = entries.map(describeEntry);\n\n return PLAN_SECTION_ORDER.flatMap((label) =>\n sectionWithItems(label, itemsUnder(described, label)),\n );\n}\n\n/** Every line the described entries contributed under one heading, in plan order. */\nfunction itemsUnder(described: readonly RemovalPlanSection[], label: string): string[] {\n return described.filter((section) => section.label === label).flatMap((section) => section.items);\n}\n\nfunction sectionWithItems(label: string, items: string[]): RemovalPlanSection[] {\n if (items.length === 0) return [];\n return [{ label, items }];\n}\n\nconst UNINSTALL_CONFIRM_MESSAGE = \"Are you sure you want to uninstall?\";\n\nexport default class Uninstall extends BaseCommand {\n /**\n * The name this run prints itself under, resolved once in {@link run} because three separate\n * lines carry it: the heading, the sign-off, and the warning naming the tool a preserved\n * directory was not created by. The last of those sits three calls below the resolution, and\n * threading a display string through `executeUninstall` -> `removeLocalFiles` ->\n * `removePlannedSkills` would widen three signatures to deliver one word.\n *\n * A plain `string` holding the shipped default rather than an optional every reader would have\n * to answer for. `run` replaces it before the first line is printed, so what a user sees is\n * always what their configuration says.\n */\n private brandingName: string = DEFAULT_BRANDING.NAME;\n\n static summary = `Remove ${DEFAULT_BRANDING.NAME} from this project`;\n\n static description = `Uninstall ${DEFAULT_BRANDING.NAME} from this project. Removes CLI-managed skills (matched by marketplace), compiled agents, plugins, and the config manifest (config.ts + config-types.ts) this project keeps. User-created content is preserved.`;\n\n static examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --yes\",\n ];\n\n static flags = {\n yes: Flags.boolean({\n char: \"y\",\n description: \"Skip confirmation prompt\",\n default: false,\n }),\n provider: providerFlag(),\n };\n\n /**\n * Starts at any terminal size. The size gate exists for the wizard's layout, and this command\n * draws none — a plan and a one-line confirm fit any terminal — while it is the way out of every\n * state, so a window too small to hold a wizard must not hold up an uninstall.\n */\n protected async ensureTerminalSize(): Promise<void> {\n // Nothing to wait for — see above.\n }\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Uninstall);\n const projectDir = process.cwd();\n\n // Before the branding read and before anything is detected: this run is destructive, and a\n // scope holding two installations has two answers to \"which one\". Removing the roster's first\n // is removing a user's other installation with nothing on screen to say so.\n await refuseAnAmbiguousInstallation(\n projectDir,\n \"uninstall\",\n providerNamedBy(flags.provider),\n (message) => this.error(message, { exit: EXIT_CODES.INVALID_ARGS }),\n );\n\n this.brandingName = await this.resolveBrandingName(projectDir);\n\n this.printHeader();\n\n const target = await detectUninstallTarget(projectDir, (failure) =>\n this.warn(uninstallConfigUnreadable(failure)),\n );\n const plan = buildRemovalPlan(target);\n if (!hasAnythingToRemove(plan)) {\n this.reportNothingToUninstall();\n return;\n }\n\n const confirmed = flags.yes ? this.printRemovalPlan(plan) : await this.confirmRemoval(plan);\n if (!confirmed) {\n this.log(\"\");\n this.log(\"Uninstall cancelled\");\n this.exit(EXIT_CODES.CANCELLED);\n }\n\n await this.executeUninstall(plan, target, projectDir);\n this.reportSuccess();\n }\n\n private printHeader(): void {\n this.log(\"\");\n this.log(`${this.brandingName} Uninstall`);\n this.log(\"\");\n }\n\n private reportNothingToUninstall(): void {\n this.warn(\"Nothing to uninstall.\");\n this.log(\"\");\n this.log(notInstalledHere(this.brandingName));\n this.log(\"\");\n this.log(INFO_MESSAGES.NO_CHANGES_MADE);\n }\n\n private printRemovalPlan(plan: RemovalPlan): true {\n this.log(UNINSTALL_PLAN.PREVIEW_HEADING);\n this.log(\"\");\n\n for (const section of planSections(plan.entries)) {\n this.log(` ${section.label}`);\n for (const item of section.items) {\n this.log(` ${item}`);\n }\n }\n\n for (const statement of plan.kept) {\n this.log(\"\");\n this.log(statement);\n }\n\n this.log(\"\");\n return true;\n }\n\n private async confirmRemoval(plan: RemovalPlan): Promise<boolean> {\n // A confirm nobody can answer must never become a yes, and mounting it here died on Ink's\n // raw-mode error — so a run with no terminal is refused before it, naming `--yes`.\n if (!process.stdin.isTTY) {\n this.error(uninstallNeedsTerminal(), { exit: EXIT_CODES.ERROR });\n }\n\n const outcome = await promptConfirm(({ onConfirm, onCancel }) => (\n <RemovalPlanConfirm\n heading={UNINSTALL_PLAN.PREVIEW_HEADING}\n sections={planSections(plan.entries)}\n statements={plan.kept}\n message={UNINSTALL_CONFIRM_MESSAGE}\n onConfirm={onConfirm}\n onCancel={onCancel}\n />\n ));\n return outcome === \"confirmed\";\n }\n\n private async executeUninstall(\n plan: RemovalPlan,\n target: UninstallTarget,\n projectDir: string,\n ): Promise<void> {\n const isGlobalUninstall = isHomeDirectory(projectDir);\n // Prepared BEFORE any removal: the projects[] registry and the source used\n // to regenerate each project's config-types.ts both live in the global\n // config this uninstall is about to delete.\n const propagation = isGlobalUninstall\n ? await this.prepareGlobalPropagation(target, projectDir)\n : null;\n\n await this.removePlannedPlugins(planEntry(plan, \"plugins\"), target.config, projectDir);\n\n try {\n await this.removeLocalFiles(plan, target);\n } catch (error) {\n this.log(\"Failed to remove local files\");\n this.error(getErrorMessage(error), {\n exit: EXIT_CODES.ERROR,\n });\n }\n\n if (isGlobalUninstall) {\n // The global manifest is gone — prune the inlined global-scoped entries the\n // registered projects still carry so they stop referencing removed content.\n if (propagation) {\n await this.updateRegisteredProjects(propagation);\n }\n return;\n }\n\n // Deregister this project from the global config's tracked projects so future\n // global edits stop propagating back into it. Best-effort by nature: a missing,\n // project-less, or corrupt global config (ConfigLoadError) must never fail the\n // uninstall — warn and move on.\n try {\n // Registration bookkeeping only: nothing inlines the `projects[]` list into a\n // project config, so this loads neither the matrix nor the agent definitions\n // and the uninstall stays offline.\n await mutateGlobal({ kind: \"deregister-project\", projectDir }, lazyGateDeps(projectDir));\n } catch (error) {\n this.warn(`Could not update the global project registry: ${getErrorMessage(error)}`);\n }\n }\n\n /**\n * Loads what updating registered projects needs (global config with its\n * projects[] registry, skills matrix, agent definitions) BEFORE the global\n * the config manifest is deleted — source resolution reads that config.\n * Returns null when nothing is registered or loading fails; a load failure\n * warns and never aborts the uninstall.\n *\n * `skipExtraSources: true` is NOT a divergence from the wizard's full\n * multi-source load: extra-source loading only annotates each skill's\n * `availableSources`/`activeSource` for wizard UI tagging — it never adds\n * skills or categories to the matrix, and the config-types writer never reads\n * those annotations, so the regenerated project types are byte-identical\n * either way (pinned by the skipExtraSources parity test in\n * local-installer.test.ts). Skipping avoids fetching every registered extra\n * source (network on a cold cache, plus unreachable-remote warnings) during\n * an uninstall that must never hang or noise-fail on remote state.\n *\n * `purpose: \"upkeep\"` because the prune only rewrites projects that are already installed: a\n * marketplace that has since lost its manifest must not leave every registered project naming\n * the global content this uninstall deletes.\n */\n private async prepareGlobalPropagation(\n target: UninstallTarget,\n projectDir: string,\n ): Promise<GlobalPropagationData | null> {\n if (target.config === null || target.registeredProjects.length === 0) return null;\n // Every project either source folder registered: a global uninstall over two folders removes\n // both, so a project registered only in the one not being read is still owed its prune.\n const globalConfig: ProjectConfig = { ...target.config, projects: target.registeredProjects };\n\n try {\n const [sourceResult, agentDefs] = await Promise.all([\n loadSkillsMatrixFromSource({\n projectDir,\n purpose: \"upkeep\",\n skipExtraSources: true,\n matrixOnly: true,\n }),\n loadAgentDefs(),\n ]);\n return {\n globalConfig,\n matrix: sourceResult.matrix,\n agents: agentDefs.agents,\n provider: providerInUse(projectDir),\n };\n } catch (error) {\n this.warn(registeredProjectsUpdateFailed(getErrorMessage(error)));\n return null;\n }\n }\n\n /**\n * Prunes the inlined global-scoped config entries from every registered\n * project and regenerates its config-types.ts. Runs AFTER the global manifest\n * removal so the regenerated project types fall back to the standalone form\n * instead of importing from the deleted global config-types.ts. Best-effort:\n * unreachable projects are warned and skipped, and no failure here may abort\n * the uninstall.\n */\n private async updateRegisteredProjects(propagation: GlobalPropagationData): Promise<void> {\n try {\n const report = await propagateGlobalRemoval(\n propagation.globalConfig,\n { matrix: propagation.matrix, agents: propagation.agents },\n propagation.provider,\n );\n const { updated, unreadable, failed } = report.propagated;\n this.reportProjectsOutOfReach(report.propagated);\n // Uninstall's own sentence, only where it is true: the config is there and was not pruned.\n for (const skippedPath of [...unreadable, ...failed]) {\n this.warn(registeredProjectUpdateSkipped(skippedPath));\n }\n if (updated.length === 0) return;\n\n this.logSuccess(registeredProjectsUpdated(updated.length));\n\n // The pruned projects' compiled agents were built from the global rows this\n // uninstall just removed, so the prune owes them a recompile too.\n this.reportPropagatedRecompile(report);\n } catch (error) {\n this.warn(registeredProjectsUpdateFailed(getErrorMessage(error)));\n }\n }\n\n private reportSuccess(): void {\n this.log(\"\");\n this.log(`${this.brandingName} has been uninstalled.`);\n this.log(\"\");\n this.logSuccess(SUCCESS_MESSAGES.UNINSTALL_COMPLETE);\n this.log(\"\");\n }\n\n /**\n * Each step acts only when the plan carries its entry, so what the user was shown and\n * what this run removes are the same list read twice.\n */\n private async removeLocalFiles(plan: RemovalPlan, target: UninstallTarget): Promise<void> {\n await this.removePlannedSkills(planEntry(plan, \"skills\"));\n await this.removePlannedAgents(planEntry(plan, \"agents\"));\n\n // The same statements the preview showed, from the same plan: what the user approved and\n // what this run reports are one value read twice, never two derivations that agree today.\n for (const statement of plan.kept) {\n this.log(statement);\n }\n\n const cleanup = await cleanupEmptyDirs(target, planEntries(plan, \"config\"));\n\n for (const folder of cleanup.sourceFolders) {\n this.reportSourceFolderCleanup(folder);\n }\n\n this.reportSourceRootCleanup(target.sourceRoot, cleanup);\n\n if (cleanup.claudeDirRemoved) {\n this.logSuccess(`Removed ${CLAUDE_DIR}/`);\n } else if (cleanup.claudeDirKept) {\n this.log(`Kept ${CLAUDE_DIR}/ (contains user content)`);\n }\n }\n\n /** What became of one source folder: gone, emptied of the CLI's files, or kept for the user's. */\n private reportSourceFolderCleanup(folder: SourceFolderCleanup): void {\n if (folder.dirRemoved) {\n this.logSuccess(`Removed ${folder.relName}/`);\n } else if (folder.manifestRemoved) {\n this.logSuccess(`Removed CLI config from ${folder.relName}/`);\n } else if (folder.kept) {\n this.log(`Kept ${folder.relName}/ (contains user content)`);\n }\n }\n\n /**\n * What became of the `.agents-inc/` parent, said out loud in both directions.\n *\n * A scope still on the legacy name has no such parent and gets no line — the folder it kept or\n * lost was reported one statement above this one.\n */\n private reportSourceRootCleanup(sourceRoot: SourceRoot | null, cleanup: CleanupResult): void {\n if (sourceRoot === null) return;\n\n if (cleanup.sourceRootRemoved) {\n this.logSuccess(`Removed ${sourceRoot.relName}/`);\n return;\n }\n if (cleanup.sourceRootKept) {\n this.log(`Kept ${sourceRoot.relName}/ (contains user content)`);\n }\n }\n\n private async removePlannedPlugins(\n entry: PluginRemoval | undefined,\n config: ProjectConfig | null,\n projectDir: string,\n ): Promise<void> {\n if (!entry) return;\n\n this.log(\"Uninstalling plugins...\");\n\n try {\n const pluginResult = await uninstallPlugins(entry, config, projectDir, (name) =>\n this.log(` Uninstalled plugin '${name}'`),\n );\n\n // Silent on zero rather than \"Uninstalled 0 plugins\": the line's job is to account for\n // removals, and a run that observed none has nothing to account for — the plan preview has\n // already said what it expected to find. Saying \"0\" beside a plan that named one reads as a\n // failure, which it is not: a plugin a user removed themselves is the ordinary case.\n if (pluginResult.totalUninstalled > 0) {\n this.logSuccess(\n `Uninstalled ${pluginResult.totalUninstalled} ${pluginResult.totalUninstalled === 1 ? \"plugin\" : \"plugins\"}`,\n );\n }\n } catch (error) {\n this.log(\"Plugin uninstall failed\");\n this.error(getErrorMessage(error), {\n exit: EXIT_CODES.ERROR,\n });\n }\n }\n\n private async removePlannedSkills(entry: SkillsRemoval | undefined): Promise<void> {\n if (!entry) return;\n\n const result = await removeMatchingSkills(\n entry,\n (dirName) => this.log(` Uninstalled skill '${dirName}'`),\n (dirName) => this.warn(`Skipping '${dirName}': not created by ${this.brandingName} CLI`),\n );\n if (result.removedCount === 0) return;\n\n this.logSuccess(\n `Removed ${result.removedCount} CLI-installed ${result.removedCount === 1 ? \"skill\" : \"skills\"}`,\n );\n }\n\n private async removePlannedAgents(entry: AgentsRemoval | undefined): Promise<void> {\n if (!entry) return;\n\n const result = await removeMatchingAgents(entry, (agentName) =>\n this.log(` Uninstalled agent '${agentName}'`),\n );\n if (result.removedCount === 0) return;\n\n this.logSuccess(\n `Removed ${result.removedCount} compiled ${result.removedCount === 1 ? \"agent\" : \"agents\"}`,\n );\n }\n}\n\n/** @internal Exported for testing */\nexport type UninstallTarget = {\n hasPlugins: boolean;\n pluginNames: string[];\n /** Plugin names filtered to only those installed by this CLI (matched against config skills) */\n cliPluginNames: string[];\n hasLocalSkills: boolean;\n hasLocalAgents: boolean;\n hasClaudeDir: boolean;\n pluginsDir: string;\n skillsDir: string;\n agentsDir: string;\n /**\n * How this installation's host names and marks a compiled agent — `*.md` on Claude, `*.toml` on\n * Codex. Everything the plan reads or removes in `agentsDir` goes through it (CLI-896).\n */\n agentCodec: AgentCodec;\n claudeDir: string;\n /**\n * Every source folder this scope holds, the one being read first. One folder normally; both of\n * Claude's — `.claude-src/` and `.agents-inc/claude/` — when both are on disk, because an\n * uninstall removes the installation whichever name it is under.\n */\n sourceFolders: ScopeSourceFolder[];\n /**\n * The `.agents-inc/` parent a provider folder of this scope sits under, and `null` when every\n * folder is on the legacy name — whose parent is the scope root itself and is nobody's to remove.\n */\n sourceRoot: SourceRoot | null;\n /**\n * The first readable config among {@link UninstallTarget.sourceFolders}, the one being read\n * first.\n */\n config: ProjectConfig | null;\n /** Every project any readable config registers — only a global config registers any. */\n registeredProjects: string[];\n /** Agent names every readable config names (e.g., [\"web-developer\"]) */\n configuredAgents: AgentName[];\n /** On-disk agent basenames carrying this CLI's provenance marker. */\n markedAgents: string[];\n /** On-disk agent basenames carrying no marker — the user's, unless the config claims them. */\n unmarkedAgents: string[];\n};\n\n/** One source folder of the scope being uninstalled, and what this run reads and removes in it. */\ntype ScopeSourceFolder = {\n dir: string;\n /** The folder as a user writes it: `.agents-inc/claude`, or `.claude-src`. */\n relName: string;\n /** Whether it is the legacy `.claude-src/`, which has no `.agents-inc/` parent. */\n legacy: boolean;\n /** The manifest files on disk in it — `config.ts`, `config-types.ts` — which this run removes. */\n manifestFiles: string[];\n /** Its config, or `null` when the file is absent or unreadable — see {@link loadUninstallConfig}. */\n config: ProjectConfig | null;\n};\n\n/**\n * Everything a global uninstall needs to update the registered projects after\n * the global manifest is removed — captured before removal because it is\n * sourced from the config being deleted.\n */\ntype GlobalPropagationData = {\n globalConfig: ProjectConfig;\n matrix: MergedSkillsMatrix;\n agents: Partial<Record<AgentName, AgentDefinition>>;\n /**\n * Which installation is being removed.\n *\n * Captured here for the same reason everything else on this type is: by the time the prune\n * runs, the folder that says which provider this was has been deleted, so reading it then\n * would answer about whatever is left rather than about what was removed.\n */\n provider: Provider;\n};\n\ntype SkillRemovalResult = {\n removedCount: number;\n skippedCount: number;\n removedNames: string[];\n skippedNames: string[];\n /** Whether the skills directory was cleaned up (empty after removal) */\n dirCleaned: boolean;\n};\n\ntype AgentRemovalResult = {\n removedCount: number;\n removedNames: string[];\n /** Whether the agents directory was cleaned up (empty after removal) */\n dirCleaned: boolean;\n};\n\ntype UninstallPluginsResult = {\n uninstalledNames: string[];\n totalUninstalled: number;\n};\n\n/** What a cleanup pass did with one source folder. */\ntype SourceFolderCleanup = {\n relName: string;\n legacy: boolean;\n /** Whether config.ts/config-types.ts were removed from it */\n manifestRemoved: boolean;\n /** Whether the emptied folder itself was removed */\n dirRemoved: boolean;\n /** Whether it is still there because something this CLI never wrote lives in it */\n kept: boolean;\n};\n\ntype CleanupResult = {\n claudeDirRemoved: boolean;\n /** One per source folder of the scope, in {@link UninstallTarget.sourceFolders} order */\n sourceFolders: SourceFolderCleanup[];\n /** Whether the emptied `.agents-inc/` parent went with the last provider folder under it */\n sourceRootRemoved: boolean;\n /** Whether `.agents-inc/` still exists because something this CLI never wrote lives in it */\n sourceRootKept: boolean;\n /** Whether .claude/ still exists with user content after cleanup */\n claudeDirKept: boolean;\n};\n\nfunction collectConfiguredAgents(config: Partial<ProjectConfig> | null): AgentName[] {\n if (!config?.agents) return [];\n return config.agents.map((a) => a.name);\n}\n\n/** @internal Exported for testing */\nexport function getCliInstalledPluginKeys(config: Partial<ProjectConfig> | null): Set<string> {\n if (!config?.skills) return new Set();\n const { marketplaceName } = config;\n return new Set(\n config.skills.flatMap((skill) => [\n // Primary key: skill.id@skill.origin\n buildMarketplacePluginRef(skill.id, skill.origin),\n // Marketplace variant for plugins installed via marketplace where\n // skill.origin may differ (e.g., \"eject\" vs the marketplace name)\n ...(marketplaceName && skill.origin !== marketplaceName && skill.origin !== EJECT_SOURCE\n ? [buildMarketplacePluginRef(skill.id, marketplaceName)]\n : []),\n ]),\n );\n}\n\n/**\n * Loads the config that drives the removal plan. A config file that exists but\n * cannot be parsed (`ConfigLoadError`) is handed to `onLoadFailed` and\n * then treated exactly like a missing one — an unreadable config is precisely\n * when a user needs to uninstall, so it must never fail the run. Same posture as\n * the `deregister-project` mutation's call site above. Only the plan degrades: the plugins and\n * compiled agents the config named can no longer be identified, while file\n * removal proceeds. Any other failure is a real fault and still propagates.\n */\nasync function loadUninstallConfig(\n configPath: string,\n projectDir: string,\n provider: Provider,\n onLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<ProjectConfig | null> {\n try {\n const result = await loadProjectConfigAt(configPath, projectDir, provider);\n return result?.config ?? null;\n } catch (error) {\n if (!(error instanceof ConfigLoadError)) throw error;\n onLoadFailed(error);\n return null;\n }\n}\n\n/**\n * The scope's source folders, the one being read first: one normally, and both of Claude's when\n * both are on disk — the state every write command refuses over, and the one an uninstall has to\n * clear rather than refuse, since leaving either behind leaves an installation behind.\n */\nfunction sourceFoldersOf(\n projectDir: string,\n provider: Provider,\n): Pick<ScopeSourceFolder, \"dir\" | \"relName\" | \"legacy\">[] {\n const read = sourceFolderInUse(projectDir, provider);\n const move = plannedSourceFolderMove(projectDir, provider);\n if (!read.both || move === null) return [read];\n\n const rival = read.legacy ? move.to : move.from;\n return [read, { dir: rival.dir, relName: rival.relName, legacy: !read.legacy }];\n}\n\n/** One folder's manifest files on disk, and its config read under uninstall's degrade posture. */\nasync function readSourceFolder(\n folder: Pick<ScopeSourceFolder, \"dir\" | \"relName\" | \"legacy\">,\n projectDir: string,\n provider: Provider,\n onConfigLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<ScopeSourceFolder> {\n const [manifestFiles, config] = await Promise.all([\n manifestFilesIn(folder.dir),\n loadUninstallConfig(\n path.join(folder.dir, STANDARD_FILES.CONFIG_TS),\n projectDir,\n provider,\n onConfigLoadFailed,\n ),\n ]);\n return { ...folder, manifestFiles, config };\n}\n\n/** The files of the CLI config manifest — `config.ts`, `config-types.ts` — that are in `dir`. */\nasync function manifestFilesIn(dir: string): Promise<string[]> {\n const manifest = [STANDARD_FILES.CONFIG_TS, STANDARD_FILES.CONFIG_TYPES_TS];\n const present = await Promise.all(\n manifest.map((fileName) => fileExists(path.join(dir, fileName))),\n );\n return manifest.filter((_, index) => present[index]);\n}\n\n/**\n * The `.agents-inc/` parent, when any of the scope's folders sits under it: the folder being read\n * answers for itself, and where that one is the legacy name, a rival on the new name brings its\n * parent with it.\n */\nfunction sourceRootFor(\n projectDir: string,\n provider: Provider,\n folders: readonly Pick<ScopeSourceFolder, \"legacy\">[],\n): SourceRoot | null {\n const ofTheFolderBeingRead = sourceRootOf(projectDir, provider);\n if (ofTheFolderBeingRead !== null) return ofTheFolderBeingRead;\n\n const move = plannedSourceFolderMove(projectDir, provider);\n const rivalOnTheNewName = folders.some((folder) => !folder.legacy);\n return rivalOnTheNewName && move !== null ? move.parent : null;\n}\n\n/** {@link getCliInstalledPluginKeys} as a list, so several configs' keys can be pooled. */\nfunction cliInstalledPluginKeysOf(config: ProjectConfig): string[] {\n return [...getCliInstalledPluginKeys(config)];\n}\n\n/** A config with its excluded rows dropped — the entries an uninstall acts on. */\nfunction activeConfigOf(config: ProjectConfig): ProjectConfig {\n return {\n ...config,\n skills: config.skills.filter((s) => !s.excluded),\n agents: config.agents.filter((a) => !a.excluded),\n };\n}\n\n/**\n * Detects what's installed in a project directory for uninstallation.\n *\n * Checks for plugins, local skills, agents, config directories, and\n * resolves which plugins were installed by this CLI.\n */\nasync function detectUninstallTarget(\n projectDir: string,\n onConfigLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<UninstallTarget> {\n const pluginsDir = getProjectPluginsDir(projectDir);\n const { skillsDir, agentsDir } = resolveInstallPaths(projectDir);\n const claudeDir = path.join(projectDir, CLAUDE_DIR);\n // One read for the folders being removed and the parent they sit in: an uninstall that\n // resolved those against different installations would delete half of each.\n const provider = providerInUse(projectDir);\n const folders = sourceFoldersOf(projectDir, provider);\n const sourceRoot = sourceRootFor(projectDir, provider, folders);\n\n const [hasLocalSkills, hasLocalAgents, hasClaudeDir, sourceFolders, agentProvenance] =\n await Promise.all([\n directoryExists(skillsDir),\n directoryExists(agentsDir),\n directoryExists(claudeDir),\n Promise.all(\n folders.map((folder) => readSourceFolder(folder, projectDir, provider, onConfigLoadFailed)),\n ),\n splitAgentsByProvenance(agentsDir, agentCodec(provider)),\n ]);\n\n const readable = sourceFolders.flatMap((folder) => (folder.config ? [folder.config] : []));\n const config = readable[0] ?? null;\n\n let pluginNames: string[] = [];\n try {\n pluginNames = await listPluginNames(projectDir);\n } catch {\n // Best-effort: plugin detection may fail\n }\n\n // Every readable config's roster, not only the one being read: over two folders each names what\n // was installed from it, and the run removes both.\n const activeConfigs = readable.map(activeConfigOf);\n const configuredAgents = unique(activeConfigs.flatMap(collectConfiguredAgents));\n const cliInstalledKeys = new Set(activeConfigs.flatMap(cliInstalledPluginKeysOf));\n const cliPluginNames = thisRunOwnsAnyPlugin(projectDir)\n ? pluginNames.filter((name) => cliInstalledKeys.has(name))\n : [];\n\n return {\n hasPlugins: cliPluginNames.length > 0,\n pluginNames,\n cliPluginNames,\n hasLocalSkills,\n hasLocalAgents,\n hasClaudeDir,\n pluginsDir,\n skillsDir,\n agentsDir,\n claudeDir,\n sourceFolders,\n sourceRoot,\n config,\n registeredProjects: unique(readable.flatMap((each) => each.projects ?? [])),\n configuredAgents,\n agentCodec: agentCodec(provider),\n markedAgents: agentProvenance.marked,\n unmarkedAgents: agentProvenance.unmarked,\n };\n}\n\n/**\n * Whether this run has any plugin of its own to remove.\n *\n * **A PROJECT uninstall on a host that installs no project-scoped plugin has none, and asking\n * anyway is the one operation in this step that can destroy work nobody asked about.** Codex\n * installs plugins for a MACHINE: no subcommand takes a scope, `plugin add` run inside a project\n * writes the switch to the global config and silently un-scopes it, and `plugin list` answers the\n * same set from every directory. So every plugin a Codex project can see belongs to every other\n * project on that machine, and a project uninstall that swept them would take them all away.\n *\n * Read off {@link PluginHost.installsProjectScopedPlugins} rather than written as a provider\n * check, so a host added later is covered by the flag it already has to set. Claude's answer is\n * `true`, so its sweep is unchanged at both scopes — which is deliberate: whether a Claude project\n * uninstall should drop a user-scoped registration is a separate question about a host that CAN\n * hold both, and nothing here is the place to settle it.\n *\n * A GLOBAL uninstall owns the machine's plugins whatever the host, which is the case the other\n * half of this pair rests on.\n */\nfunction thisRunOwnsAnyPlugin(projectDir: string): boolean {\n if (isHomeDirectory(projectDir)) return true;\n return hostAt(projectDir).installsProjectScopedPlugins;\n}\n\nfunction shouldRemoveSkill(forkedFrom: { source?: string } | null): boolean {\n return forkedFrom !== null;\n}\n\nasync function removeMatchingSkills(\n entry: SkillsRemoval,\n onRemoved?: (dirName: string) => void,\n onSkipped?: (dirName: string) => void,\n): Promise<SkillRemovalResult> {\n const classified = await classifySkillDirs(entry.skillsDir);\n const removedNames = await removeClassifiedSkills(\n classified.toRemove,\n entry.skillsDir,\n onRemoved,\n );\n classified.toSkip.forEach((name) => onSkipped?.(name));\n const dirCleaned = await cleanupSkillsDir(entry.skillsDir, classified.toSkip.length === 0);\n\n return {\n removedCount: removedNames.length,\n skippedCount: classified.toSkip.length,\n removedNames,\n skippedNames: classified.toSkip,\n dirCleaned,\n };\n}\n\nasync function classifySkillDirs(\n skillsDir: string,\n): Promise<{ toRemove: string[]; toSkip: string[] }> {\n const dirNames = await listDirectories(skillsDir);\n const entries = await Promise.all(\n dirNames.map(async (name) => ({\n name,\n forkedFrom: await readForkedFromMetadata(path.join(skillsDir, name)),\n })),\n );\n const [removable, skippable] = partition(entries, (entry) => shouldRemoveSkill(entry.forkedFrom));\n return { toRemove: removable.map((e) => e.name), toSkip: skippable.map((e) => e.name) };\n}\n\nasync function removeClassifiedSkills(\n names: string[],\n skillsDir: string,\n onRemoved?: (name: string) => void,\n): Promise<string[]> {\n for (const name of names) {\n await remove(path.join(skillsDir, name));\n onRemoved?.(name);\n }\n return names;\n}\n\nasync function cleanupSkillsDir(dir: string, allRemoved: boolean): Promise<boolean> {\n if (!allRemoved) return false;\n return removeDirIfEmpty(dir);\n}\n\n/**\n * Removes the compiled agent files the plan's agents entry names — `.md` on Claude, `.toml` on\n * Codex, as the entry's codec says.\n *\n * A file is removed only when its basename is one the entry names; every other agent file\n * is preserved. Cleans up the agents directory if empty after removal. Which names those are\n * — the configured roster, or the files carrying the provenance marker — is the plan's\n * decision, not this function's, and so is whether to act at all: no entry, no call.\n *\n * @param onRemoved - Called for each removed agent name (for logging)\n */\nasync function removeMatchingAgents(\n entry: AgentsRemoval,\n onRemoved?: (agentName: string) => void,\n): Promise<AgentRemovalResult> {\n const agentFiles = await listAgentFiles(entry.agentsDir, entry.codec);\n const removedNames = agentFiles\n .map((agentFile) => path.basename(agentFile, entry.codec.extension))\n .filter((agentName) => entry.agentNames.includes(agentName));\n\n for (const agentName of removedNames) {\n await remove(path.join(entry.agentsDir, `${agentName}${entry.codec.extension}`));\n onRemoved?.(agentName);\n }\n\n const dirCleaned = await removeDirIfEmpty(entry.agentsDir);\n\n return {\n removedCount: removedNames.length,\n removedNames,\n dirCleaned,\n };\n}\n\n/**\n * Where a plugin is looked for FIRST when the config has no entry naming its skill.\n *\n * The scope translation swallowed this until C3 — an absent scope and a project one mapped to\n * the same Claude scope word — so the order is unchanged and only the silence is gone.\n */\nconst SCOPE_OF_AN_UNRECORDED_SKILL: SkillScope = \"project\";\n\n/**\n * Every scope this host installs plugins at, the config's own answer first.\n *\n * A plugin's registered scope is genuinely ambiguous here — a skill re-scoped after install is\n * filed under the scope it was installed at, not the one the config now names — so a removal\n * asks at each scope rather than trusting one. The best-effort sweep this replaces did it with\n * two hard-coded Claude scope words and no way to answer for another host; reading the roster off\n * {@link PluginHost.offeredPlacements} is the same sweep with the host deciding its width, which\n * for a host that installs plugins globally only is one scope rather than two.\n */\nfunction pluginScopesToSweep(host: PluginHost, primary: SkillScope): SkillScope[] {\n const offered = host.offeredPlacements\n .filter((placement) => placement.mode === \"plugin\")\n .map((placement) => placement.scope);\n\n if (!offered.includes(primary)) return offered;\n return [primary, ...offered.filter((scope) => scope !== primary)];\n}\n\n/**\n * Drops a plugin's registration wherever the host filed it, and says whether one was there.\n *\n * Best-effort on the FAILURE side and unchanged by C3: the directory is deleted either way, so a\n * registration that cannot be dropped is untidy rather than wrong, and a thrown error here would\n * abort an uninstall that has already removed files. What changed is that it no longer reports\n * NOTHING — {@link PluginHost.uninstallPlugin} answers `removed` or `absent`, and that answer is\n * the only thing on this path that knows the difference.\n *\n * `removed` if ANY swept scope had one, because the sweep exists precisely for a plugin filed\n * under a scope the config no longer names: one registration dropped at either scope is one\n * removal, not two, and not none.\n */\nasync function removePluginWhereverItIsFiled(\n host: PluginHost,\n pluginRef: string,\n primaryScope: SkillScope,\n projectDir: string,\n): Promise<PluginRemovalOutcome> {\n let outcome: PluginRemovalOutcome = \"absent\";\n\n for (const scope of pluginScopesToSweep(host, primaryScope)) {\n try {\n const swept = await host.uninstallPlugin(pluginRef, scope, projectDir);\n if (swept === \"removed\") outcome = \"removed\";\n } catch {\n // Best-effort: the plugin may not be registered at this scope. A throw is neither outcome\n // — the host reserves it for a failure that is not \"there was nothing there\" — so it leaves\n // `outcome` exactly as the other scopes found it rather than claiming an absence.\n }\n }\n\n return outcome;\n}\n\n/**\n * Uninstalls the plugins the plan's plugins entry names, by removing them from the\n * host and deleting their local directories. The entry decides WHICH plugins go;\n * `config` only answers HOW to ask the host — the scope its registry filed each\n * one under, per-skill where the config says, project-level otherwise.\n *\n * @param onUninstalled - Called for each successfully uninstalled plugin name (for logging)\n * @internal Exported for testing\n */\nexport async function uninstallPlugins(\n entry: PluginRemoval,\n config: ProjectConfig | null,\n projectDir: string,\n onUninstalled?: (pluginName: string) => void,\n): Promise<UninstallPluginsResult> {\n const host = hostAt(projectDir);\n const hostAvailable = await host.isAvailable();\n const observed: string[] = [];\n\n for (const pluginName of entry.names) {\n const removal = hostAvailable\n ? await removePluginWhereverItIsFiled(\n host,\n pluginName,\n scopeThePluginIsFiledUnder(config, pluginName),\n projectDir,\n )\n : nothingWasObserved();\n\n await remove(path.join(entry.pluginsDir, pluginName));\n\n if (removal === \"removed\") {\n observed.push(pluginName);\n onUninstalled?.(pluginName);\n }\n }\n\n // What was OBSERVED, never what was asked for — D11(b), and the one Claude-visible change this\n // step signs off. On Codex `plugin remove` exits 0 and prints the same document for a plugin\n // that was never installed, one that really went and one removed twice, so a count built from\n // the plan is a count of intentions: it says \"Uninstalled 1 plugin\" for a plugin this command\n // did not touch. The listing the host takes BEFORE its removal is the only thing that knows.\n return {\n uninstalledNames: observed,\n totalUninstalled: observed.length,\n };\n}\n\n/**\n * The scope the host's registry most likely filed this plugin under, from the config's own row.\n *\n * The sweep beside it asks at every scope the host installs plugins at, so this is a PREFERENCE\n * rather than an answer: a skill re-scoped after install is filed under the scope it was installed\n * at, not the one the config now names.\n */\nfunction scopeThePluginIsFiledUnder(config: ProjectConfig | null, pluginName: string): SkillScope {\n const skillId = parseMarketplacePluginRef(pluginName);\n return (\n config?.skills.find((skill) => skill.id === skillId)?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL\n );\n}\n\n/**\n * What a run with no host binary observed, which is nothing — the OTHER half of D11(b).\n *\n * This command used to count every plugin it MEANT to remove, so a machine where the host is not\n * installed reported a clean sweep of registrations nothing had touched. An unreachable host is\n * not a failure — the directory is still deleted and the uninstall still completes — so it is an\n * outcome rather than a throw, and the outcome is that nothing was seen to go.\n */\nfunction nothingWasObserved(): PluginRemovalOutcome {\n return \"absent\";\n}\n\n/**\n * Removes exactly the manifest files the plan's config entry names from the source folder,\n * then the source folder itself when it has nothing else left. User-owned content in it\n * (e.g. ejected templates) keeps the directory alive. No entry\n * means the plan promised no manifest removal, so none is made.\n */\nasync function removeConfigManifest(\n entry: ConfigManifestRemoval | undefined,\n): Promise<{ manifestRemoved: boolean; dirRemoved: boolean }> {\n if (!entry) return { manifestRemoved: false, dirRemoved: false };\n\n await Promise.all(\n entry.fileNames.map((fileName) => remove(path.join(entry.sourceDir, fileName))),\n );\n\n return { manifestRemoved: true, dirRemoved: await removeDirIfEmpty(entry.sourceDir) };\n}\n\n/**\n * Removes the CLI config manifest the plan named, then cleans up the emptied .claude/\n * directory and the emptied source folder.\n *\n * The directories themselves are not plan entries — they are the user's, and are removed\n * only when nothing of theirs is left in them once the CLI-managed contents are gone.\n */\nasync function cleanupEmptyDirs(\n target: Pick<UninstallTarget, \"hasClaudeDir\" | \"claudeDir\" | \"sourceRoot\" | \"sourceFolders\">,\n manifestEntries: readonly ConfigManifestRemoval[],\n): Promise<CleanupResult> {\n // In parallel: the two folders are siblings under the scope root, and neither holds the other.\n const sourceFolders = await Promise.all(\n target.sourceFolders.map((folder) =>\n cleanupSourceFolder(\n folder,\n manifestEntries.find((entry) => entry.sourceDir === folder.dir),\n ),\n ),\n );\n const providerDirRemoved = sourceFolders.some((folder) => !folder.legacy && folder.dirRemoved);\n const sourceRoot = await cleanupSourceRoot(target.sourceRoot, providerDirRemoved);\n\n const claudeDirRemoved = target.hasClaudeDir && (await removeDirIfEmpty(target.claudeDir));\n // Nothing else removes .claude itself, so \"kept\" is exactly \"present but not removed\".\n const claudeDirKept =\n !claudeDirRemoved && target.hasClaudeDir && (await directoryExists(target.claudeDir));\n\n return {\n claudeDirRemoved,\n sourceFolders,\n sourceRootRemoved: sourceRoot.removed,\n sourceRootKept: sourceRoot.kept,\n claudeDirKept,\n };\n}\n\n/**\n * One source folder, once the plan's manifest files are out of it: removed when nothing is left,\n * kept when the user's own content is. A folder the plan named no file in is still removed when it\n * is empty — an empty rival folder is what `mkdir` or a half-finished move leaves, and it is the\n * product's name on a folder holding nothing.\n */\nasync function cleanupSourceFolder(\n folder: ScopeSourceFolder,\n entry: ConfigManifestRemoval | undefined,\n): Promise<SourceFolderCleanup> {\n const manifest = await removeConfigManifest(entry);\n const dirRemoved = manifest.dirRemoved || (await removeDirIfEmpty(folder.dir));\n const kept = !dirRemoved && (await directoryExists(folder.dir));\n return {\n relName: folder.relName,\n legacy: folder.legacy,\n manifestRemoved: manifest.manifestRemoved,\n dirRemoved,\n kept,\n };\n}\n\n/** What a cleanup pass did with the `.agents-inc/` parent: took it away, or deliberately left it. */\ntype SourceRootCleanup = { removed: boolean; kept: boolean };\n\n/**\n * The provider folder's `.agents-inc/` parent, once the folder under it has gone.\n *\n * Cleanup above is leaf-first — `removeDirIfEmpty` on the folder the config was in — so nothing\n * looks one level up, and `.agents-inc/` left standing is a directory naming this product with\n * nothing installed under it. A later `init` and `doctor` both read that as a half-built layout.\n *\n * Only when it is empty, and only through the same `removeDirIfEmpty` the leaf uses: a consuming\n * repository keeps its own state under this parent — the gate's `baseline.json` is the live case\n * — and that state is not this CLI's to remove. Keeping it is then SAID rather than done in\n * silence, because a directory left where an install was reads as one the uninstall forgot.\n *\n * Attempted only when the provider folder itself went: while that folder is still there the\n * parent is not empty for a reason the user has already been told about, and a second line about\n * it would name the wrong cause.\n */\nasync function cleanupSourceRoot(\n sourceRoot: SourceRoot | null,\n providerDirRemoved: boolean,\n): Promise<SourceRootCleanup> {\n if (sourceRoot === null || !providerDirRemoved) return { removed: false, kept: false };\n\n const removed = await removeDirIfEmpty(sourceRoot.dir);\n return { removed, kept: !removed && (await directoryExists(sourceRoot.dir)) };\n}\n\nasync function listAgentFiles(agentsDir: string, codec: AgentCodec): Promise<string[]> {\n try {\n return await listAgentFilesOf(agentsDir, codec);\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/commands/uninstall.tsx"],"sourcesContent":["import React from \"react\";\nimport { partition } from \"remeda\";\nimport path from \"path\";\n\nimport { Flags } from \"@oclif/core\";\n\nimport { BaseCommand } from \"../base-command\";\nimport {\n RemovalPlanConfirm,\n type RemovalPlanSection,\n} from \"../components/common/removal-plan-confirm.js\";\nimport { promptConfirm } from \"../components/common/prompt-confirm.js\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport {\n directoryExists,\n fileExists,\n listDirectories,\n remove,\n removeDirIfEmpty,\n} from \"../utils/fs\";\nimport { listAgentFilesOf, splitAgentsByProvenance } from \"../lib/agents\";\nimport { hostAt } from \"../lib/hosts/host-for.js\";\nimport type { PluginHost, PluginRemovalOutcome } from \"../lib/hosts/plugin-host.js\";\nimport {\n listPluginNames,\n getProjectPluginsDir,\n buildMarketplacePluginRef,\n parseMarketplacePluginRef,\n} from \"../lib/plugins/index\";\nimport { readForkedFromMetadata } from \"../lib/skills/index\";\nimport { isHomeDirectory, resolveInstallPaths } from \"../lib/installation/index\";\nimport { lazyGateDeps, mutateGlobal, propagateGlobalRemoval } from \"../lib/config-gate/index.js\";\nimport { loadSkillsMatrixFromSource } from \"../lib/loading\";\nimport { loadAgentDefs } from \"../lib/operations\";\nimport { ConfigLoadError, loadProjectConfigAt } from \"../lib/configuration/project-config\";\nimport type { Provider } from \"../consts\";\nimport { CLAUDE_DIR, DEFAULT_BRANDING, EJECT_SOURCE, STANDARD_FILES } from \"../consts\";\nimport {\n providerInUse,\n agentCodec,\n type AgentCodec,\n sourceFolderInUse,\n sourceRootOf,\n type SourceRoot,\n} from \"../lib/installation/install-layout.js\";\nimport {\n providerFlag,\n providerNamedBy,\n refuseAnAmbiguousInstallation,\n} from \"../lib/installation/provider-flag.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes\";\nimport {\n SUCCESS_MESSAGES,\n INFO_MESSAGES,\n UNINSTALL_PLAN,\n compiledAgentsRemoval,\n localSkillsRemoval,\n notInstalledHere,\n unmarkedAgentsKept,\n registeredProjectsUpdated,\n registeredProjectUpdateSkipped,\n registeredProjectsUpdateFailed,\n uninstallConfigUnreadable,\n uninstallNeedsTerminal,\n} from \"../utils/messages\";\nimport type { AgentDefinition, AgentName, MergedSkillsMatrix, ProjectConfig } from \"../types/index\";\nimport type { SkillScope } from \"../types/config\";\n\n/**\n * One removal this run promises AND makes. Each entry carries what its remover needs\n * — paths and names, never display strings — so the preview and the removal are two\n * readings of one value rather than two derivations of the same target.\n */\ntype RemovalEntry =\n | { kind: \"plugins\"; pluginsDir: string; names: string[] }\n | { kind: \"skills\"; skillsDir: string }\n | { kind: \"agents\"; agentsDir: string; agentNames: string[]; codec: AgentCodec }\n | { kind: \"config\"; sourceDir: string; fileNames: string[] };\n\n/** The single entry of a kind, for the remover that consumes it. */\ntype PluginRemoval = Extract<RemovalEntry, { kind: \"plugins\" }>;\ntype SkillsRemoval = Extract<RemovalEntry, { kind: \"skills\" }>;\ntype AgentsRemoval = Extract<RemovalEntry, { kind: \"agents\" }>;\ntype ConfigManifestRemoval = Extract<RemovalEntry, { kind: \"config\" }>;\n\n/**\n * The uninstall removal plan: what this run removes, and what it deliberately leaves\n * behind. Built once per run — the confirm UI renders it, the executor consumes the\n * same value, so the plan cannot name a removal the run declines to make.\n */\ntype RemovalPlan = {\n entries: RemovalEntry[];\n /** Statements naming content that stays, and why — see {@link keptStatements}. */\n kept: string[];\n};\n\n/**\n * Pure builder for the uninstall removal plan — the one place that decides what this\n * run removes. Every removal downstream is driven by an entry from here, and nothing\n * downstream re-reads the target to decide whether to act.\n */\nfunction buildRemovalPlan(target: UninstallTarget): RemovalPlan {\n const agents = compiledAgentsEntry(target);\n\n return {\n entries: [\n ...pluginsEntry(target),\n ...localSkillsEntry(target),\n ...agents,\n ...configManifestEntries(target),\n ],\n kept: keptStatements(target, agents),\n };\n}\n\n/** The plan carries at least one removal — the only thing that makes this run an uninstall. */\nfunction hasAnythingToRemove(plan: RemovalPlan): boolean {\n return plan.entries.length > 0;\n}\n\nfunction pluginsEntry(target: UninstallTarget): PluginRemoval[] {\n if (!target.hasPlugins) return [];\n return [{ kind: \"plugins\", pluginsDir: target.pluginsDir, names: [...target.cliPluginNames] }];\n}\n\nfunction localSkillsEntry(target: UninstallTarget): SkillsRemoval[] {\n if (!target.hasLocalSkills) return [];\n return [{ kind: \"skills\", skillsDir: target.skillsDir }];\n}\n\nfunction compiledAgentsEntry(target: UninstallTarget): AgentsRemoval[] {\n if (!target.hasLocalAgents) return [];\n\n const agentNames = identifiableAgents(target);\n if (agentNames.length === 0) return [];\n\n return [{ kind: \"agents\", agentsDir: target.agentsDir, agentNames, codec: target.agentCodec }];\n}\n\n/**\n * The agent files this run can say are its own, and on whose authority.\n *\n * The configuration is the first: it names the agents this install compiled, and a marker-less\n * file it names is still this CLI's — every install predating the marker is in exactly that\n * state. The provenance marker the compiler stamps into every agent is the second, reached\n * when there is no configuration left to read: provably ours, whoever lost the config. With\n * neither, nothing here is identifiable and the plan carries no removal for it.\n */\nfunction identifiableAgents(target: UninstallTarget): string[] {\n if (target.configuredAgents.length > 0) return target.configuredAgents;\n return target.markedAgents;\n}\n\n/** The CLI config manifest (config.ts + config-types.ts) is always removed from the source folder. */\nfunction configManifestEntries(target: UninstallTarget): ConfigManifestRemoval[] {\n const { dir, manifestFiles } = target.sourceFolder;\n if (manifestFiles.length === 0) return [];\n return [{ kind: \"config\", sourceDir: dir, fileNames: manifestFiles }];\n}\n\n/**\n * The plan's kept half: the agent files carrying no provenance marker that the removal above\n * does not claim anyway. Derived FROM the removal rather than from a second reading of the\n * target, so the two halves of the plan cannot contradict each other — an agent named in both\n * lists would be a promise to remove a file the same plan calls kept.\n */\nfunction keptStatements(target: UninstallTarget, agentRemovals: AgentsRemoval[]): string[] {\n const removing = new Set(agentRemovals.flatMap((entry) => entry.agentNames));\n const kept = target.unmarkedAgents.filter((name) => !removing.has(name));\n if (kept.length === 0) return [];\n return [unmarkedAgentsKept(target.agentsDir, kept.length)];\n}\n\n/** Every entry of a kind — the config kind has one per source folder. */\nfunction planEntries<K extends RemovalEntry[\"kind\"]>(\n plan: RemovalPlan,\n kind: K,\n): Extract<RemovalEntry, { kind: K }>[] {\n return plan.entries.filter(\n (entry): entry is Extract<RemovalEntry, { kind: K }> => entry.kind === kind,\n );\n}\n\n/** The plan's entry of a kind, for the remover that consumes it. At most one exists. */\nfunction planEntry<K extends RemovalEntry[\"kind\"]>(\n plan: RemovalPlan,\n kind: K,\n): Extract<RemovalEntry, { kind: K }> | undefined {\n return plan.entries.find(\n (entry): entry is Extract<RemovalEntry, { kind: K }> => entry.kind === kind,\n );\n}\n\n/** The heading an entry prints under, and the lines it contributes beneath it. */\nfunction describeEntry(entry: RemovalEntry): RemovalPlanSection {\n switch (entry.kind) {\n case \"plugins\":\n return { label: UNINSTALL_PLAN.PLUGINS_HEADING, items: entry.names };\n case \"skills\":\n return {\n label: UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n items: [localSkillsRemoval(entry.skillsDir)],\n };\n case \"agents\":\n return {\n label: UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n items: [compiledAgentsRemoval(entry.agentsDir)],\n };\n case \"config\":\n return {\n label: UNINSTALL_PLAN.CONFIG_HEADING,\n items: entry.fileNames.map((fileName) => `${entry.sourceDir}/${fileName}`),\n };\n default: {\n const _exhaustive: never = entry;\n return _exhaustive;\n }\n }\n}\n\nconst PLAN_SECTION_ORDER = [\n UNINSTALL_PLAN.PLUGINS_HEADING,\n UNINSTALL_PLAN.CLI_MANAGED_FILES_HEADING,\n UNINSTALL_PLAN.CONFIG_HEADING,\n] as const;\n\n/**\n * The plan's display half: entries grouped under their heading, in printing order —\n * skills and compiled agents share one. A heading is a promise about the lines beneath\n * it, so a heading no entry contributed to is not printed at all. The single source of\n * the strings shared by printRemovalPlan (plain text) and the UninstallConfirm Ink\n * component; each renderer only adds its own indentation and styling.\n */\nfunction planSections(entries: readonly RemovalEntry[]): RemovalPlanSection[] {\n const described = entries.map(describeEntry);\n\n return PLAN_SECTION_ORDER.flatMap((label) =>\n sectionWithItems(label, itemsUnder(described, label)),\n );\n}\n\n/** Every line the described entries contributed under one heading, in plan order. */\nfunction itemsUnder(described: readonly RemovalPlanSection[], label: string): string[] {\n return described.filter((section) => section.label === label).flatMap((section) => section.items);\n}\n\nfunction sectionWithItems(label: string, items: string[]): RemovalPlanSection[] {\n if (items.length === 0) return [];\n return [{ label, items }];\n}\n\nconst UNINSTALL_CONFIRM_MESSAGE = \"Are you sure you want to uninstall?\";\n\nexport default class Uninstall extends BaseCommand {\n /**\n * The name this run prints itself under, resolved once in {@link run} because three separate\n * lines carry it: the heading, the sign-off, and the warning naming the tool a preserved\n * directory was not created by. The last of those sits three calls below the resolution, and\n * threading a display string through `executeUninstall` -> `removeLocalFiles` ->\n * `removePlannedSkills` would widen three signatures to deliver one word.\n *\n * A plain `string` holding the shipped default rather than an optional every reader would have\n * to answer for. `run` replaces it before the first line is printed, so what a user sees is\n * always what their configuration says.\n */\n private brandingName: string = DEFAULT_BRANDING.NAME;\n\n static summary = `Remove ${DEFAULT_BRANDING.NAME} from this project`;\n\n static description = `Uninstall ${DEFAULT_BRANDING.NAME} from this project. Removes CLI-managed skills (matched by marketplace), compiled agents, plugins, and the config manifest (config.ts + config-types.ts) this project keeps. User-created content is preserved.`;\n\n static examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --yes\",\n ];\n\n static flags = {\n yes: Flags.boolean({\n char: \"y\",\n description: \"Skip confirmation prompt\",\n default: false,\n }),\n provider: providerFlag(),\n };\n\n /**\n * Starts at any terminal size. The size gate exists for the wizard's layout, and this command\n * draws none — a plan and a one-line confirm fit any terminal — while it is the way out of every\n * state, so a window too small to hold a wizard must not hold up an uninstall.\n */\n protected async ensureTerminalSize(): Promise<void> {\n // Nothing to wait for — see above.\n }\n\n async run(): Promise<void> {\n const { flags } = await this.parse(Uninstall);\n const projectDir = process.cwd();\n\n // Before the branding read and before anything is detected: this run is destructive, and a\n // scope holding two installations has two answers to \"which one\". Removing the roster's first\n // is removing a user's other installation with nothing on screen to say so.\n await refuseAnAmbiguousInstallation(\n projectDir,\n \"uninstall\",\n providerNamedBy(flags.provider),\n (message) => this.error(message, { exit: EXIT_CODES.INVALID_ARGS }),\n );\n\n this.brandingName = await this.resolveBrandingName(projectDir);\n\n this.printHeader();\n\n const target = await detectUninstallTarget(projectDir, (failure) =>\n this.warn(uninstallConfigUnreadable(failure)),\n );\n const plan = buildRemovalPlan(target);\n if (!hasAnythingToRemove(plan)) {\n this.reportNothingToUninstall();\n return;\n }\n\n const confirmed = flags.yes ? this.printRemovalPlan(plan) : await this.confirmRemoval(plan);\n if (!confirmed) {\n this.log(\"\");\n this.log(\"Uninstall cancelled\");\n this.exit(EXIT_CODES.CANCELLED);\n }\n\n await this.executeUninstall(plan, target, projectDir);\n this.reportSuccess();\n }\n\n private printHeader(): void {\n this.log(\"\");\n this.log(`${this.brandingName} Uninstall`);\n this.log(\"\");\n }\n\n private reportNothingToUninstall(): void {\n this.warn(\"Nothing to uninstall.\");\n this.log(\"\");\n this.log(notInstalledHere(this.brandingName));\n this.log(\"\");\n this.log(INFO_MESSAGES.NO_CHANGES_MADE);\n }\n\n private printRemovalPlan(plan: RemovalPlan): true {\n this.log(UNINSTALL_PLAN.PREVIEW_HEADING);\n this.log(\"\");\n\n for (const section of planSections(plan.entries)) {\n this.log(` ${section.label}`);\n for (const item of section.items) {\n this.log(` ${item}`);\n }\n }\n\n for (const statement of plan.kept) {\n this.log(\"\");\n this.log(statement);\n }\n\n this.log(\"\");\n return true;\n }\n\n private async confirmRemoval(plan: RemovalPlan): Promise<boolean> {\n // A confirm nobody can answer must never become a yes, and mounting it here died on Ink's\n // raw-mode error — so a run with no terminal is refused before it, naming `--yes`.\n if (!process.stdin.isTTY) {\n this.error(uninstallNeedsTerminal(), { exit: EXIT_CODES.ERROR });\n }\n\n const outcome = await promptConfirm(({ onConfirm, onCancel }) => (\n <RemovalPlanConfirm\n heading={UNINSTALL_PLAN.PREVIEW_HEADING}\n sections={planSections(plan.entries)}\n statements={plan.kept}\n message={UNINSTALL_CONFIRM_MESSAGE}\n onConfirm={onConfirm}\n onCancel={onCancel}\n />\n ));\n return outcome === \"confirmed\";\n }\n\n private async executeUninstall(\n plan: RemovalPlan,\n target: UninstallTarget,\n projectDir: string,\n ): Promise<void> {\n const isGlobalUninstall = isHomeDirectory(projectDir);\n // Prepared BEFORE any removal: the projects[] registry and the source used\n // to regenerate each project's config-types.ts both live in the global\n // config this uninstall is about to delete.\n const propagation = isGlobalUninstall\n ? await this.prepareGlobalPropagation(target, projectDir)\n : null;\n\n await this.removePlannedPlugins(planEntry(plan, \"plugins\"), target.config, projectDir);\n\n try {\n await this.removeLocalFiles(plan, target);\n } catch (error) {\n this.log(\"Failed to remove local files\");\n this.error(getErrorMessage(error), {\n exit: EXIT_CODES.ERROR,\n });\n }\n\n if (isGlobalUninstall) {\n // The global manifest is gone — prune the inlined global-scoped entries the\n // registered projects still carry so they stop referencing removed content.\n if (propagation) {\n await this.updateRegisteredProjects(propagation);\n }\n return;\n }\n\n // Deregister this project from the global config's tracked projects so future\n // global edits stop propagating back into it. Best-effort by nature: a missing,\n // project-less, or corrupt global config (ConfigLoadError) must never fail the\n // uninstall — warn and move on.\n try {\n // Registration bookkeeping only: nothing inlines the `projects[]` list into a\n // project config, so this loads neither the matrix nor the agent definitions\n // and the uninstall stays offline.\n await mutateGlobal({ kind: \"deregister-project\", projectDir }, lazyGateDeps(projectDir));\n } catch (error) {\n this.warn(`Could not update the global project registry: ${getErrorMessage(error)}`);\n }\n }\n\n /**\n * Loads what updating registered projects needs (global config with its\n * projects[] registry, skills matrix, agent definitions) BEFORE the global\n * the config manifest is deleted — source resolution reads that config.\n * Returns null when nothing is registered or loading fails; a load failure\n * warns and never aborts the uninstall.\n *\n * `skipExtraSources: true` is NOT a divergence from the wizard's full\n * multi-source load: extra-source loading only annotates each skill's\n * `availableSources`/`activeSource` for wizard UI tagging — it never adds\n * skills or categories to the matrix, and the config-types writer never reads\n * those annotations, so the regenerated project types are byte-identical\n * either way (pinned by the skipExtraSources parity test in\n * local-installer.test.ts). Skipping avoids fetching every registered extra\n * source (network on a cold cache, plus unreachable-remote warnings) during\n * an uninstall that must never hang or noise-fail on remote state.\n *\n * `purpose: \"upkeep\"` because the prune only rewrites projects that are already installed: a\n * marketplace that has since lost its manifest must not leave every registered project naming\n * the global content this uninstall deletes.\n */\n private async prepareGlobalPropagation(\n target: UninstallTarget,\n projectDir: string,\n ): Promise<GlobalPropagationData | null> {\n if (target.config === null || target.registeredProjects.length === 0) return null;\n const globalConfig = target.config;\n\n try {\n const [sourceResult, agentDefs] = await Promise.all([\n loadSkillsMatrixFromSource({\n projectDir,\n purpose: \"upkeep\",\n skipExtraSources: true,\n matrixOnly: true,\n }),\n loadAgentDefs(),\n ]);\n return {\n globalConfig,\n matrix: sourceResult.matrix,\n agents: agentDefs.agents,\n provider: providerInUse(projectDir),\n };\n } catch (error) {\n this.warn(registeredProjectsUpdateFailed(getErrorMessage(error)));\n return null;\n }\n }\n\n /**\n * Prunes the inlined global-scoped config entries from every registered\n * project and regenerates its config-types.ts. Runs AFTER the global manifest\n * removal so the regenerated project types fall back to the standalone form\n * instead of importing from the deleted global config-types.ts. Best-effort:\n * unreachable projects are warned and skipped, and no failure here may abort\n * the uninstall.\n */\n private async updateRegisteredProjects(propagation: GlobalPropagationData): Promise<void> {\n try {\n const report = await propagateGlobalRemoval(\n propagation.globalConfig,\n { matrix: propagation.matrix, agents: propagation.agents },\n propagation.provider,\n );\n const { updated, unreadable, failed } = report.propagated;\n this.reportProjectsOutOfReach(report.propagated);\n // Uninstall's own sentence, only where it is true: the config is there and was not pruned.\n for (const skippedPath of [...unreadable, ...failed]) {\n this.warn(registeredProjectUpdateSkipped(skippedPath));\n }\n if (updated.length === 0) return;\n\n this.logSuccess(registeredProjectsUpdated(updated.length));\n\n // The pruned projects' compiled agents were built from the global rows this\n // uninstall just removed, so the prune owes them a recompile too.\n this.reportPropagatedRecompile(report);\n } catch (error) {\n this.warn(registeredProjectsUpdateFailed(getErrorMessage(error)));\n }\n }\n\n private reportSuccess(): void {\n this.log(\"\");\n this.log(`${this.brandingName} has been uninstalled.`);\n this.log(\"\");\n this.logSuccess(SUCCESS_MESSAGES.UNINSTALL_COMPLETE);\n this.log(\"\");\n }\n\n /**\n * Each step acts only when the plan carries its entry, so what the user was shown and\n * what this run removes are the same list read twice.\n */\n private async removeLocalFiles(plan: RemovalPlan, target: UninstallTarget): Promise<void> {\n await this.removePlannedSkills(planEntry(plan, \"skills\"));\n await this.removePlannedAgents(planEntry(plan, \"agents\"));\n\n // The same statements the preview showed, from the same plan: what the user approved and\n // what this run reports are one value read twice, never two derivations that agree today.\n for (const statement of plan.kept) {\n this.log(statement);\n }\n\n const cleanup = await cleanupEmptyDirs(target, planEntries(plan, \"config\"));\n\n this.reportSourceFolderCleanup(cleanup.sourceFolder);\n\n this.reportSourceRootCleanup(target.sourceRoot, cleanup);\n\n if (cleanup.claudeDirRemoved) {\n this.logSuccess(`Removed ${CLAUDE_DIR}/`);\n } else if (cleanup.claudeDirKept) {\n this.log(`Kept ${CLAUDE_DIR}/ (contains user content)`);\n }\n }\n\n /** What became of one source folder: gone, emptied of the CLI's files, or kept for the user's. */\n private reportSourceFolderCleanup(folder: SourceFolderCleanup): void {\n if (folder.dirRemoved) {\n this.logSuccess(`Removed ${folder.relName}/`);\n } else if (folder.manifestRemoved) {\n this.logSuccess(`Removed CLI config from ${folder.relName}/`);\n } else if (folder.kept) {\n this.log(`Kept ${folder.relName}/ (contains user content)`);\n }\n }\n\n /** What became of the `.agents-inc/` parent, said out loud in both directions. */\n private reportSourceRootCleanup(sourceRoot: SourceRoot, cleanup: CleanupResult): void {\n if (cleanup.sourceRootRemoved) {\n this.logSuccess(`Removed ${sourceRoot.relName}/`);\n return;\n }\n if (cleanup.sourceRootKept) {\n this.log(`Kept ${sourceRoot.relName}/ (contains user content)`);\n }\n }\n\n private async removePlannedPlugins(\n entry: PluginRemoval | undefined,\n config: ProjectConfig | null,\n projectDir: string,\n ): Promise<void> {\n if (!entry) return;\n\n this.log(\"Uninstalling plugins...\");\n\n try {\n const pluginResult = await uninstallPlugins(entry, config, projectDir, (name) =>\n this.log(` Uninstalled plugin '${name}'`),\n );\n\n // Silent on zero rather than \"Uninstalled 0 plugins\": the line's job is to account for\n // removals, and a run that observed none has nothing to account for — the plan preview has\n // already said what it expected to find. Saying \"0\" beside a plan that named one reads as a\n // failure, which it is not: a plugin a user removed themselves is the ordinary case.\n if (pluginResult.totalUninstalled > 0) {\n this.logSuccess(\n `Uninstalled ${pluginResult.totalUninstalled} ${pluginResult.totalUninstalled === 1 ? \"plugin\" : \"plugins\"}`,\n );\n }\n } catch (error) {\n this.log(\"Plugin uninstall failed\");\n this.error(getErrorMessage(error), {\n exit: EXIT_CODES.ERROR,\n });\n }\n }\n\n private async removePlannedSkills(entry: SkillsRemoval | undefined): Promise<void> {\n if (!entry) return;\n\n const result = await removeMatchingSkills(\n entry,\n (dirName) => this.log(` Uninstalled skill '${dirName}'`),\n (dirName) => this.warn(`Skipping '${dirName}': not created by ${this.brandingName} CLI`),\n );\n if (result.removedCount === 0) return;\n\n this.logSuccess(\n `Removed ${result.removedCount} CLI-installed ${result.removedCount === 1 ? \"skill\" : \"skills\"}`,\n );\n }\n\n private async removePlannedAgents(entry: AgentsRemoval | undefined): Promise<void> {\n if (!entry) return;\n\n const result = await removeMatchingAgents(entry, (agentName) =>\n this.log(` Uninstalled agent '${agentName}'`),\n );\n if (result.removedCount === 0) return;\n\n this.logSuccess(\n `Removed ${result.removedCount} compiled ${result.removedCount === 1 ? \"agent\" : \"agents\"}`,\n );\n }\n}\n\n/** @internal Exported for testing */\nexport type UninstallTarget = {\n hasPlugins: boolean;\n pluginNames: string[];\n /** Plugin names filtered to only those installed by this CLI (matched against config skills) */\n cliPluginNames: string[];\n hasLocalSkills: boolean;\n hasLocalAgents: boolean;\n hasClaudeDir: boolean;\n pluginsDir: string;\n skillsDir: string;\n agentsDir: string;\n /**\n * How this installation's host names and marks a compiled agent — `*.md` on Claude, `*.toml` on\n * Codex. Everything the plan reads or removes in `agentsDir` goes through it (CLI-896).\n */\n agentCodec: AgentCodec;\n claudeDir: string;\n /** This scope's source folder: what this run reads and removes in it. */\n sourceFolder: ScopeSourceFolder;\n /** The `.agents-inc/` parent the source folder sits under. */\n sourceRoot: SourceRoot;\n /** The source folder's config, when it is readable. */\n config: ProjectConfig | null;\n /** Every project the config registers — only a global config registers any. */\n registeredProjects: string[];\n /** Agent names the config names (e.g., [\"web-developer\"]) */\n configuredAgents: AgentName[];\n /** On-disk agent basenames carrying this CLI's provenance marker. */\n markedAgents: string[];\n /** On-disk agent basenames carrying no marker — the user's, unless the config claims them. */\n unmarkedAgents: string[];\n};\n\n/** The source folder of the scope being uninstalled, and what this run reads and removes in it. */\ntype ScopeSourceFolder = {\n dir: string;\n /** The folder as a user writes it: `.agents-inc/claude`. */\n relName: string;\n /** The manifest files on disk in it — `config.ts`, `config-types.ts` — which this run removes. */\n manifestFiles: string[];\n /** Its config, or `null` when the file is absent or unreadable — see {@link loadUninstallConfig}. */\n config: ProjectConfig | null;\n};\n\n/**\n * Everything a global uninstall needs to update the registered projects after\n * the global manifest is removed — captured before removal because it is\n * sourced from the config being deleted.\n */\ntype GlobalPropagationData = {\n globalConfig: ProjectConfig;\n matrix: MergedSkillsMatrix;\n agents: Partial<Record<AgentName, AgentDefinition>>;\n /**\n * Which installation is being removed.\n *\n * Captured here for the same reason everything else on this type is: by the time the prune\n * runs, the folder that says which provider this was has been deleted, so reading it then\n * would answer about whatever is left rather than about what was removed.\n */\n provider: Provider;\n};\n\ntype SkillRemovalResult = {\n removedCount: number;\n skippedCount: number;\n removedNames: string[];\n skippedNames: string[];\n /** Whether the skills directory was cleaned up (empty after removal) */\n dirCleaned: boolean;\n};\n\ntype AgentRemovalResult = {\n removedCount: number;\n removedNames: string[];\n /** Whether the agents directory was cleaned up (empty after removal) */\n dirCleaned: boolean;\n};\n\ntype UninstallPluginsResult = {\n uninstalledNames: string[];\n totalUninstalled: number;\n};\n\n/** What a cleanup pass did with the source folder. */\ntype SourceFolderCleanup = {\n relName: string;\n /** Whether config.ts/config-types.ts were removed from it */\n manifestRemoved: boolean;\n /** Whether the emptied folder itself was removed */\n dirRemoved: boolean;\n /** Whether it is still there because something this CLI never wrote lives in it */\n kept: boolean;\n};\n\ntype CleanupResult = {\n claudeDirRemoved: boolean;\n sourceFolder: SourceFolderCleanup;\n /** Whether the emptied `.agents-inc/` parent went with the last provider folder under it */\n sourceRootRemoved: boolean;\n /** Whether `.agents-inc/` still exists because something this CLI never wrote lives in it */\n sourceRootKept: boolean;\n /** Whether .claude/ still exists with user content after cleanup */\n claudeDirKept: boolean;\n};\n\nfunction collectConfiguredAgents(config: Partial<ProjectConfig> | null): AgentName[] {\n if (!config?.agents) return [];\n return config.agents.map((a) => a.name);\n}\n\n/** @internal Exported for testing */\nexport function getCliInstalledPluginKeys(config: Partial<ProjectConfig> | null): Set<string> {\n if (!config?.skills) return new Set();\n const { marketplaceName } = config;\n return new Set(\n config.skills.flatMap((skill) => [\n // Primary key: skill.id@skill.origin\n buildMarketplacePluginRef(skill.id, skill.origin),\n // Marketplace variant for plugins installed via marketplace where\n // skill.origin may differ (e.g., \"eject\" vs the marketplace name)\n ...(marketplaceName && skill.origin !== marketplaceName && skill.origin !== EJECT_SOURCE\n ? [buildMarketplacePluginRef(skill.id, marketplaceName)]\n : []),\n ]),\n );\n}\n\n/**\n * Loads the config that drives the removal plan. A config file that exists but\n * cannot be parsed (`ConfigLoadError`) is handed to `onLoadFailed` and\n * then treated exactly like a missing one — an unreadable config is precisely\n * when a user needs to uninstall, so it must never fail the run. Same posture as\n * the `deregister-project` mutation's call site above. Only the plan degrades: the plugins and\n * compiled agents the config named can no longer be identified, while file\n * removal proceeds. Any other failure is a real fault and still propagates.\n */\nasync function loadUninstallConfig(\n configPath: string,\n projectDir: string,\n provider: Provider,\n onLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<ProjectConfig | null> {\n try {\n const result = await loadProjectConfigAt(configPath, projectDir, provider);\n return result?.config ?? null;\n } catch (error) {\n if (!(error instanceof ConfigLoadError)) throw error;\n onLoadFailed(error);\n return null;\n }\n}\n\n/** The folder's manifest files on disk, and its config read under uninstall's degrade posture. */\nasync function readSourceFolder(\n folder: Pick<ScopeSourceFolder, \"dir\" | \"relName\">,\n projectDir: string,\n provider: Provider,\n onConfigLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<ScopeSourceFolder> {\n const [manifestFiles, config] = await Promise.all([\n manifestFilesIn(folder.dir),\n loadUninstallConfig(\n path.join(folder.dir, STANDARD_FILES.CONFIG_TS),\n projectDir,\n provider,\n onConfigLoadFailed,\n ),\n ]);\n return { ...folder, manifestFiles, config };\n}\n\n/** The files of the CLI config manifest — `config.ts`, `config-types.ts` — that are in `dir`. */\nasync function manifestFilesIn(dir: string): Promise<string[]> {\n const manifest = [STANDARD_FILES.CONFIG_TS, STANDARD_FILES.CONFIG_TYPES_TS];\n const present = await Promise.all(\n manifest.map((fileName) => fileExists(path.join(dir, fileName))),\n );\n return manifest.filter((_, index) => present[index]);\n}\n\n/** A config with its excluded rows dropped — the entries an uninstall acts on. */\nfunction activeConfigOf(config: ProjectConfig): ProjectConfig {\n return {\n ...config,\n skills: config.skills.filter((s) => !s.excluded),\n agents: config.agents.filter((a) => !a.excluded),\n };\n}\n\n/**\n * Detects what's installed in a project directory for uninstallation.\n *\n * Checks for plugins, local skills, agents, config directories, and\n * resolves which plugins were installed by this CLI.\n */\nasync function detectUninstallTarget(\n projectDir: string,\n onConfigLoadFailed: (failure: ConfigLoadError) => void,\n): Promise<UninstallTarget> {\n const pluginsDir = getProjectPluginsDir(projectDir);\n const { skillsDir, agentsDir } = resolveInstallPaths(projectDir);\n const claudeDir = path.join(projectDir, CLAUDE_DIR);\n const provider = providerInUse(projectDir);\n\n const [hasLocalSkills, hasLocalAgents, hasClaudeDir, sourceFolder, agentProvenance] =\n await Promise.all([\n directoryExists(skillsDir),\n directoryExists(agentsDir),\n directoryExists(claudeDir),\n readSourceFolder(\n sourceFolderInUse(projectDir, provider),\n projectDir,\n provider,\n onConfigLoadFailed,\n ),\n splitAgentsByProvenance(agentsDir, agentCodec(provider)),\n ]);\n\n const { config } = sourceFolder;\n\n let pluginNames: string[] = [];\n try {\n pluginNames = await listPluginNames(projectDir);\n } catch {\n // Best-effort: plugin detection may fail\n }\n\n const activeConfig = config === null ? null : activeConfigOf(config);\n const configuredAgents = collectConfiguredAgents(activeConfig);\n const cliInstalledKeys = getCliInstalledPluginKeys(activeConfig);\n const cliPluginNames = thisRunOwnsAnyPlugin(projectDir)\n ? pluginNames.filter((name) => cliInstalledKeys.has(name))\n : [];\n\n return {\n hasPlugins: cliPluginNames.length > 0,\n pluginNames,\n cliPluginNames,\n hasLocalSkills,\n hasLocalAgents,\n hasClaudeDir,\n pluginsDir,\n skillsDir,\n agentsDir,\n claudeDir,\n sourceFolder,\n sourceRoot: sourceRootOf(projectDir),\n config,\n registeredProjects: config?.projects ?? [],\n configuredAgents,\n agentCodec: agentCodec(provider),\n markedAgents: agentProvenance.marked,\n unmarkedAgents: agentProvenance.unmarked,\n };\n}\n\n/**\n * Whether this run has any plugin of its own to remove.\n *\n * **A PROJECT uninstall on a host that installs no project-scoped plugin has none, and asking\n * anyway is the one operation in this step that can destroy work nobody asked about.** Codex\n * installs plugins for a MACHINE: no subcommand takes a scope, `plugin add` run inside a project\n * writes the switch to the global config and silently un-scopes it, and `plugin list` answers the\n * same set from every directory. So every plugin a Codex project can see belongs to every other\n * project on that machine, and a project uninstall that swept them would take them all away.\n *\n * Read off {@link PluginHost.installsProjectScopedPlugins} rather than written as a provider\n * check, so a host added later is covered by the flag it already has to set. Claude's answer is\n * `true`, so its sweep is unchanged at both scopes — which is deliberate: whether a Claude project\n * uninstall should drop a user-scoped registration is a separate question about a host that CAN\n * hold both, and nothing here is the place to settle it.\n *\n * A GLOBAL uninstall owns the machine's plugins whatever the host, which is the case the other\n * half of this pair rests on.\n */\nfunction thisRunOwnsAnyPlugin(projectDir: string): boolean {\n if (isHomeDirectory(projectDir)) return true;\n return hostAt(projectDir).installsProjectScopedPlugins;\n}\n\nfunction shouldRemoveSkill(forkedFrom: { source?: string } | null): boolean {\n return forkedFrom !== null;\n}\n\nasync function removeMatchingSkills(\n entry: SkillsRemoval,\n onRemoved?: (dirName: string) => void,\n onSkipped?: (dirName: string) => void,\n): Promise<SkillRemovalResult> {\n const classified = await classifySkillDirs(entry.skillsDir);\n const removedNames = await removeClassifiedSkills(\n classified.toRemove,\n entry.skillsDir,\n onRemoved,\n );\n classified.toSkip.forEach((name) => onSkipped?.(name));\n const dirCleaned = await cleanupSkillsDir(entry.skillsDir, classified.toSkip.length === 0);\n\n return {\n removedCount: removedNames.length,\n skippedCount: classified.toSkip.length,\n removedNames,\n skippedNames: classified.toSkip,\n dirCleaned,\n };\n}\n\nasync function classifySkillDirs(\n skillsDir: string,\n): Promise<{ toRemove: string[]; toSkip: string[] }> {\n const dirNames = await listDirectories(skillsDir);\n const entries = await Promise.all(\n dirNames.map(async (name) => ({\n name,\n forkedFrom: await readForkedFromMetadata(path.join(skillsDir, name)),\n })),\n );\n const [removable, skippable] = partition(entries, (entry) => shouldRemoveSkill(entry.forkedFrom));\n return { toRemove: removable.map((e) => e.name), toSkip: skippable.map((e) => e.name) };\n}\n\nasync function removeClassifiedSkills(\n names: string[],\n skillsDir: string,\n onRemoved?: (name: string) => void,\n): Promise<string[]> {\n for (const name of names) {\n await remove(path.join(skillsDir, name));\n onRemoved?.(name);\n }\n return names;\n}\n\nasync function cleanupSkillsDir(dir: string, allRemoved: boolean): Promise<boolean> {\n if (!allRemoved) return false;\n return removeDirIfEmpty(dir);\n}\n\n/**\n * Removes the compiled agent files the plan's agents entry names — `.md` on Claude, `.toml` on\n * Codex, as the entry's codec says.\n *\n * A file is removed only when its basename is one the entry names; every other agent file\n * is preserved. Cleans up the agents directory if empty after removal. Which names those are\n * — the configured roster, or the files carrying the provenance marker — is the plan's\n * decision, not this function's, and so is whether to act at all: no entry, no call.\n *\n * @param onRemoved - Called for each removed agent name (for logging)\n */\nasync function removeMatchingAgents(\n entry: AgentsRemoval,\n onRemoved?: (agentName: string) => void,\n): Promise<AgentRemovalResult> {\n const agentFiles = await listAgentFiles(entry.agentsDir, entry.codec);\n const removedNames = agentFiles\n .map((agentFile) => path.basename(agentFile, entry.codec.extension))\n .filter((agentName) => entry.agentNames.includes(agentName));\n\n for (const agentName of removedNames) {\n await remove(path.join(entry.agentsDir, `${agentName}${entry.codec.extension}`));\n onRemoved?.(agentName);\n }\n\n const dirCleaned = await removeDirIfEmpty(entry.agentsDir);\n\n return {\n removedCount: removedNames.length,\n removedNames,\n dirCleaned,\n };\n}\n\n/**\n * Where a plugin is looked for FIRST when the config has no entry naming its skill.\n *\n * The scope translation swallowed this until C3 — an absent scope and a project one mapped to\n * the same Claude scope word — so the order is unchanged and only the silence is gone.\n */\nconst SCOPE_OF_AN_UNRECORDED_SKILL: SkillScope = \"project\";\n\n/**\n * Every scope this host installs plugins at, the config's own answer first.\n *\n * A plugin's registered scope is genuinely ambiguous here — a skill re-scoped after install is\n * filed under the scope it was installed at, not the one the config now names — so a removal\n * asks at each scope rather than trusting one. The best-effort sweep this replaces did it with\n * two hard-coded Claude scope words and no way to answer for another host; reading the roster off\n * {@link PluginHost.offeredPlacements} is the same sweep with the host deciding its width, which\n * for a host that installs plugins globally only is one scope rather than two.\n */\nfunction pluginScopesToSweep(host: PluginHost, primary: SkillScope): SkillScope[] {\n const offered = host.offeredPlacements\n .filter((placement) => placement.mode === \"plugin\")\n .map((placement) => placement.scope);\n\n if (!offered.includes(primary)) return offered;\n return [primary, ...offered.filter((scope) => scope !== primary)];\n}\n\n/**\n * Drops a plugin's registration wherever the host filed it, and says whether one was there.\n *\n * Best-effort on the FAILURE side and unchanged by C3: the directory is deleted either way, so a\n * registration that cannot be dropped is untidy rather than wrong, and a thrown error here would\n * abort an uninstall that has already removed files. What changed is that it no longer reports\n * NOTHING — {@link PluginHost.uninstallPlugin} answers `removed` or `absent`, and that answer is\n * the only thing on this path that knows the difference.\n *\n * `removed` if ANY swept scope had one, because the sweep exists precisely for a plugin filed\n * under a scope the config no longer names: one registration dropped at either scope is one\n * removal, not two, and not none.\n */\nasync function removePluginWhereverItIsFiled(\n host: PluginHost,\n pluginRef: string,\n primaryScope: SkillScope,\n projectDir: string,\n): Promise<PluginRemovalOutcome> {\n let outcome: PluginRemovalOutcome = \"absent\";\n\n for (const scope of pluginScopesToSweep(host, primaryScope)) {\n try {\n const swept = await host.uninstallPlugin(pluginRef, scope, projectDir);\n if (swept === \"removed\") outcome = \"removed\";\n } catch {\n // Best-effort: the plugin may not be registered at this scope. A throw is neither outcome\n // — the host reserves it for a failure that is not \"there was nothing there\" — so it leaves\n // `outcome` exactly as the other scopes found it rather than claiming an absence.\n }\n }\n\n return outcome;\n}\n\n/**\n * Uninstalls the plugins the plan's plugins entry names, by removing them from the\n * host and deleting their local directories. The entry decides WHICH plugins go;\n * `config` only answers HOW to ask the host — the scope its registry filed each\n * one under, per-skill where the config says, project-level otherwise.\n *\n * @param onUninstalled - Called for each successfully uninstalled plugin name (for logging)\n * @internal Exported for testing\n */\nexport async function uninstallPlugins(\n entry: PluginRemoval,\n config: ProjectConfig | null,\n projectDir: string,\n onUninstalled?: (pluginName: string) => void,\n): Promise<UninstallPluginsResult> {\n const host = hostAt(projectDir);\n const hostAvailable = await host.isAvailable();\n const observed: string[] = [];\n\n for (const pluginName of entry.names) {\n const removal = hostAvailable\n ? await removePluginWhereverItIsFiled(\n host,\n pluginName,\n scopeThePluginIsFiledUnder(config, pluginName),\n projectDir,\n )\n : nothingWasObserved();\n\n await remove(path.join(entry.pluginsDir, pluginName));\n\n if (removal === \"removed\") {\n observed.push(pluginName);\n onUninstalled?.(pluginName);\n }\n }\n\n // What was OBSERVED, never what was asked for — D11(b), and the one Claude-visible change this\n // step signs off. On Codex `plugin remove` exits 0 and prints the same document for a plugin\n // that was never installed, one that really went and one removed twice, so a count built from\n // the plan is a count of intentions: it says \"Uninstalled 1 plugin\" for a plugin this command\n // did not touch. The listing the host takes BEFORE its removal is the only thing that knows.\n return {\n uninstalledNames: observed,\n totalUninstalled: observed.length,\n };\n}\n\n/**\n * The scope the host's registry most likely filed this plugin under, from the config's own row.\n *\n * The sweep beside it asks at every scope the host installs plugins at, so this is a PREFERENCE\n * rather than an answer: a skill re-scoped after install is filed under the scope it was installed\n * at, not the one the config now names.\n */\nfunction scopeThePluginIsFiledUnder(config: ProjectConfig | null, pluginName: string): SkillScope {\n const skillId = parseMarketplacePluginRef(pluginName);\n return (\n config?.skills.find((skill) => skill.id === skillId)?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL\n );\n}\n\n/**\n * What a run with no host binary observed, which is nothing — the OTHER half of D11(b).\n *\n * This command used to count every plugin it MEANT to remove, so a machine where the host is not\n * installed reported a clean sweep of registrations nothing had touched. An unreachable host is\n * not a failure — the directory is still deleted and the uninstall still completes — so it is an\n * outcome rather than a throw, and the outcome is that nothing was seen to go.\n */\nfunction nothingWasObserved(): PluginRemovalOutcome {\n return \"absent\";\n}\n\n/**\n * Removes exactly the manifest files the plan's config entry names from the source folder,\n * then the source folder itself when it has nothing else left. User-owned content in it\n * (e.g. ejected templates) keeps the directory alive. No entry\n * means the plan promised no manifest removal, so none is made.\n */\nasync function removeConfigManifest(\n entry: ConfigManifestRemoval | undefined,\n): Promise<{ manifestRemoved: boolean; dirRemoved: boolean }> {\n if (!entry) return { manifestRemoved: false, dirRemoved: false };\n\n await Promise.all(\n entry.fileNames.map((fileName) => remove(path.join(entry.sourceDir, fileName))),\n );\n\n return { manifestRemoved: true, dirRemoved: await removeDirIfEmpty(entry.sourceDir) };\n}\n\n/**\n * Removes the CLI config manifest the plan named, then cleans up the emptied .claude/\n * directory and the emptied source folder.\n *\n * The directories themselves are not plan entries — they are the user's, and are removed\n * only when nothing of theirs is left in them once the CLI-managed contents are gone.\n */\nasync function cleanupEmptyDirs(\n target: Pick<UninstallTarget, \"hasClaudeDir\" | \"claudeDir\" | \"sourceRoot\" | \"sourceFolder\">,\n manifestEntries: readonly ConfigManifestRemoval[],\n): Promise<CleanupResult> {\n const sourceFolder = await cleanupSourceFolder(\n target.sourceFolder,\n manifestEntries.find((entry) => entry.sourceDir === target.sourceFolder.dir),\n );\n const sourceRoot = await cleanupSourceRoot(target.sourceRoot, sourceFolder.dirRemoved);\n\n const claudeDirRemoved = target.hasClaudeDir && (await removeDirIfEmpty(target.claudeDir));\n // Nothing else removes .claude itself, so \"kept\" is exactly \"present but not removed\".\n const claudeDirKept =\n !claudeDirRemoved && target.hasClaudeDir && (await directoryExists(target.claudeDir));\n\n return {\n claudeDirRemoved,\n sourceFolder,\n sourceRootRemoved: sourceRoot.removed,\n sourceRootKept: sourceRoot.kept,\n claudeDirKept,\n };\n}\n\n/**\n * The source folder, once the plan's manifest files are out of it: removed when nothing is left,\n * kept when the user's own content is. A folder the plan named no file in is still removed when it\n * is empty — it is the product's name on a folder holding nothing.\n */\nasync function cleanupSourceFolder(\n folder: ScopeSourceFolder,\n entry: ConfigManifestRemoval | undefined,\n): Promise<SourceFolderCleanup> {\n const manifest = await removeConfigManifest(entry);\n const dirRemoved = manifest.dirRemoved || (await removeDirIfEmpty(folder.dir));\n const kept = !dirRemoved && (await directoryExists(folder.dir));\n return {\n relName: folder.relName,\n manifestRemoved: manifest.manifestRemoved,\n dirRemoved,\n kept,\n };\n}\n\n/** What a cleanup pass did with the `.agents-inc/` parent: took it away, or deliberately left it. */\ntype SourceRootCleanup = { removed: boolean; kept: boolean };\n\n/**\n * The provider folder's `.agents-inc/` parent, once the folder under it has gone.\n *\n * Cleanup above is leaf-first — `removeDirIfEmpty` on the folder the config was in — so nothing\n * looks one level up, and `.agents-inc/` left standing is a directory naming this product with\n * nothing installed under it. A later `init` and `doctor` both read that as a half-built layout.\n *\n * Only when it is empty, and only through the same `removeDirIfEmpty` the leaf uses: a consuming\n * repository keeps its own state under this parent — the gate's `baseline.json` is the live case\n * — and that state is not this CLI's to remove. Keeping it is then SAID rather than done in\n * silence, because a directory left where an install was reads as one the uninstall forgot.\n *\n * Attempted only when the provider folder itself went: while that folder is still there the\n * parent is not empty for a reason the user has already been told about, and a second line about\n * it would name the wrong cause.\n */\nasync function cleanupSourceRoot(\n sourceRoot: SourceRoot,\n providerDirRemoved: boolean,\n): Promise<SourceRootCleanup> {\n if (!providerDirRemoved) return { removed: false, kept: false };\n\n const removed = await removeDirIfEmpty(sourceRoot.dir);\n return { removed, kept: !removed && (await directoryExists(sourceRoot.dir)) };\n}\n\nasync function listAgentFiles(agentsDir: string, codec: AgentCodec): Promise<string[]> {\n try {\n return await listAgentFilesOf(agentsDir, codec);\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAkB;AAClB,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AAEjB,SAAS,aAAa;AAkXhB;AAjRN,SAAS,iBAAiB,QAAsC;AAC9D,QAAM,SAAS,oBAAoB,MAAM;AAEzC,SAAO;AAAA,IACL,SAAS;AAAA,MACP,GAAG,aAAa,MAAM;AAAA,MACtB,GAAG,iBAAiB,MAAM;AAAA,MAC1B,GAAG;AAAA,MACH,GAAG,sBAAsB,MAAM;AAAA,IACjC;AAAA,IACA,MAAM,eAAe,QAAQ,MAAM;AAAA,EACrC;AACF;AAGA,SAAS,oBAAoB,MAA4B;AACvD,SAAO,KAAK,QAAQ,SAAS;AAC/B;AAEA,SAAS,aAAa,QAA0C;AAC9D,MAAI,CAAC,OAAO,WAAY,QAAO,CAAC;AAChC,SAAO,CAAC,EAAE,MAAM,WAAW,YAAY,OAAO,YAAY,OAAO,CAAC,GAAG,OAAO,cAAc,EAAE,CAAC;AAC/F;AAEA,SAAS,iBAAiB,QAA0C;AAClE,MAAI,CAAC,OAAO,eAAgB,QAAO,CAAC;AACpC,SAAO,CAAC,EAAE,MAAM,UAAU,WAAW,OAAO,UAAU,CAAC;AACzD;AAEA,SAAS,oBAAoB,QAA0C;AACrE,MAAI,CAAC,OAAO,eAAgB,QAAO,CAAC;AAEpC,QAAM,aAAa,mBAAmB,MAAM;AAC5C,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AAErC,SAAO,CAAC,EAAE,MAAM,UAAU,WAAW,OAAO,WAAW,YAAY,OAAO,OAAO,WAAW,CAAC;AAC/F;AAWA,SAAS,mBAAmB,QAAmC;AAC7D,MAAI,OAAO,iBAAiB,SAAS,EAAG,QAAO,OAAO;AACtD,SAAO,OAAO;AAChB;AAGA,SAAS,sBAAsB,QAAkD;AAC/E,QAAM,EAAE,KAAK,cAAc,IAAI,OAAO;AACtC,MAAI,cAAc,WAAW,EAAG,QAAO,CAAC;AACxC,SAAO,CAAC,EAAE,MAAM,UAAU,WAAW,KAAK,WAAW,cAAc,CAAC;AACtE;AAQA,SAAS,eAAe,QAAyB,eAA0C;AACzF,QAAM,WAAW,IAAI,IAAI,cAAc,QAAQ,CAAC,UAAU,MAAM,UAAU,CAAC;AAC3E,QAAM,OAAO,OAAO,eAAe,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;AACvE,MAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAC/B,SAAO,CAAC,mBAAmB,OAAO,WAAW,KAAK,MAAM,CAAC;AAC3D;AAGA,SAAS,YACP,MACA,MACsC;AACtC,SAAO,KAAK,QAAQ;AAAA,IAClB,CAAC,UAAuD,MAAM,SAAS;AAAA,EACzE;AACF;AAGA,SAAS,UACP,MACA,MACgD;AAChD,SAAO,KAAK,QAAQ;AAAA,IAClB,CAAC,UAAuD,MAAM,SAAS;AAAA,EACzE;AACF;AAGA,SAAS,cAAc,OAAyC;AAC9D,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,OAAO,eAAe,iBAAiB,OAAO,MAAM,MAAM;AAAA,IACrE,KAAK;AACH,aAAO;AAAA,QACL,OAAO,eAAe;AAAA,QACtB,OAAO,CAAC,mBAAmB,MAAM,SAAS,CAAC;AAAA,MAC7C;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO,eAAe;AAAA,QACtB,OAAO,CAAC,sBAAsB,MAAM,SAAS,CAAC;AAAA,MAChD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO,eAAe;AAAA,QACtB,OAAO,MAAM,UAAU,IAAI,CAAC,aAAa,GAAG,MAAM,SAAS,IAAI,QAAQ,EAAE;AAAA,MAC3E;AAAA,IACF,SAAS;AACP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,qBAAqB;AAAA,EACzB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AACjB;AASA,SAAS,aAAa,SAAwD;AAC5E,QAAM,YAAY,QAAQ,IAAI,aAAa;AAE3C,SAAO,mBAAmB;AAAA,IAAQ,CAAC,UACjC,iBAAiB,OAAO,WAAW,WAAW,KAAK,CAAC;AAAA,EACtD;AACF;AAGA,SAAS,WAAW,WAA0C,OAAyB;AACrF,SAAO,UAAU,OAAO,CAAC,YAAY,QAAQ,UAAU,KAAK,EAAE,QAAQ,CAAC,YAAY,QAAQ,KAAK;AAClG;AAEA,SAAS,iBAAiB,OAAe,OAAuC;AAC9E,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,SAAO,CAAC,EAAE,OAAO,MAAM,CAAC;AAC1B;AAEA,IAAM,4BAA4B;AAElC,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYzC,eAAuB,iBAAiB;AAAA,EAEhD,OAAO,UAAU,UAAU,iBAAiB,IAAI;AAAA,EAEhD,OAAO,cAAc,aAAa,iBAAiB,IAAI;AAAA,EAEvD,OAAO,WAAW;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ;AAAA,IACb,KAAK,MAAM,QAAQ;AAAA,MACjB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,IACD,UAAU,aAAa;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAgB,qBAAoC;AAAA,EAEpD;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,UAAS;AAC5C,UAAM,aAAa,QAAQ,IAAI;AAK/B,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,gBAAgB,MAAM,QAAQ;AAAA,MAC9B,CAAC,YAAY,KAAK,MAAM,SAAS,EAAE,MAAM,WAAW,aAAa,CAAC;AAAA,IACpE;AAEA,SAAK,eAAe,MAAM,KAAK,oBAAoB,UAAU;AAE7D,SAAK,YAAY;AAEjB,UAAM,SAAS,MAAM;AAAA,MAAsB;AAAA,MAAY,CAAC,YACtD,KAAK,KAAK,0BAA0B,OAAO,CAAC;AAAA,IAC9C;AACA,UAAM,OAAO,iBAAiB,MAAM;AACpC,QAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,WAAK,yBAAyB;AAC9B;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,MAAM,KAAK,iBAAiB,IAAI,IAAI,MAAM,KAAK,eAAe,IAAI;AAC1F,QAAI,CAAC,WAAW;AACd,WAAK,IAAI,EAAE;AACX,WAAK,IAAI,qBAAqB;AAC9B,WAAK,KAAK,WAAW,SAAS;AAAA,IAChC;AAEA,UAAM,KAAK,iBAAiB,MAAM,QAAQ,UAAU;AACpD,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,cAAoB;AAC1B,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,GAAG,KAAK,YAAY,YAAY;AACzC,SAAK,IAAI,EAAE;AAAA,EACb;AAAA,EAEQ,2BAAiC;AACvC,SAAK,KAAK,uBAAuB;AACjC,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,iBAAiB,KAAK,YAAY,CAAC;AAC5C,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,cAAc,eAAe;AAAA,EACxC;AAAA,EAEQ,iBAAiB,MAAyB;AAChD,SAAK,IAAI,eAAe,eAAe;AACvC,SAAK,IAAI,EAAE;AAEX,eAAW,WAAW,aAAa,KAAK,OAAO,GAAG;AAChD,WAAK,IAAI,KAAK,QAAQ,KAAK,EAAE;AAC7B,iBAAW,QAAQ,QAAQ,OAAO;AAChC,aAAK,IAAI,OAAO,IAAI,EAAE;AAAA,MACxB;AAAA,IACF;AAEA,eAAW,aAAa,KAAK,MAAM;AACjC,WAAK,IAAI,EAAE;AACX,WAAK,IAAI,SAAS;AAAA,IACpB;AAEA,SAAK,IAAI,EAAE;AACX,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAe,MAAqC;AAGhE,QAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,WAAK,MAAM,uBAAuB,GAAG,EAAE,MAAM,WAAW,MAAM,CAAC;AAAA,IACjE;AAEA,UAAM,UAAU,MAAM,cAAc,CAAC,EAAE,WAAW,SAAS,MACzD;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,eAAe;AAAA,QACxB,UAAU,aAAa,KAAK,OAAO;AAAA,QACnC,YAAY,KAAK;AAAA,QACjB,SAAS;AAAA,QACT;AAAA,QACA;AAAA;AAAA,IACF,CACD;AACD,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAc,iBACZ,MACA,QACA,YACe;AACf,UAAM,oBAAoB,gBAAgB,UAAU;AAIpD,UAAM,cAAc,oBAChB,MAAM,KAAK,yBAAyB,QAAQ,UAAU,IACtD;AAEJ,UAAM,KAAK,qBAAqB,UAAU,MAAM,SAAS,GAAG,OAAO,QAAQ,UAAU;AAErF,QAAI;AACF,YAAM,KAAK,iBAAiB,MAAM,MAAM;AAAA,IAC1C,SAAS,OAAO;AACd,WAAK,IAAI,8BAA8B;AACvC,WAAK,MAAM,gBAAgB,KAAK,GAAG;AAAA,QACjC,MAAM,WAAW;AAAA,MACnB,CAAC;AAAA,IACH;AAEA,QAAI,mBAAmB;AAGrB,UAAI,aAAa;AACf,cAAM,KAAK,yBAAyB,WAAW;AAAA,MACjD;AACA;AAAA,IACF;AAMA,QAAI;AAIF,YAAM,aAAa,EAAE,MAAM,sBAAsB,WAAW,GAAG,aAAa,UAAU,CAAC;AAAA,IACzF,SAAS,OAAO;AACd,WAAK,KAAK,iDAAiD,gBAAgB,KAAK,CAAC,EAAE;AAAA,IACrF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAc,yBACZ,QACA,YACuC;AACvC,QAAI,OAAO,WAAW,QAAQ,OAAO,mBAAmB,WAAW,EAAG,QAAO;AAC7E,UAAM,eAAe,OAAO;AAE5B,QAAI;AACF,YAAM,CAAC,cAAc,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,QAClD,2BAA2B;AAAA,UACzB;AAAA,UACA,SAAS;AAAA,UACT,kBAAkB;AAAA,UAClB,YAAY;AAAA,QACd,CAAC;AAAA,QACD,cAAc;AAAA,MAChB,CAAC;AACD,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,aAAa;AAAA,QACrB,QAAQ,UAAU;AAAA,QAClB,UAAU,cAAc,UAAU;AAAA,MACpC;AAAA,IACF,SAAS,OAAO;AACd,WAAK,KAAK,+BAA+B,gBAAgB,KAAK,CAAC,CAAC;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,yBAAyB,aAAmD;AACxF,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB,YAAY;AAAA,QACZ,EAAE,QAAQ,YAAY,QAAQ,QAAQ,YAAY,OAAO;AAAA,QACzD,YAAY;AAAA,MACd;AACA,YAAM,EAAE,SAAS,YAAY,OAAO,IAAI,OAAO;AAC/C,WAAK,yBAAyB,OAAO,UAAU;AAE/C,iBAAW,eAAe,CAAC,GAAG,YAAY,GAAG,MAAM,GAAG;AACpD,aAAK,KAAK,+BAA+B,WAAW,CAAC;AAAA,MACvD;AACA,UAAI,QAAQ,WAAW,EAAG;AAE1B,WAAK,WAAW,0BAA0B,QAAQ,MAAM,CAAC;AAIzD,WAAK,0BAA0B,MAAM;AAAA,IACvC,SAAS,OAAO;AACd,WAAK,KAAK,+BAA+B,gBAAgB,KAAK,CAAC,CAAC;AAAA,IAClE;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAC5B,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,GAAG,KAAK,YAAY,wBAAwB;AACrD,SAAK,IAAI,EAAE;AACX,SAAK,WAAW,iBAAiB,kBAAkB;AACnD,SAAK,IAAI,EAAE;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,MAAmB,QAAwC;AACxF,UAAM,KAAK,oBAAoB,UAAU,MAAM,QAAQ,CAAC;AACxD,UAAM,KAAK,oBAAoB,UAAU,MAAM,QAAQ,CAAC;AAIxD,eAAW,aAAa,KAAK,MAAM;AACjC,WAAK,IAAI,SAAS;AAAA,IACpB;AAEA,UAAM,UAAU,MAAM,iBAAiB,QAAQ,YAAY,MAAM,QAAQ,CAAC;AAE1E,SAAK,0BAA0B,QAAQ,YAAY;AAEnD,SAAK,wBAAwB,OAAO,YAAY,OAAO;AAEvD,QAAI,QAAQ,kBAAkB;AAC5B,WAAK,WAAW,WAAW,UAAU,GAAG;AAAA,IAC1C,WAAW,QAAQ,eAAe;AAChC,WAAK,IAAI,QAAQ,UAAU,2BAA2B;AAAA,IACxD;AAAA,EACF;AAAA;AAAA,EAGQ,0BAA0B,QAAmC;AACnE,QAAI,OAAO,YAAY;AACrB,WAAK,WAAW,WAAW,OAAO,OAAO,GAAG;AAAA,IAC9C,WAAW,OAAO,iBAAiB;AACjC,WAAK,WAAW,2BAA2B,OAAO,OAAO,GAAG;AAAA,IAC9D,WAAW,OAAO,MAAM;AACtB,WAAK,IAAI,QAAQ,OAAO,OAAO,2BAA2B;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA,EAGQ,wBAAwB,YAAwB,SAA8B;AACpF,QAAI,QAAQ,mBAAmB;AAC7B,WAAK,WAAW,WAAW,WAAW,OAAO,GAAG;AAChD;AAAA,IACF;AACA,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,IAAI,QAAQ,WAAW,OAAO,2BAA2B;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,MAAc,qBACZ,OACA,QACA,YACe;AACf,QAAI,CAAC,MAAO;AAEZ,SAAK,IAAI,yBAAyB;AAElC,QAAI;AACF,YAAM,eAAe,MAAM;AAAA,QAAiB;AAAA,QAAO;AAAA,QAAQ;AAAA,QAAY,CAAC,SACtE,KAAK,IAAI,yBAAyB,IAAI,GAAG;AAAA,MAC3C;AAMA,UAAI,aAAa,mBAAmB,GAAG;AACrC,aAAK;AAAA,UACH,eAAe,aAAa,gBAAgB,IAAI,aAAa,qBAAqB,IAAI,WAAW,SAAS;AAAA,QAC5G;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,IAAI,yBAAyB;AAClC,WAAK,MAAM,gBAAgB,KAAK,GAAG;AAAA,QACjC,MAAM,WAAW;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,OAAiD;AACjF,QAAI,CAAC,MAAO;AAEZ,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,CAAC,YAAY,KAAK,IAAI,wBAAwB,OAAO,GAAG;AAAA,MACxD,CAAC,YAAY,KAAK,KAAK,aAAa,OAAO,qBAAqB,KAAK,YAAY,MAAM;AAAA,IACzF;AACA,QAAI,OAAO,iBAAiB,EAAG;AAE/B,SAAK;AAAA,MACH,WAAW,OAAO,YAAY,kBAAkB,OAAO,iBAAiB,IAAI,UAAU,QAAQ;AAAA,IAChG;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,OAAiD;AACjF,QAAI,CAAC,MAAO;AAEZ,UAAM,SAAS,MAAM;AAAA,MAAqB;AAAA,MAAO,CAAC,cAChD,KAAK,IAAI,wBAAwB,SAAS,GAAG;AAAA,IAC/C;AACA,QAAI,OAAO,iBAAiB,EAAG;AAE/B,SAAK;AAAA,MACH,WAAW,OAAO,YAAY,aAAa,OAAO,iBAAiB,IAAI,UAAU,QAAQ;AAAA,IAC3F;AAAA,EACF;AACF;AA6GA,SAAS,wBAAwB,QAAoD;AACnF,MAAI,CAAC,QAAQ,OAAQ,QAAO,CAAC;AAC7B,SAAO,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AACxC;AAGO,SAAS,0BAA0B,QAAoD;AAC5F,MAAI,CAAC,QAAQ,OAAQ,QAAO,oBAAI,IAAI;AACpC,QAAM,EAAE,gBAAgB,IAAI;AAC5B,SAAO,IAAI;AAAA,IACT,OAAO,OAAO,QAAQ,CAAC,UAAU;AAAA;AAAA,MAE/B,0BAA0B,MAAM,IAAI,MAAM,MAAM;AAAA;AAAA;AAAA,MAGhD,GAAI,mBAAmB,MAAM,WAAW,mBAAmB,MAAM,WAAW,eACxE,CAAC,0BAA0B,MAAM,IAAI,eAAe,CAAC,IACrD,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AACF;AAWA,eAAe,oBACb,YACA,YACA,UACA,cAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,MAAM,oBAAoB,YAAY,YAAY,QAAQ;AACzE,WAAO,QAAQ,UAAU;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,EAAE,iBAAiB,iBAAkB,OAAM;AAC/C,iBAAa,KAAK;AAClB,WAAO;AAAA,EACT;AACF;AAGA,eAAe,iBACb,QACA,YACA,UACA,oBAC4B;AAC5B,QAAM,CAAC,eAAe,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,IAChD,gBAAgB,OAAO,GAAG;AAAA,IAC1B;AAAA,MACE,KAAK,KAAK,OAAO,KAAK,eAAe,SAAS;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO,EAAE,GAAG,QAAQ,eAAe,OAAO;AAC5C;AAGA,eAAe,gBAAgB,KAAgC;AAC7D,QAAM,WAAW,CAAC,eAAe,WAAW,eAAe,eAAe;AAC1E,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,SAAS,IAAI,CAAC,aAAa,WAAW,KAAK,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,EACjE;AACA,SAAO,SAAS,OAAO,CAAC,GAAG,UAAU,QAAQ,KAAK,CAAC;AACrD;AAGA,SAAS,eAAe,QAAsC;AAC5D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AAAA,IAC/C,QAAQ,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AAAA,EACjD;AACF;AAQA,eAAe,sBACb,YACA,oBAC0B;AAC1B,QAAM,aAAa,qBAAqB,UAAU;AAClD,QAAM,EAAE,WAAW,UAAU,IAAI,oBAAoB,UAAU;AAC/D,QAAM,YAAY,KAAK,KAAK,YAAY,UAAU;AAClD,QAAM,WAAW,cAAc,UAAU;AAEzC,QAAM,CAAC,gBAAgB,gBAAgB,cAAc,cAAc,eAAe,IAChF,MAAM,QAAQ,IAAI;AAAA,IAChB,gBAAgB,SAAS;AAAA,IACzB,gBAAgB,SAAS;AAAA,IACzB,gBAAgB,SAAS;AAAA,IACzB;AAAA,MACE,kBAAkB,YAAY,QAAQ;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,wBAAwB,WAAW,WAAW,QAAQ,CAAC;AAAA,EACzD,CAAC;AAEH,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,cAAwB,CAAC;AAC7B,MAAI;AACF,kBAAc,MAAM,gBAAgB,UAAU;AAAA,EAChD,QAAQ;AAAA,EAER;AAEA,QAAM,eAAe,WAAW,OAAO,OAAO,eAAe,MAAM;AACnE,QAAM,mBAAmB,wBAAwB,YAAY;AAC7D,QAAM,mBAAmB,0BAA0B,YAAY;AAC/D,QAAM,iBAAiB,qBAAqB,UAAU,IAClD,YAAY,OAAO,CAAC,SAAS,iBAAiB,IAAI,IAAI,CAAC,IACvD,CAAC;AAEL,SAAO;AAAA,IACL,YAAY,eAAe,SAAS;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,aAAa,UAAU;AAAA,IACnC;AAAA,IACA,oBAAoB,QAAQ,YAAY,CAAC;AAAA,IACzC;AAAA,IACA,YAAY,WAAW,QAAQ;AAAA,IAC/B,cAAc,gBAAgB;AAAA,IAC9B,gBAAgB,gBAAgB;AAAA,EAClC;AACF;AAqBA,SAAS,qBAAqB,YAA6B;AACzD,MAAI,gBAAgB,UAAU,EAAG,QAAO;AACxC,SAAO,OAAO,UAAU,EAAE;AAC5B;AAEA,SAAS,kBAAkB,YAAiD;AAC1E,SAAO,eAAe;AACxB;AAEA,eAAe,qBACb,OACA,WACA,WAC6B;AAC7B,QAAM,aAAa,MAAM,kBAAkB,MAAM,SAAS;AAC1D,QAAM,eAAe,MAAM;AAAA,IACzB,WAAW;AAAA,IACX,MAAM;AAAA,IACN;AAAA,EACF;AACA,aAAW,OAAO,QAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AACrD,QAAM,aAAa,MAAM,iBAAiB,MAAM,WAAW,WAAW,OAAO,WAAW,CAAC;AAEzF,SAAO;AAAA,IACL,cAAc,aAAa;AAAA,IAC3B,cAAc,WAAW,OAAO;AAAA,IAChC;AAAA,IACA,cAAc,WAAW;AAAA,IACzB;AAAA,EACF;AACF;AAEA,eAAe,kBACb,WACmD;AACnD,QAAM,WAAW,MAAM,gBAAgB,SAAS;AAChD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,SAAS,IAAI,OAAO,UAAU;AAAA,MAC5B;AAAA,MACA,YAAY,MAAM,uBAAuB,KAAK,KAAK,WAAW,IAAI,CAAC;AAAA,IACrE,EAAE;AAAA,EACJ;AACA,QAAM,CAAC,WAAW,SAAS,IAAI,UAAU,SAAS,CAAC,UAAU,kBAAkB,MAAM,UAAU,CAAC;AAChG,SAAO,EAAE,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,QAAQ,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;AACxF;AAEA,eAAe,uBACb,OACA,WACA,WACmB;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,KAAK,KAAK,WAAW,IAAI,CAAC;AACvC,gBAAY,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAEA,eAAe,iBAAiB,KAAa,YAAuC;AAClF,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,iBAAiB,GAAG;AAC7B;AAaA,eAAe,qBACb,OACA,WAC6B;AAC7B,QAAM,aAAa,MAAM,eAAe,MAAM,WAAW,MAAM,KAAK;AACpE,QAAM,eAAe,WAClB,IAAI,CAAC,cAAc,KAAK,SAAS,WAAW,MAAM,MAAM,SAAS,CAAC,EAClE,OAAO,CAAC,cAAc,MAAM,WAAW,SAAS,SAAS,CAAC;AAE7D,aAAW,aAAa,cAAc;AACpC,UAAM,OAAO,KAAK,KAAK,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,MAAM,SAAS,EAAE,CAAC;AAC/E,gBAAY,SAAS;AAAA,EACvB;AAEA,QAAM,aAAa,MAAM,iBAAiB,MAAM,SAAS;AAEzD,SAAO;AAAA,IACL,cAAc,aAAa;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACF;AAQA,IAAM,+BAA2C;AAYjD,SAAS,oBAAoB,MAAkB,SAAmC;AAChF,QAAM,UAAU,KAAK,kBAClB,OAAO,CAAC,cAAc,UAAU,SAAS,QAAQ,EACjD,IAAI,CAAC,cAAc,UAAU,KAAK;AAErC,MAAI,CAAC,QAAQ,SAAS,OAAO,EAAG,QAAO;AACvC,SAAO,CAAC,SAAS,GAAG,QAAQ,OAAO,CAAC,UAAU,UAAU,OAAO,CAAC;AAClE;AAeA,eAAe,8BACb,MACA,WACA,cACA,YAC+B;AAC/B,MAAI,UAAgC;AAEpC,aAAW,SAAS,oBAAoB,MAAM,YAAY,GAAG;AAC3D,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,gBAAgB,WAAW,OAAO,UAAU;AACrE,UAAI,UAAU,UAAW,WAAU;AAAA,IACrC,QAAQ;AAAA,IAIR;AAAA,EACF;AAEA,SAAO;AACT;AAWA,eAAsB,iBACpB,OACA,QACA,YACA,eACiC;AACjC,QAAM,OAAO,OAAO,UAAU;AAC9B,QAAM,gBAAgB,MAAM,KAAK,YAAY;AAC7C,QAAM,WAAqB,CAAC;AAE5B,aAAW,cAAc,MAAM,OAAO;AACpC,UAAM,UAAU,gBACZ,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,2BAA2B,QAAQ,UAAU;AAAA,MAC7C;AAAA,IACF,IACA,mBAAmB;AAEvB,UAAM,OAAO,KAAK,KAAK,MAAM,YAAY,UAAU,CAAC;AAEpD,QAAI,YAAY,WAAW;AACzB,eAAS,KAAK,UAAU;AACxB,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF;AAOA,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,kBAAkB,SAAS;AAAA,EAC7B;AACF;AASA,SAAS,2BAA2B,QAA8B,YAAgC;AAChG,QAAM,UAAU,0BAA0B,UAAU;AACpD,SACE,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO,GAAG,SAAS;AAEnE;AAUA,SAAS,qBAA2C;AAClD,SAAO;AACT;AAQA,eAAe,qBACb,OAC4D;AAC5D,MAAI,CAAC,MAAO,QAAO,EAAE,iBAAiB,OAAO,YAAY,MAAM;AAE/D,QAAM,QAAQ;AAAA,IACZ,MAAM,UAAU,IAAI,CAAC,aAAa,OAAO,KAAK,KAAK,MAAM,WAAW,QAAQ,CAAC,CAAC;AAAA,EAChF;AAEA,SAAO,EAAE,iBAAiB,MAAM,YAAY,MAAM,iBAAiB,MAAM,SAAS,EAAE;AACtF;AASA,eAAe,iBACb,QACA,iBACwB;AACxB,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO;AAAA,IACP,gBAAgB,KAAK,CAAC,UAAU,MAAM,cAAc,OAAO,aAAa,GAAG;AAAA,EAC7E;AACA,QAAM,aAAa,MAAM,kBAAkB,OAAO,YAAY,aAAa,UAAU;AAErF,QAAM,mBAAmB,OAAO,gBAAiB,MAAM,iBAAiB,OAAO,SAAS;AAExF,QAAM,gBACJ,CAAC,oBAAoB,OAAO,gBAAiB,MAAM,gBAAgB,OAAO,SAAS;AAErF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB,WAAW;AAAA,IAC9B,gBAAgB,WAAW;AAAA,IAC3B;AAAA,EACF;AACF;AAOA,eAAe,oBACb,QACA,OAC8B;AAC9B,QAAM,WAAW,MAAM,qBAAqB,KAAK;AACjD,QAAM,aAAa,SAAS,cAAe,MAAM,iBAAiB,OAAO,GAAG;AAC5E,QAAM,OAAO,CAAC,cAAe,MAAM,gBAAgB,OAAO,GAAG;AAC7D,SAAO;AAAA,IACL,SAAS,OAAO;AAAA,IAChB,iBAAiB,SAAS;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AACF;AAqBA,eAAe,kBACb,YACA,oBAC4B;AAC5B,MAAI,CAAC,mBAAoB,QAAO,EAAE,SAAS,OAAO,MAAM,MAAM;AAE9D,QAAM,UAAU,MAAM,iBAAiB,WAAW,GAAG;AACrD,SAAO,EAAE,SAAS,MAAM,CAAC,WAAY,MAAM,gBAAgB,WAAW,GAAG,EAAG;AAC9E;AAEA,eAAe,eAAe,WAAmB,OAAsC;AACrF,MAAI;AACF,WAAO,MAAM,iBAAiB,WAAW,KAAK;AAAA,EAChD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;","names":[]}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
unofferablePlacementsFound
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-PXWG7KFO.js";
|
|
5
5
|
import {
|
|
6
6
|
providerFlag,
|
|
7
7
|
providerNamedBy,
|
|
8
8
|
refuseAnAmbiguousInstallation
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-NBXXFIIW.js";
|
|
10
10
|
import {
|
|
11
11
|
BaseCommand,
|
|
12
12
|
EXIT_CODES
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
13
|
+
} from "../chunk-Y3VIRVVE.js";
|
|
14
|
+
import "../chunk-VAPTVR3J.js";
|
|
15
|
+
import "../chunk-RZDXDMT6.js";
|
|
16
16
|
import {
|
|
17
17
|
ERROR_MESSAGES,
|
|
18
18
|
INFO_MESSAGES,
|
|
@@ -23,11 +23,11 @@ import {
|
|
|
23
23
|
marketplaceRefreshFailed,
|
|
24
24
|
marketplacesRefreshFailed,
|
|
25
25
|
marketplacesRefreshed
|
|
26
|
-
} from "../chunk-
|
|
27
|
-
import "../chunk-
|
|
26
|
+
} from "../chunk-A4KDXNOT.js";
|
|
27
|
+
import "../chunk-V5WXIM2F.js";
|
|
28
28
|
import {
|
|
29
29
|
EJECT_SOURCE
|
|
30
|
-
} from "../chunk-
|
|
30
|
+
} from "../chunk-ENGYJCTF.js";
|
|
31
31
|
import "../chunk-OI2H6M5I.js";
|
|
32
32
|
|
|
33
33
|
// src/cli/commands/update.ts
|
|
@@ -55,7 +55,6 @@ var Update = class _Update extends BaseCommand {
|
|
|
55
55
|
(message) => this.error(message, { exit: EXIT_CODES.INVALID_ARGS })
|
|
56
56
|
);
|
|
57
57
|
await this.ensureConfigReadable(process.cwd());
|
|
58
|
-
await this.settleSourceLayoutBeforeWriting(process.cwd());
|
|
59
58
|
await this.refuseUnofferablePlacements(process.cwd());
|
|
60
59
|
const config = await this.loadInstalledConfig(process.cwd());
|
|
61
60
|
if (!config) {
|