skillmux 0.1.4 → 0.1.6

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.
@@ -59,8 +59,8 @@ var defaultAgentRuleMap = Object.fromEntries(
59
59
  );
60
60
 
61
61
  // src/commands/adopt.ts
62
- import { homedir } from "os";
63
- import { join as join7, resolve as resolve8 } from "path";
62
+ import { homedir as homedir2 } from "os";
63
+ import { join as join8, resolve as resolve8 } from "path";
64
64
 
65
65
  // src/core/errors.ts
66
66
  var SkillMuxError = class extends Error {
@@ -119,8 +119,13 @@ function resolveSkillmuxHome(homeDir) {
119
119
  }
120
120
 
121
121
  // src/discovery/discover-agents.ts
122
- import * as fs2 from "fs/promises";
123
- import { join as join2, resolve as resolve2 } from "path";
122
+ import * as fs3 from "fs/promises";
123
+ import { join as join3, resolve as resolve2 } from "path";
124
+
125
+ // src/config/auto-register-agents.ts
126
+ import { readdirSync, statSync } from "fs";
127
+ import { homedir } from "os";
128
+ import { join as join2 } from "path";
124
129
 
125
130
  // src/config/load-user-config.ts
126
131
  import * as fs from "fs/promises";
@@ -131,16 +136,27 @@ var agentOverrideSchema = z.object({
131
136
  supportedPlatforms: z.array(supportedPlatformSchema).min(1).optional(),
132
137
  homeRelativeRootPath: z.string().min(1).optional(),
133
138
  skillsDirectoryPath: z.string().min(1).optional(),
134
- enabledByDefault: z.boolean().optional()
139
+ enabledByDefault: z.boolean().optional(),
140
+ autoDiscovered: z.literal(true).optional()
135
141
  }).strict();
136
142
  var userConfigSchema = z.object({
137
143
  version: z.literal(1),
138
- agents: z.record(z.string().min(1), agentOverrideSchema)
144
+ agents: z.record(z.string().min(1), agentOverrideSchema),
145
+ autoDiscover: z.object({
146
+ lastRunAt: z.string().nullable(),
147
+ intervalMs: z.number().int().nonnegative()
148
+ }).optional(),
149
+ removedAutoAgentIds: z.array(z.string().min(1)).optional()
139
150
  }).strict();
140
151
  function createEmptyUserConfig() {
141
152
  return {
142
153
  version: 1,
143
- agents: {}
154
+ agents: {},
155
+ autoDiscover: {
156
+ lastRunAt: null,
157
+ intervalMs: 36e5
158
+ },
159
+ removedAutoAgentIds: []
144
160
  };
145
161
  }
146
162
  function stripUtf8Bom(contents) {
@@ -177,6 +193,101 @@ async function loadUserConfig(skillmuxHome) {
177
193
  }
178
194
  }
179
195
 
196
+ // src/config/write-user-config.ts
197
+ import * as fs2 from "fs/promises";
198
+ async function writeUserConfig(skillmuxHome, config) {
199
+ const configPath = buildConfigPath(skillmuxHome);
200
+ await fs2.mkdir(skillmuxHome, { recursive: true });
201
+ await fs2.writeFile(configPath, `${JSON.stringify(config, null, 2)}
202
+ `, "utf8");
203
+ return {
204
+ skillmuxHome,
205
+ configPath
206
+ };
207
+ }
208
+
209
+ // src/config/auto-register-agents.ts
210
+ function isDotDir(name) {
211
+ return name.startsWith(".");
212
+ }
213
+ function isValidAgentId(name) {
214
+ if (name.length < 2) return false;
215
+ const stem = name.slice(1);
216
+ return /^[a-z][a-z0-9-]*$/u.test(stem);
217
+ }
218
+ function dirExists(dirPath) {
219
+ try {
220
+ return statSync(dirPath).isDirectory();
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
225
+ function isBuiltInAgentId(id) {
226
+ return builtInAgentIds.includes(id);
227
+ }
228
+ function makeAutoDiscoveredOverride(dirName) {
229
+ const stem = dirName.slice(1);
230
+ return {
231
+ stableName: stem.charAt(0).toUpperCase() + stem.slice(1),
232
+ supportedPlatforms: ["win32", "linux", "darwin"],
233
+ homeRelativeRootPath: dirName,
234
+ skillsDirectoryPath: "skills",
235
+ enabledByDefault: true,
236
+ autoDiscovered: true
237
+ };
238
+ }
239
+ async function autoRegisterNewAgents(homeDir) {
240
+ const resolvedHomeDir = homeDir ?? homedir();
241
+ const { skillmuxHome } = resolveSkillmuxHome(resolvedHomeDir);
242
+ const config = await loadUserConfig(skillmuxHome);
243
+ const autoDiscover = config.autoDiscover ?? {
244
+ lastRunAt: null,
245
+ intervalMs: 36e5
246
+ };
247
+ if (autoDiscover.lastRunAt !== null && autoDiscover.intervalMs > 0) {
248
+ const elapsed = Date.now() - new Date(autoDiscover.lastRunAt).getTime();
249
+ if (elapsed < autoDiscover.intervalMs) {
250
+ return;
251
+ }
252
+ }
253
+ const wellKnownNonAgentDirs = /* @__PURE__ */ new Set([".skillmux"]);
254
+ const removedIds = new Set(config.removedAutoAgentIds ?? []);
255
+ let changed = false;
256
+ let entries;
257
+ try {
258
+ entries = readdirSync(resolvedHomeDir);
259
+ } catch {
260
+ return;
261
+ }
262
+ const nextAgents = { ...config.agents };
263
+ for (const name of entries) {
264
+ if (!isDotDir(name)) continue;
265
+ if (wellKnownNonAgentDirs.has(name)) continue;
266
+ if (!isValidAgentId(name)) continue;
267
+ const agentId = name.slice(1).toLowerCase().replace(/[^a-z0-9]+/g, "-");
268
+ if (agentId in nextAgents) continue;
269
+ if (isBuiltInAgentId(agentId)) continue;
270
+ if (removedIds.has(agentId)) continue;
271
+ const skillsDir = join2(resolvedHomeDir, name, "skills");
272
+ if (!dirExists(skillsDir)) continue;
273
+ nextAgents[agentId] = makeAutoDiscoveredOverride(name);
274
+ changed = true;
275
+ }
276
+ if (!changed && autoDiscover.lastRunAt !== null) {
277
+ return;
278
+ }
279
+ const nextConfig = {
280
+ ...config,
281
+ agents: nextAgents,
282
+ autoDiscover: {
283
+ ...autoDiscover,
284
+ lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
285
+ },
286
+ removedAutoAgentIds: [...removedIds]
287
+ };
288
+ await writeUserConfig(skillmuxHome, nextConfig);
289
+ }
290
+
180
291
  // src/discovery/discover-agents.ts
181
292
  function mergeRule(rule, override) {
182
293
  if (override === void 0) {
@@ -205,7 +316,7 @@ function buildCustomRule(id, override) {
205
316
  }
206
317
  async function pathExists(path) {
207
318
  try {
208
- await fs2.access(path);
319
+ await fs3.access(path);
209
320
  return true;
210
321
  } catch {
211
322
  return false;
@@ -215,7 +326,7 @@ function resolveAgentRulePaths(homeDir, rule) {
215
326
  const absoluteRootPath = resolve2(homeDir, rule.homeRelativeRootPath);
216
327
  return {
217
328
  absoluteRootPath,
218
- absoluteSkillsDirectoryPath: join2(
329
+ absoluteSkillsDirectoryPath: join3(
219
330
  absoluteRootPath,
220
331
  rule.skillsDirectoryPath
221
332
  )
@@ -225,6 +336,7 @@ async function discoverAgents(options) {
225
336
  const platform = options.platform ?? process.platform;
226
337
  const homeDir = resolve2(options.homeDir);
227
338
  const skillmuxHome = options.skillmuxHome ?? resolveSkillmuxHome(homeDir).skillmuxHome;
339
+ await autoRegisterNewAgents(homeDir);
228
340
  const userConfig = await loadUserConfig(skillmuxHome);
229
341
  const discoveredAgents = [];
230
342
  for (const agentId of builtInAgentIds) {
@@ -254,22 +366,23 @@ async function discoverAgents(options) {
254
366
  exists: await pathExists(resolvedPaths.absoluteSkillsDirectoryPath),
255
367
  supportedOnPlatform: customRule.supportedPlatforms.some(
256
368
  (supportedPlatform) => supportedPlatform === platform
257
- )
369
+ ),
370
+ autoDiscovered: override.autoDiscovered === true || void 0
258
371
  });
259
372
  }
260
373
  return discoveredAgents;
261
374
  }
262
375
 
263
376
  // src/discovery/scan-agent-skills.ts
264
- import * as fs5 from "fs/promises";
265
- import { join as join3 } from "path";
377
+ import * as fs6 from "fs/promises";
378
+ import { join as join4 } from "path";
266
379
 
267
380
  // src/discovery/infer-skill-entry.ts
268
- import * as fs4 from "fs/promises";
381
+ import * as fs5 from "fs/promises";
269
382
  import { basename, resolve as resolve4 } from "path";
270
383
 
271
384
  // src/fs/path-utils.ts
272
- import * as fs3 from "fs/promises";
385
+ import * as fs4 from "fs/promises";
273
386
  import { dirname, parse, relative, resolve as resolve3, sep } from "path";
274
387
  function normalizeAbsolutePath(path) {
275
388
  const normalized = resolve3(path);
@@ -300,7 +413,7 @@ async function assertNoSymlinkAncestors(path, options) {
300
413
  let current = options?.includeLeaf === true ? resolve3(path) : dirname(resolve3(path));
301
414
  while (true) {
302
415
  try {
303
- const entry = await fs3.lstat(current);
416
+ const entry = await fs4.lstat(current);
304
417
  if (entry.isSymbolicLink()) {
305
418
  throw new Error(`Refusing to use path with symlink ancestor at ${current}`);
306
419
  }
@@ -324,10 +437,10 @@ function buildIssue(code, severity, message, path) {
324
437
  async function inferSkillEntry(options) {
325
438
  const absolutePath = resolve4(options.path);
326
439
  const skillName = basename(absolutePath);
327
- const stats = await fs4.lstat(absolutePath);
440
+ const stats = await fs5.lstat(absolutePath);
328
441
  if (stats.isSymbolicLink()) {
329
442
  try {
330
- const targetPath = await fs4.realpath(absolutePath);
443
+ const targetPath = await fs5.realpath(absolutePath);
331
444
  if (isPathInside(options.skillmuxHome, targetPath)) {
332
445
  return {
333
446
  entry: {
@@ -407,7 +520,7 @@ async function scanAgentSkills(agent, skillmuxHome) {
407
520
  issues: []
408
521
  };
409
522
  }
410
- const directoryEntries = await fs5.readdir(agent.absoluteSkillsDirectoryPath, {
523
+ const directoryEntries = await fs6.readdir(agent.absoluteSkillsDirectoryPath, {
411
524
  withFileTypes: true
412
525
  });
413
526
  const sortedDirectoryEntries = [...directoryEntries].sort(
@@ -419,7 +532,7 @@ async function scanAgentSkills(agent, skillmuxHome) {
419
532
  const result = await inferSkillEntry({
420
533
  agentId: agent.id,
421
534
  agentName: agent.stableName,
422
- path: join3(agent.absoluteSkillsDirectoryPath, directoryEntry.name),
535
+ path: join4(agent.absoluteSkillsDirectoryPath, directoryEntry.name),
423
536
  skillmuxHome
424
537
  });
425
538
  entries.push(result.entry);
@@ -434,23 +547,23 @@ async function scanAgentSkills(agent, skillmuxHome) {
434
547
  }
435
548
 
436
549
  // src/fs/safe-copy.ts
437
- import * as fs6 from "fs/promises";
438
- import { dirname as dirname2, join as join4, resolve as resolve5 } from "path";
550
+ import * as fs7 from "fs/promises";
551
+ import { dirname as dirname2, join as join5, resolve as resolve5 } from "path";
439
552
  async function assertDirectory(path) {
440
- const entry = await fs6.lstat(path);
553
+ const entry = await fs7.lstat(path);
441
554
  if (!entry.isDirectory()) {
442
555
  throw new Error(`Expected a directory at ${path}`);
443
556
  }
444
557
  }
445
558
  async function assertRegularFile(path, label) {
446
- const entry = await fs6.lstat(path);
559
+ const entry = await fs7.lstat(path);
447
560
  if (!entry.isFile()) {
448
561
  throw new Error(`Expected ${label} to be a regular file at ${path}`);
449
562
  }
450
563
  }
451
564
  async function assertTargetDoesNotExist(path) {
452
565
  try {
453
- await fs6.lstat(path);
566
+ await fs7.lstat(path);
454
567
  throw new Error(`Refusing to overwrite existing path at ${path}`);
455
568
  } catch (error) {
456
569
  if (error.code !== "ENOENT") {
@@ -459,12 +572,12 @@ async function assertTargetDoesNotExist(path) {
459
572
  }
460
573
  }
461
574
  async function copyDirectoryContents(sourcePath, targetPath) {
462
- await fs6.mkdir(targetPath, { recursive: true });
463
- const entries = await fs6.readdir(sourcePath, { withFileTypes: true });
575
+ await fs7.mkdir(targetPath, { recursive: true });
576
+ const entries = await fs7.readdir(sourcePath, { withFileTypes: true });
464
577
  for (const entry of entries) {
465
- const sourceEntryPath = join4(sourcePath, entry.name);
466
- const targetEntryPath = join4(targetPath, entry.name);
467
- const entryStats = await fs6.lstat(sourceEntryPath);
578
+ const sourceEntryPath = join5(sourcePath, entry.name);
579
+ const targetEntryPath = join5(targetPath, entry.name);
580
+ const entryStats = await fs7.lstat(sourceEntryPath);
468
581
  if (entryStats.isSymbolicLink()) {
469
582
  throw new Error(`Refusing to copy source symlink at ${sourceEntryPath}`);
470
583
  }
@@ -473,8 +586,8 @@ async function copyDirectoryContents(sourcePath, targetPath) {
473
586
  continue;
474
587
  }
475
588
  if (entryStats.isFile()) {
476
- await fs6.mkdir(dirname2(targetEntryPath), { recursive: true });
477
- await fs6.copyFile(sourceEntryPath, targetEntryPath);
589
+ await fs7.mkdir(dirname2(targetEntryPath), { recursive: true });
590
+ await fs7.copyFile(sourceEntryPath, targetEntryPath);
478
591
  continue;
479
592
  }
480
593
  throw new Error(`Unsupported filesystem entry at ${sourceEntryPath}`);
@@ -482,7 +595,7 @@ async function copyDirectoryContents(sourcePath, targetPath) {
482
595
  }
483
596
  async function assertSkillSourceLayout(sourcePath) {
484
597
  const resolvedSourcePath = resolve5(sourcePath);
485
- const skillFilePath = join4(resolvedSourcePath, "SKILL.md");
598
+ const skillFilePath = join5(resolvedSourcePath, "SKILL.md");
486
599
  await assertNoSymlinkAncestors(resolvedSourcePath, { includeLeaf: true });
487
600
  await assertDirectory(resolvedSourcePath);
488
601
  try {
@@ -496,7 +609,7 @@ async function assertSkillSourceLayout(sourcePath) {
496
609
  }
497
610
  async function hasRootSkillFile(sourcePath) {
498
611
  const resolvedSourcePath = resolve5(sourcePath);
499
- const skillFilePath = join4(resolvedSourcePath, "SKILL.md");
612
+ const skillFilePath = join5(resolvedSourcePath, "SKILL.md");
500
613
  try {
501
614
  await assertDirectory(resolvedSourcePath);
502
615
  await assertRegularFile(skillFilePath, "SKILL.md");
@@ -550,7 +663,7 @@ var BatchOperationError = class extends Error {
550
663
  };
551
664
 
552
665
  // src/fs/link-ops.ts
553
- import * as fs7 from "fs/promises";
666
+ import * as fs8 from "fs/promises";
554
667
  import { dirname as dirname3, resolve as resolve6 } from "path";
555
668
  var directoryLinkType = process.platform === "win32" ? "junction" : "dir";
556
669
  async function createManagedLink(linkPath, targetPath) {
@@ -558,25 +671,25 @@ async function createManagedLink(linkPath, targetPath) {
558
671
  const resolvedTargetPath = resolve6(targetPath);
559
672
  await assertNoSymlinkAncestors(resolvedLinkPath);
560
673
  await assertNoSymlinkAncestors(resolvedTargetPath, { includeLeaf: true });
561
- await fs7.mkdir(dirname3(resolvedLinkPath), { recursive: true });
674
+ await fs8.mkdir(dirname3(resolvedLinkPath), { recursive: true });
562
675
  try {
563
- const existingEntry = await fs7.lstat(resolvedLinkPath);
676
+ const existingEntry = await fs8.lstat(resolvedLinkPath);
564
677
  if (!existingEntry.isSymbolicLink()) {
565
678
  throw new Error(`Refusing to replace non-link entry at ${resolvedLinkPath}`);
566
679
  }
567
- const currentTargetPath = await fs7.realpath(resolvedLinkPath);
680
+ const currentTargetPath = await fs8.realpath(resolvedLinkPath);
568
681
  if (pathsAreEqual(currentTargetPath, resolvedTargetPath)) {
569
682
  return;
570
683
  }
571
684
  throw new Error(`Refusing to replace link at ${resolvedLinkPath}`);
572
685
  } catch (error) {
573
- if (error.code === "ENOENT" && await fs7.lstat(resolvedLinkPath).then((entry) => entry.isSymbolicLink()).catch(() => false)) {
574
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
686
+ if (error.code === "ENOENT" && await fs8.lstat(resolvedLinkPath).then((entry) => entry.isSymbolicLink()).catch(() => false)) {
687
+ await fs8.rm(resolvedLinkPath, { recursive: true, force: false });
575
688
  } else if (error.code !== "ENOENT") {
576
689
  throw error;
577
690
  }
578
691
  }
579
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
692
+ await fs8.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
580
693
  }
581
694
  async function replaceEntryWithManagedLink(linkPath, targetPath, expectedCurrentPath) {
582
695
  const resolvedLinkPath = resolve6(linkPath);
@@ -584,38 +697,38 @@ async function replaceEntryWithManagedLink(linkPath, targetPath, expectedCurrent
584
697
  const resolvedExpectedCurrentPath = resolve6(expectedCurrentPath);
585
698
  await assertNoSymlinkAncestors(resolvedLinkPath);
586
699
  await assertNoSymlinkAncestors(resolvedTargetPath, { includeLeaf: true });
587
- await fs7.mkdir(dirname3(resolvedLinkPath), { recursive: true });
588
- const existingEntry = await fs7.lstat(resolvedLinkPath);
700
+ await fs8.mkdir(dirname3(resolvedLinkPath), { recursive: true });
701
+ const existingEntry = await fs8.lstat(resolvedLinkPath);
589
702
  if (existingEntry.isSymbolicLink()) {
590
- const currentTargetPath = await fs7.realpath(resolvedLinkPath);
703
+ const currentTargetPath = await fs8.realpath(resolvedLinkPath);
591
704
  if (pathsAreEqual(currentTargetPath, resolvedTargetPath)) {
592
705
  return false;
593
706
  }
594
707
  if (!pathsAreEqual(currentTargetPath, resolvedExpectedCurrentPath)) {
595
708
  throw new Error(`Refusing to replace unexpected link at ${resolvedLinkPath}`);
596
709
  }
597
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
598
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
710
+ await fs8.rm(resolvedLinkPath, { recursive: true, force: false });
711
+ await fs8.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
599
712
  return true;
600
713
  }
601
714
  if (!existingEntry.isDirectory()) {
602
715
  throw new Error(`Refusing to replace non-directory entry at ${resolvedLinkPath}`);
603
716
  }
604
- const currentPath = await fs7.realpath(resolvedLinkPath);
717
+ const currentPath = await fs8.realpath(resolvedLinkPath);
605
718
  if (!pathsAreEqual(currentPath, resolvedExpectedCurrentPath)) {
606
719
  throw new Error(`Refusing to replace unexpected directory at ${resolvedLinkPath}`);
607
720
  }
608
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
609
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
721
+ await fs8.rm(resolvedLinkPath, { recursive: true, force: false });
722
+ await fs8.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
610
723
  return true;
611
724
  }
612
725
  async function isLinkPointingToTarget(linkPath, targetPath) {
613
726
  try {
614
- const entry = await fs7.lstat(linkPath);
727
+ const entry = await fs8.lstat(linkPath);
615
728
  if (!entry.isSymbolicLink()) {
616
729
  return false;
617
730
  }
618
- const resolvedTargetPath = await fs7.realpath(linkPath);
731
+ const resolvedTargetPath = await fs8.realpath(linkPath);
619
732
  return pathsAreEqual(resolvedTargetPath, targetPath);
620
733
  } catch (error) {
621
734
  if (error.code === "ENOENT") {
@@ -626,8 +739,8 @@ async function isLinkPointingToTarget(linkPath, targetPath) {
626
739
  }
627
740
 
628
741
  // src/manifest/read-manifest.ts
629
- import * as fs9 from "fs/promises";
630
- import { join as join6, resolve as resolve7 } from "path";
742
+ import * as fs10 from "fs/promises";
743
+ import { join as join7, resolve as resolve7 } from "path";
631
744
 
632
745
  // src/manifest/build-empty-manifest.ts
633
746
  function buildEmptyManifest(skillmuxHome) {
@@ -752,32 +865,32 @@ var manifestSchema = z2.object({
752
865
 
753
866
  // src/manifest/write-manifest.ts
754
867
  import { randomUUID } from "crypto";
755
- import * as fs8 from "fs/promises";
756
- import { join as join5 } from "path";
868
+ import * as fs9 from "fs/promises";
869
+ import { join as join6 } from "path";
757
870
  function getManifestPath(home) {
758
- return join5(home, "manifest.json");
871
+ return join6(home, "manifest.json");
759
872
  }
760
873
  function createManifestTempPath(manifestPath) {
761
874
  return `${manifestPath}.${process.pid}.${randomUUID()}.tmp`;
762
875
  }
763
876
  async function writeManifest(home, manifest) {
764
- await fs8.mkdir(home, { recursive: true });
877
+ await fs9.mkdir(home, { recursive: true });
765
878
  const manifestPath = getManifestPath(home);
766
879
  const tempPath = createManifestTempPath(manifestPath);
767
880
  const contents = `${JSON.stringify(manifest, null, 2)}
768
881
  `;
769
- await fs8.writeFile(tempPath, contents, "utf8");
882
+ await fs9.writeFile(tempPath, contents, "utf8");
770
883
  try {
771
- await fs8.rename(tempPath, manifestPath);
884
+ await fs9.rename(tempPath, manifestPath);
772
885
  } catch (error) {
773
- await fs8.unlink(tempPath).catch(() => void 0);
886
+ await fs9.unlink(tempPath).catch(() => void 0);
774
887
  throw error;
775
888
  }
776
889
  }
777
890
 
778
891
  // src/manifest/read-manifest.ts
779
892
  function getManifestPath2(home) {
780
- return join6(home, "manifest.json");
893
+ return join7(home, "manifest.json");
781
894
  }
782
895
  function normalizeHomePath(home) {
783
896
  const resolvedHome = resolve7(home);
@@ -792,7 +905,7 @@ function formatValidationIssues2(error) {
792
905
  async function readManifest(home) {
793
906
  const manifestPath = getManifestPath2(home);
794
907
  try {
795
- const contents = await fs9.readFile(manifestPath, "utf8");
908
+ const contents = await fs10.readFile(manifestPath, "utf8");
796
909
  const parsedJson = JSON.parse(contents);
797
910
  const parsedManifest = manifestSchema.safeParse(parsedJson);
798
911
  if (!parsedManifest.success) {
@@ -937,7 +1050,7 @@ function buildOutput(result, json) {
937
1050
  `;
938
1051
  }
939
1052
  async function runAdoptSingle(options) {
940
- const homeDir = options.homeDir ?? homedir();
1053
+ const homeDir = options.homeDir ?? homedir2();
941
1054
  const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
942
1055
  const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
943
1056
  const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
@@ -1016,7 +1129,7 @@ async function runAdoptSingle(options) {
1016
1129
  const activation = buildActivationRecord(
1017
1130
  skillId,
1018
1131
  agent.id,
1019
- join7(agent.absoluteSkillsDirectoryPath, entry.skillName),
1132
+ join8(agent.absoluteSkillsDirectoryPath, entry.skillName),
1020
1133
  timestamp
1021
1134
  );
1022
1135
  upsertActivation(manifest, activation);
@@ -1085,7 +1198,7 @@ async function runAdopt(options) {
1085
1198
  }
1086
1199
 
1087
1200
  // src/commands/config-add-agent.ts
1088
- import { homedir as homedir2 } from "os";
1201
+ import { homedir as homedir3 } from "os";
1089
1202
 
1090
1203
  // src/config/agent-override-validation.ts
1091
1204
  import { isAbsolute } from "path";
@@ -1126,19 +1239,6 @@ function normalizePlatforms(value) {
1126
1239
  return normalized;
1127
1240
  }
1128
1241
 
1129
- // src/config/write-user-config.ts
1130
- import * as fs10 from "fs/promises";
1131
- async function writeUserConfig(skillmuxHome, config) {
1132
- const configPath = buildConfigPath(skillmuxHome);
1133
- await fs10.mkdir(skillmuxHome, { recursive: true });
1134
- await fs10.writeFile(configPath, `${JSON.stringify(config, null, 2)}
1135
- `, "utf8");
1136
- return {
1137
- skillmuxHome,
1138
- configPath
1139
- };
1140
- }
1141
-
1142
1242
  // src/output/print-table.ts
1143
1243
  function printTable(rows, columns) {
1144
1244
  const renderedRows = rows.map(
@@ -1211,7 +1311,7 @@ function buildTableOutput(result) {
1211
1311
  return `${summary}${detail}`;
1212
1312
  }
1213
1313
  async function runConfigAddAgent(options) {
1214
- const homeDir = options.homeDir ?? homedir2();
1314
+ const homeDir = options.homeDir ?? homedir3();
1215
1315
  const resolvedPaths = resolveSkillmuxHome(homeDir);
1216
1316
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1217
1317
  const configPath = buildConfigPath(skillmuxHome);
@@ -1242,7 +1342,7 @@ async function runConfigAddAgent(options) {
1242
1342
  }
1243
1343
 
1244
1344
  // src/commands/config-remove-agent.ts
1245
- import { homedir as homedir3 } from "os";
1345
+ import { homedir as homedir4 } from "os";
1246
1346
  function normalizeAgentId2(value) {
1247
1347
  const trimmed = value.trim();
1248
1348
  if (trimmed.length === 0 || /[a-z0-9]/i.test(trimmed) === false) {
@@ -1269,18 +1369,22 @@ function buildTableOutput2(result) {
1269
1369
  );
1270
1370
  }
1271
1371
  async function runConfigRemoveAgent(options) {
1272
- const homeDir = options.homeDir ?? homedir3();
1372
+ const homeDir = options.homeDir ?? homedir4();
1273
1373
  const resolvedPaths = resolveSkillmuxHome(homeDir);
1274
1374
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1275
1375
  const configPath = buildConfigPath(skillmuxHome);
1276
1376
  const config = await loadUserConfig(skillmuxHome);
1277
1377
  const agentId = normalizeAgentId2(options.id);
1278
1378
  const removed = agentId in config.agents;
1379
+ const removedOverride = config.agents[agentId];
1380
+ const wasAutoDiscovered = removedOverride?.autoDiscovered === true;
1381
+ const removedAutoAgentIds = wasAutoDiscovered ? [...config.removedAutoAgentIds ?? [], agentId] : config.removedAutoAgentIds;
1279
1382
  const nextConfig = {
1280
1383
  ...config,
1281
1384
  agents: Object.fromEntries(
1282
1385
  Object.entries(config.agents).filter(([currentAgentId]) => currentAgentId !== agentId)
1283
- )
1386
+ ),
1387
+ ...wasAutoDiscovered ? { removedAutoAgentIds } : {}
1284
1388
  };
1285
1389
  if (removed) {
1286
1390
  await writeUserConfig(skillmuxHome, nextConfig);
@@ -1300,7 +1404,7 @@ async function runConfigRemoveAgent(options) {
1300
1404
  }
1301
1405
 
1302
1406
  // src/commands/config-update-agent.ts
1303
- import { homedir as homedir4 } from "os";
1407
+ import { homedir as homedir5 } from "os";
1304
1408
  function buildAgentPatch(options) {
1305
1409
  const patch = {};
1306
1410
  if (options.root !== void 0) {
@@ -1364,7 +1468,7 @@ function buildTableOutput3(result) {
1364
1468
  return `${summary}${detail}`;
1365
1469
  }
1366
1470
  async function runConfigUpdateAgent(options) {
1367
- const homeDir = options.homeDir ?? homedir4();
1471
+ const homeDir = options.homeDir ?? homedir5();
1368
1472
  const resolvedPaths = resolveSkillmuxHome(homeDir);
1369
1473
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1370
1474
  const configPath = buildConfigPath(skillmuxHome);
@@ -1378,6 +1482,9 @@ async function runConfigUpdateAgent(options) {
1378
1482
  ...previous,
1379
1483
  ...buildAgentPatch(options)
1380
1484
  };
1485
+ if (previous.autoDiscovered === true) {
1486
+ delete agent.autoDiscovered;
1487
+ }
1381
1488
  const changed = JSON.stringify(previous) !== JSON.stringify(agent);
1382
1489
  const nextConfig = {
1383
1490
  ...config,
@@ -1404,11 +1511,11 @@ async function runConfigUpdateAgent(options) {
1404
1511
  }
1405
1512
 
1406
1513
  // src/commands/doctor.ts
1407
- import { homedir as homedir5 } from "os";
1514
+ import { homedir as homedir6 } from "os";
1408
1515
 
1409
1516
  // src/diagnostics/collect-doctor-issues.ts
1410
1517
  import * as fs11 from "fs/promises";
1411
- import { join as join8 } from "path";
1518
+ import { join as join9 } from "path";
1412
1519
  function buildIssue2(code, severity, message, path) {
1413
1520
  return path === void 0 ? { code, severity, message } : { code, severity, message, path };
1414
1521
  }
@@ -1428,7 +1535,7 @@ async function addUnmanagedDirectoryIssues(entries, issues) {
1428
1535
  if (entry.kind !== "unmanaged-directory") {
1429
1536
  continue;
1430
1537
  }
1431
- if (await pathExists2(join8(entry.path, "SKILL.md"))) {
1538
+ if (await pathExists2(join9(entry.path, "SKILL.md"))) {
1432
1539
  issues.push(
1433
1540
  buildIssue2(
1434
1541
  "unmanaged-skill-directory",
@@ -1534,7 +1641,7 @@ function buildJsonOutput(result) {
1534
1641
  });
1535
1642
  }
1536
1643
  async function runDoctor(options = {}) {
1537
- const homeDir = options.homeDir ?? homedir5();
1644
+ const homeDir = options.homeDir ?? homedir6();
1538
1645
  const resolvedPaths = resolveSkillmuxHome(homeDir);
1539
1646
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1540
1647
  const [manifest, config, agents] = await Promise.all([
@@ -1575,8 +1682,8 @@ async function runDoctor(options = {}) {
1575
1682
 
1576
1683
  // src/commands/disable.ts
1577
1684
  import * as fs13 from "fs/promises";
1578
- import { homedir as homedir6 } from "os";
1579
- import { join as join9, resolve as resolve9 } from "path";
1685
+ import { homedir as homedir7 } from "os";
1686
+ import { join as join10, resolve as resolve9 } from "path";
1580
1687
 
1581
1688
  // src/fs/safe-remove-link.ts
1582
1689
  import * as fs12 from "fs/promises";
@@ -1685,14 +1792,14 @@ async function runDisableSingle(options) {
1685
1792
  if (options.agent === void 0) {
1686
1793
  throw new Error("Disable requires one target agent");
1687
1794
  }
1688
- const homeDir = options.homeDir ?? homedir6();
1795
+ const homeDir = options.homeDir ?? homedir7();
1689
1796
  const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
1690
1797
  const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
1691
1798
  const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
1692
1799
  const manifest = await readManifest(skillmuxHome);
1693
1800
  const skillId = normalizeId(options.skill);
1694
1801
  const agent = await resolveTargetAgent2(homeDir, skillmuxHome, options.agent);
1695
- const linkPath = join9(agent.absoluteSkillsDirectoryPath, skillId);
1802
+ const linkPath = join10(agent.absoluteSkillsDirectoryPath, skillId);
1696
1803
  const adoption = manifest.skills[skillId] ? void 0 : await tryAdoptManagedSkill(
1697
1804
  manifest,
1698
1805
  skillmuxHome,
@@ -1776,8 +1883,8 @@ async function runDisable(options) {
1776
1883
 
1777
1884
  // src/commands/enable.ts
1778
1885
  import * as fs14 from "fs/promises";
1779
- import { homedir as homedir7 } from "os";
1780
- import { join as join10 } from "path";
1886
+ import { homedir as homedir8 } from "os";
1887
+ import { join as join11 } from "path";
1781
1888
  function buildAgentRecord3(agent, timestamp) {
1782
1889
  return {
1783
1890
  id: agent.id,
@@ -1823,7 +1930,7 @@ async function runEnableSingle(options) {
1823
1930
  if (options.agent === void 0) {
1824
1931
  throw new Error("Enable requires one target agent");
1825
1932
  }
1826
- const homeDir = options.homeDir ?? homedir7();
1933
+ const homeDir = options.homeDir ?? homedir8();
1827
1934
  const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
1828
1935
  const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
1829
1936
  const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
@@ -1834,7 +1941,7 @@ async function runEnableSingle(options) {
1834
1941
  throw new Error(`Managed skill not found: ${skillId}`);
1835
1942
  }
1836
1943
  const agent = await resolveTargetAgent3(homeDir, skillmuxHome, options.agent);
1837
- const linkPath = join10(agent.absoluteSkillsDirectoryPath, skill.id);
1944
+ const linkPath = join11(agent.absoluteSkillsDirectoryPath, skill.id);
1838
1945
  const currentActivation = manifest.activations.find(
1839
1946
  (entry) => entry.skillId === skill.id && entry.agentId === agent.id
1840
1947
  );
@@ -1909,12 +2016,12 @@ async function runEnable(options) {
1909
2016
 
1910
2017
  // src/commands/import.ts
1911
2018
  import { resolve as resolve10 } from "path";
1912
- import { homedir as homedir8 } from "os";
2019
+ import { homedir as homedir9 } from "os";
1913
2020
  function buildManagedSkillPath3(skillmuxHome, skillId) {
1914
2021
  return resolve10(skillmuxHome, "skills", skillId);
1915
2022
  }
1916
2023
  async function runImport(options) {
1917
- const homeDir = options.homeDir ?? homedir8();
2024
+ const homeDir = options.homeDir ?? homedir9();
1918
2025
  const resolvedPaths = resolveSkillmuxHome(homeDir);
1919
2026
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1920
2027
  const sourcePath = resolve10(options.sourcePath);
@@ -1948,7 +2055,7 @@ async function runImport(options) {
1948
2055
  }
1949
2056
 
1950
2057
  // src/commands/scan.ts
1951
- import { homedir as homedir9 } from "os";
2058
+ import { homedir as homedir10 } from "os";
1952
2059
 
1953
2060
  // src/output/format-issue.ts
1954
2061
  function formatIssue(issue) {
@@ -2009,7 +2116,7 @@ ${result.issues.map(formatIssue).join("\n")}
2009
2116
  `;
2010
2117
  }
2011
2118
  async function runScan(options = {}) {
2012
- const homeDir = options.homeDir ?? homedir9();
2119
+ const homeDir = options.homeDir ?? homedir10();
2013
2120
  const resolvedPaths = resolveSkillmuxHome(homeDir);
2014
2121
  const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
2015
2122
  const manifest = await readManifest(skillmuxHome);
@@ -2050,7 +2157,7 @@ async function runScan(options = {}) {
2050
2157
 
2051
2158
  // src/commands/remove.ts
2052
2159
  import * as fs15 from "fs/promises";
2053
- import { homedir as homedir10 } from "os";
2160
+ import { homedir as homedir11 } from "os";
2054
2161
  import { resolve as resolve11 } from "path";
2055
2162
  function buildManagedSkillPath4(skillmuxHome, skillId) {
2056
2163
  return resolve11(skillmuxHome, "skills", skillId);
@@ -2126,7 +2233,7 @@ async function runRemoveSingle(options) {
2126
2233
  if (options.skill === void 0) {
2127
2234
  throw new Error("Remove requires one target skill");
2128
2235
  }
2129
- const homeDir = options.homeDir ?? homedir10();
2236
+ const homeDir = options.homeDir ?? homedir11();
2130
2237
  const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
2131
2238
  const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
2132
2239
  const manifestPath = buildManifestPath(skillmuxHome);
@@ -2236,4 +2343,4 @@ export {
2236
2343
  runScan,
2237
2344
  runRemove
2238
2345
  };
2239
- //# sourceMappingURL=chunk-EEIOA7GC.js.map
2346
+ //# sourceMappingURL=chunk-OY3C7VIL.js.map