agent-switchboard 0.1.25 → 0.1.27
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/README.md +169 -229
- package/dist/commands/library.d.ts +1 -1
- package/dist/commands/library.js +4 -5
- package/dist/commands/library.js.map +1 -1
- package/dist/config/paths.d.ts +6 -0
- package/dist/config/paths.js +9 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/schemas.d.ts +305 -21
- package/dist/config/schemas.js +10 -4
- package/dist/config/schemas.js.map +1 -1
- package/dist/index.js +198 -103
- package/dist/index.js.map +1 -1
- package/dist/library/sources.d.ts +72 -0
- package/dist/library/sources.js +279 -0
- package/dist/library/sources.js.map +1 -0
- package/dist/rules/agents.d.ts +6 -2
- package/dist/rules/agents.js +11 -2
- package/dist/rules/agents.js.map +1 -1
- package/dist/rules/distribution.d.ts +2 -1
- package/dist/rules/distribution.js +55 -4
- package/dist/rules/distribution.js.map +1 -1
- package/dist/rules/library.d.ts +1 -1
- package/dist/rules/library.js +4 -5
- package/dist/rules/library.js.map +1 -1
- package/dist/skills/library.d.ts +1 -1
- package/dist/skills/library.js +4 -5
- package/dist/skills/library.js.map +1 -1
- package/dist/subagents/library.d.ts +1 -1
- package/dist/subagents/library.js +4 -5
- package/dist/subagents/library.js.map +1 -1
- package/package.json +1 -1
- package/dist/library/subscriptions.d.ts +0 -42
- package/dist/library/subscriptions.js +0 -116
- package/dist/library/subscriptions.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -16,14 +16,14 @@ import { importCommandFromFile } from './commands/importer.js';
|
|
|
16
16
|
import { buildCommandInventory } from './commands/inventory.js';
|
|
17
17
|
import { resolveAgentSectionConfig } from './config/agent-config.js';
|
|
18
18
|
import { loadMcpConfig, stripLegacyEnabledFlagsFromMcpJson } from './config/mcp-config.js';
|
|
19
|
-
import { getAgentsHome, getClaudeDir, getCodexDir, getCommandsDir, getCursorDir, getGeminiDir, getOpencodePath, getSkillsDir, getSubagentsDir, } from './config/paths.js';
|
|
19
|
+
import { getAgentsHome, getClaudeDir, getCodexDir, getCommandsDir, getCursorDir, getGeminiDir, getOpencodePath, getSkillsDir, getSourceCacheDir, getSubagentsDir, } from './config/paths.js';
|
|
20
20
|
import { loadSwitchboardConfig, loadSwitchboardConfigWithLayers, } from './config/switchboard-config.js';
|
|
21
21
|
import { ensureLibraryDirectories, writeFileSecure } from './library/fs.js';
|
|
22
|
+
import { addLocalSource, addRemoteSource, getSources, inferSourceName, isGitUrl, parseGitUrl, removeSource, updateRemoteSources, validateSourcePath, } from './library/sources.js';
|
|
22
23
|
import { loadMcpActiveState, saveMcpActiveState } from './library/state.js';
|
|
23
|
-
import { addSubscription, getSubscriptions, removeSubscription, validateSubscriptionPath, } from './library/subscriptions.js';
|
|
24
24
|
import { RULE_SUPPORTED_AGENTS } from './rules/agents.js';
|
|
25
25
|
import { composeActiveRules } from './rules/composer.js';
|
|
26
|
-
import { distributeRules, listIndirectAgents, listUnsupportedAgents, } from './rules/distribution.js';
|
|
26
|
+
import { distributeRules, listIndirectAgents, listPerFileAgents, listUnsupportedAgents, } from './rules/distribution.js';
|
|
27
27
|
import { buildRuleInventory } from './rules/inventory.js';
|
|
28
28
|
import { loadRuleLibrary } from './rules/library.js';
|
|
29
29
|
import { loadRuleState, updateRuleState } from './rules/state.js';
|
|
@@ -44,8 +44,17 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
44
44
|
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
45
45
|
program
|
|
46
46
|
.name('asb')
|
|
47
|
-
.description('
|
|
48
|
-
.version(packageJson.version)
|
|
47
|
+
.description('Manage MCP servers, rules, commands, subagents, and skills across AI coding agents')
|
|
48
|
+
.version(packageJson.version)
|
|
49
|
+
.addHelpText('after', `
|
|
50
|
+
Examples:
|
|
51
|
+
$ asb mcp Enable/disable MCP servers interactively
|
|
52
|
+
$ asb rule Select and order rule snippets
|
|
53
|
+
$ asb sync Push all libraries to every active agent
|
|
54
|
+
$ asb sync --project . Sync with project-level overrides
|
|
55
|
+
|
|
56
|
+
Alias: agent-switchboard
|
|
57
|
+
Config: ~/.agent-switchboard/config.toml`);
|
|
49
58
|
// Initialize library directories for commands/subagents (secure permissions)
|
|
50
59
|
ensureLibraryDirectories();
|
|
51
60
|
program
|
|
@@ -53,6 +62,7 @@ program
|
|
|
53
62
|
.description('Synchronize active MCP servers, rules, commands, subagents, and skills to agent targets')
|
|
54
63
|
.option('-p, --profile <name>', 'Profile configuration to use')
|
|
55
64
|
.option('--project <path>', 'Project directory containing .asb.toml')
|
|
65
|
+
.option('--no-update', 'Skip updating remote sources')
|
|
56
66
|
.action(async (options) => {
|
|
57
67
|
try {
|
|
58
68
|
const scope = resolveScope(options);
|
|
@@ -62,6 +72,21 @@ program
|
|
|
62
72
|
console.log(`${chalk.bgRed.white(' WARNING ')} ${chalk.red('`asb sync` overwrites target files without diff checks.')}`);
|
|
63
73
|
console.log(chalk.red('Proceeding with synchronization...'));
|
|
64
74
|
console.log();
|
|
75
|
+
if (options.update !== false) {
|
|
76
|
+
const remoteResults = updateRemoteSources();
|
|
77
|
+
if (remoteResults.length > 0) {
|
|
78
|
+
console.log(chalk.blue('Remote source updates:'));
|
|
79
|
+
for (const result of remoteResults) {
|
|
80
|
+
if (result.status === 'updated') {
|
|
81
|
+
console.log(` ${chalk.green('✓')} ${chalk.cyan(result.namespace)} ${chalk.dim(result.url)}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log(` ${chalk.yellow('⚠')} ${chalk.cyan(result.namespace)} ${chalk.yellow(result.error ?? 'update failed')}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
console.log();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
65
90
|
console.log(chalk.blue('Configuration layers:'));
|
|
66
91
|
const layerEntries = [
|
|
67
92
|
{ label: 'User', exists: layers.user.exists, path: layers.user.path },
|
|
@@ -335,7 +360,7 @@ program
|
|
|
335
360
|
});
|
|
336
361
|
const ruleCommand = program
|
|
337
362
|
.command('rule')
|
|
338
|
-
.description('
|
|
363
|
+
.description('Select and order rule snippets interactively, then sync to agents')
|
|
339
364
|
.option('-p, --profile <name>', 'Profile configuration to use')
|
|
340
365
|
.option('--project <path>', 'Project directory containing .asb.toml');
|
|
341
366
|
ruleCommand
|
|
@@ -396,6 +421,10 @@ ruleCommand
|
|
|
396
421
|
console.log();
|
|
397
422
|
console.log(chalk.gray(`Unsupported agents (manual update required): ${unsupportedAgents.join(', ')}`));
|
|
398
423
|
}
|
|
424
|
+
const perFileAgents = listPerFileAgents();
|
|
425
|
+
if (perFileAgents.length > 0) {
|
|
426
|
+
console.log(chalk.gray(`Per-file rules (.mdc): ${perFileAgents.join(', ')}`));
|
|
427
|
+
}
|
|
399
428
|
const indirectAgents = listIndirectAgents();
|
|
400
429
|
if (indirectAgents.length > 0) {
|
|
401
430
|
console.log(chalk.gray(`Indirect rules support (reads CLAUDE.md + AGENTS.md): ${indirectAgents.join(', ')}`));
|
|
@@ -474,6 +503,10 @@ ruleCommand.action(async (options) => {
|
|
|
474
503
|
console.log();
|
|
475
504
|
console.log(chalk.gray(`Unsupported agents (manual update required): ${unsupportedAgents.join(', ')}`));
|
|
476
505
|
}
|
|
506
|
+
const perFileAgents = listPerFileAgents();
|
|
507
|
+
if (perFileAgents.length > 0) {
|
|
508
|
+
console.log(chalk.gray(`Per-file rules (.mdc): ${perFileAgents.join(', ')}`));
|
|
509
|
+
}
|
|
477
510
|
const indirectAgents = listIndirectAgents();
|
|
478
511
|
if (indirectAgents.length > 0) {
|
|
479
512
|
console.log(chalk.gray(`Indirect rules support (reads CLAUDE.md + AGENTS.md): ${indirectAgents.join(', ')}`));
|
|
@@ -489,7 +522,7 @@ ruleCommand.action(async (options) => {
|
|
|
489
522
|
// Commands library: manage and distribute commands
|
|
490
523
|
const commandRoot = program
|
|
491
524
|
.command('command')
|
|
492
|
-
.description('
|
|
525
|
+
.description('Select slash commands interactively and distribute to agents')
|
|
493
526
|
.option('-p, --profile <name>', 'Profile configuration to use')
|
|
494
527
|
.option('--project <path>', 'Project directory containing .asb.toml');
|
|
495
528
|
commandRoot.action(async (options) => {
|
|
@@ -638,7 +671,7 @@ commandRoot
|
|
|
638
671
|
// Subagents library: scaffold, load, list, and interactive distribute
|
|
639
672
|
const subagentRoot = program
|
|
640
673
|
.command('subagent')
|
|
641
|
-
.description('
|
|
674
|
+
.description('Select subagent definitions interactively and distribute to agents')
|
|
642
675
|
.option('-p, --profile <name>', 'Profile configuration to use')
|
|
643
676
|
.option('--project <path>', 'Project directory containing .asb.toml');
|
|
644
677
|
subagentRoot.action(async (options) => {
|
|
@@ -671,6 +704,62 @@ subagentRoot.action(async (options) => {
|
|
|
671
704
|
process.exit(1);
|
|
672
705
|
}
|
|
673
706
|
});
|
|
707
|
+
subagentRoot
|
|
708
|
+
.command('load')
|
|
709
|
+
.description('Import existing platform files into the subagent library')
|
|
710
|
+
.argument('<platform>', 'claude-code | opencode | cursor')
|
|
711
|
+
.argument('[path]', 'Source file or directory (defaults by platform)')
|
|
712
|
+
.option('-r, --recursive', 'When [path] is a directory, import files recursively')
|
|
713
|
+
.option('-f, --force', 'Overwrite existing library files without confirmation')
|
|
714
|
+
.action(async (platform, srcPath, opts) => {
|
|
715
|
+
try {
|
|
716
|
+
const exts = ['.md', '.markdown'];
|
|
717
|
+
const source = srcPath && srcPath.trim().length > 0 ? srcPath : defaultSubagentSourceDir(platform);
|
|
718
|
+
if (!fs.existsSync(source)) {
|
|
719
|
+
console.error(chalk.red(`\n✗ Source not found: ${source}`));
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
const inputs = [];
|
|
723
|
+
if (isFile(source)) {
|
|
724
|
+
inputs.push(source);
|
|
725
|
+
}
|
|
726
|
+
else if (isDir(source)) {
|
|
727
|
+
if (!opts.recursive) {
|
|
728
|
+
console.error(chalk.red('\n✗ Source is a directory. Use -r/--recursive to import recursively.'));
|
|
729
|
+
process.exit(1);
|
|
730
|
+
}
|
|
731
|
+
inputs.push(...listFilesRecursively(source, exts));
|
|
732
|
+
}
|
|
733
|
+
if (inputs.length === 0) {
|
|
734
|
+
console.log(chalk.yellow('\n⚠ No files to import.'));
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const outDir = getSubagentsDir();
|
|
738
|
+
let imported = 0;
|
|
739
|
+
for (const file of inputs) {
|
|
740
|
+
try {
|
|
741
|
+
const { slug, content } = importSubagentFromFile(platform, file);
|
|
742
|
+
const target = path.join(outDir, `${slug}.md`);
|
|
743
|
+
if (!(await confirmOverwrite(target, opts.force)))
|
|
744
|
+
continue;
|
|
745
|
+
writeFileSecure(target, content);
|
|
746
|
+
imported++;
|
|
747
|
+
console.log(`${chalk.green('✓')} ${chalk.cyan(slug)} → ${chalk.dim(target)}`);
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
751
|
+
console.log(`${chalk.red('✗')} ${chalk.dim(file)} ${chalk.red(msg)}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
console.log(`\n${chalk.green('✓')} Imported ${imported} file(s) into subagent library.`);
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
if (error instanceof Error) {
|
|
758
|
+
console.error(chalk.red(`\n✗ Error: ${error.message}`));
|
|
759
|
+
}
|
|
760
|
+
process.exit(1);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
674
763
|
subagentRoot
|
|
675
764
|
.command('list')
|
|
676
765
|
.description('Display subagent inventory and sync information')
|
|
@@ -728,66 +817,10 @@ subagentRoot
|
|
|
728
817
|
process.exit(1);
|
|
729
818
|
}
|
|
730
819
|
});
|
|
731
|
-
subagentRoot
|
|
732
|
-
.command('load')
|
|
733
|
-
.description('Import existing platform files into the subagent library')
|
|
734
|
-
.argument('<platform>', 'claude-code | opencode | cursor')
|
|
735
|
-
.argument('[path]', 'Source file or directory (defaults by platform)')
|
|
736
|
-
.option('-r, --recursive', 'When [path] is a directory, import files recursively')
|
|
737
|
-
.option('-f, --force', 'Overwrite existing library files without confirmation')
|
|
738
|
-
.action(async (platform, srcPath, opts) => {
|
|
739
|
-
try {
|
|
740
|
-
const exts = ['.md', '.markdown'];
|
|
741
|
-
const source = srcPath && srcPath.trim().length > 0 ? srcPath : defaultSubagentSourceDir(platform);
|
|
742
|
-
if (!fs.existsSync(source)) {
|
|
743
|
-
console.error(chalk.red(`\n✗ Source not found: ${source}`));
|
|
744
|
-
process.exit(1);
|
|
745
|
-
}
|
|
746
|
-
const inputs = [];
|
|
747
|
-
if (isFile(source)) {
|
|
748
|
-
inputs.push(source);
|
|
749
|
-
}
|
|
750
|
-
else if (isDir(source)) {
|
|
751
|
-
if (!opts.recursive) {
|
|
752
|
-
console.error(chalk.red('\n✗ Source is a directory. Use -r/--recursive to import recursively.'));
|
|
753
|
-
process.exit(1);
|
|
754
|
-
}
|
|
755
|
-
inputs.push(...listFilesRecursively(source, exts));
|
|
756
|
-
}
|
|
757
|
-
if (inputs.length === 0) {
|
|
758
|
-
console.log(chalk.yellow('\n⚠ No files to import.'));
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
const outDir = getSubagentsDir();
|
|
762
|
-
let imported = 0;
|
|
763
|
-
for (const file of inputs) {
|
|
764
|
-
try {
|
|
765
|
-
const { slug, content } = importSubagentFromFile(platform, file);
|
|
766
|
-
const target = path.join(outDir, `${slug}.md`);
|
|
767
|
-
if (!(await confirmOverwrite(target, opts.force)))
|
|
768
|
-
continue;
|
|
769
|
-
writeFileSecure(target, content);
|
|
770
|
-
imported++;
|
|
771
|
-
console.log(`${chalk.green('✓')} ${chalk.cyan(slug)} → ${chalk.dim(target)}`);
|
|
772
|
-
}
|
|
773
|
-
catch (error) {
|
|
774
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
775
|
-
console.log(`${chalk.red('✗')} ${chalk.dim(file)} ${chalk.red(msg)}`);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
console.log(`\n${chalk.green('✓')} Imported ${imported} file(s) into subagent library.`);
|
|
779
|
-
}
|
|
780
|
-
catch (error) {
|
|
781
|
-
if (error instanceof Error) {
|
|
782
|
-
console.error(chalk.red(`\n✗ Error: ${error.message}`));
|
|
783
|
-
}
|
|
784
|
-
process.exit(1);
|
|
785
|
-
}
|
|
786
|
-
});
|
|
787
820
|
// Skills library: manage and distribute skill bundles
|
|
788
821
|
const skillRoot = program
|
|
789
822
|
.command('skill')
|
|
790
|
-
.description('
|
|
823
|
+
.description('Select skill bundles interactively and distribute to agents')
|
|
791
824
|
.option('-p, --profile <name>', 'Profile configuration to use')
|
|
792
825
|
.option('--project <path>', 'Project directory containing .asb.toml');
|
|
793
826
|
skillRoot.action(async (options) => {
|
|
@@ -1007,25 +1040,64 @@ function showSummary(selectedServers, scope) {
|
|
|
1007
1040
|
}
|
|
1008
1041
|
console.log();
|
|
1009
1042
|
}
|
|
1010
|
-
// Library
|
|
1011
|
-
program
|
|
1012
|
-
.command('
|
|
1013
|
-
.description('
|
|
1014
|
-
|
|
1015
|
-
.
|
|
1016
|
-
.
|
|
1043
|
+
// Library source management commands
|
|
1044
|
+
const sourceRoot = program
|
|
1045
|
+
.command('source')
|
|
1046
|
+
.description('Manage external library sources (local paths or git repos)');
|
|
1047
|
+
sourceRoot
|
|
1048
|
+
.command('add')
|
|
1049
|
+
.description('Add a library source (local path or git URL)')
|
|
1050
|
+
.argument('<location>', 'Local path or git URL (e.g., https://github.com/org/repo)')
|
|
1051
|
+
.argument('[name]', 'Namespace (defaults to repo or directory name)')
|
|
1052
|
+
.action((location, nameArg) => {
|
|
1017
1053
|
try {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1054
|
+
const name = nameArg ?? inferSourceName(location);
|
|
1055
|
+
if (isGitUrl(location)) {
|
|
1056
|
+
const parsed = parseGitUrl(location);
|
|
1057
|
+
const spinner = ora(`Cloning ${parsed.url}...`).start();
|
|
1058
|
+
try {
|
|
1059
|
+
addRemoteSource(name, {
|
|
1060
|
+
url: parsed.url,
|
|
1061
|
+
ref: parsed.ref,
|
|
1062
|
+
subdir: parsed.subdir,
|
|
1063
|
+
});
|
|
1064
|
+
spinner.succeed(chalk.green(`✓ Cloned ${parsed.url}`));
|
|
1065
|
+
}
|
|
1066
|
+
catch (err) {
|
|
1067
|
+
spinner.fail(chalk.red('Failed to clone'));
|
|
1068
|
+
throw err;
|
|
1069
|
+
}
|
|
1070
|
+
let effectivePath = getSourceCacheDir(name);
|
|
1071
|
+
if (parsed.subdir)
|
|
1072
|
+
effectivePath = path.join(effectivePath, parsed.subdir);
|
|
1073
|
+
const validation = validateSourcePath(effectivePath);
|
|
1074
|
+
if (!validation.valid) {
|
|
1075
|
+
removeSource(name);
|
|
1076
|
+
console.error(chalk.red('\n✗ Cloned repository does not contain any library folders (rules/, commands/, subagents/, skills/).'));
|
|
1077
|
+
process.exit(1);
|
|
1078
|
+
}
|
|
1079
|
+
console.log(chalk.green(`\n✓ Added source "${name}" from ${parsed.url}`));
|
|
1080
|
+
if (parsed.ref)
|
|
1081
|
+
console.log(chalk.dim(` Ref: ${parsed.ref}`));
|
|
1082
|
+
if (parsed.subdir)
|
|
1083
|
+
console.log(chalk.dim(` Subdir: ${parsed.subdir}`));
|
|
1084
|
+
console.log(chalk.dim(` Found: ${validation.found.join(', ')}`));
|
|
1085
|
+
if (validation.missing.length > 0) {
|
|
1086
|
+
console.log(chalk.dim(` Missing: ${validation.missing.join(', ')}`));
|
|
1087
|
+
}
|
|
1023
1088
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1089
|
+
else {
|
|
1090
|
+
const validation = validateSourcePath(location);
|
|
1091
|
+
if (!validation.valid) {
|
|
1092
|
+
console.error(chalk.red('\n✗ Path does not contain any library folders (rules/, commands/, subagents/, skills/).'));
|
|
1093
|
+
process.exit(1);
|
|
1094
|
+
}
|
|
1095
|
+
addLocalSource(name, location);
|
|
1096
|
+
console.log(chalk.green(`\n✓ Added source "${name}" at ${path.resolve(location)}`));
|
|
1097
|
+
console.log(chalk.dim(` Found: ${validation.found.join(', ')}`));
|
|
1098
|
+
if (validation.missing.length > 0) {
|
|
1099
|
+
console.log(chalk.dim(` Missing: ${validation.missing.join(', ')}`));
|
|
1100
|
+
}
|
|
1029
1101
|
}
|
|
1030
1102
|
console.log();
|
|
1031
1103
|
console.log(chalk.dim('Library entries will now use the namespace prefix, e.g., ') +
|
|
@@ -1038,14 +1110,21 @@ program
|
|
|
1038
1110
|
process.exit(1);
|
|
1039
1111
|
}
|
|
1040
1112
|
});
|
|
1041
|
-
|
|
1042
|
-
.command('
|
|
1043
|
-
.description('Remove a library
|
|
1113
|
+
sourceRoot
|
|
1114
|
+
.command('remove')
|
|
1115
|
+
.description('Remove a library source by namespace')
|
|
1044
1116
|
.argument('<name>', 'Namespace to remove')
|
|
1045
1117
|
.action((name) => {
|
|
1046
1118
|
try {
|
|
1047
|
-
|
|
1048
|
-
|
|
1119
|
+
const sources = getSources();
|
|
1120
|
+
const source = sources.find((s) => s.namespace === name);
|
|
1121
|
+
removeSource(name);
|
|
1122
|
+
if (source?.remote) {
|
|
1123
|
+
console.log(chalk.green(`\n✓ Removed source "${name}" and cleaned up cache`));
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
console.log(chalk.green(`\n✓ Removed source "${name}"`));
|
|
1127
|
+
}
|
|
1049
1128
|
}
|
|
1050
1129
|
catch (error) {
|
|
1051
1130
|
if (error instanceof Error) {
|
|
@@ -1054,32 +1133,45 @@ program
|
|
|
1054
1133
|
process.exit(1);
|
|
1055
1134
|
}
|
|
1056
1135
|
});
|
|
1057
|
-
|
|
1058
|
-
.command('
|
|
1059
|
-
.description('List all library
|
|
1136
|
+
sourceRoot
|
|
1137
|
+
.command('list')
|
|
1138
|
+
.description('List all library sources')
|
|
1060
1139
|
.option('--json', 'Output as JSON')
|
|
1061
1140
|
.action((options) => {
|
|
1062
1141
|
try {
|
|
1063
|
-
const
|
|
1142
|
+
const sources = getSources();
|
|
1064
1143
|
if (options.json) {
|
|
1065
|
-
console.log(JSON.stringify(
|
|
1144
|
+
console.log(JSON.stringify(sources, null, 2));
|
|
1066
1145
|
return;
|
|
1067
1146
|
}
|
|
1068
|
-
if (
|
|
1069
|
-
console.log(chalk.yellow('\n⚠ No library
|
|
1070
|
-
console.log(chalk.dim(' Use `asb
|
|
1147
|
+
if (sources.length === 0) {
|
|
1148
|
+
console.log(chalk.yellow('\n⚠ No library sources configured.'));
|
|
1149
|
+
console.log(chalk.dim(' Use `asb source add <location> [name]` to add one.'));
|
|
1071
1150
|
return;
|
|
1072
1151
|
}
|
|
1073
|
-
console.log(chalk.blue('\nLibrary
|
|
1074
|
-
const header = ['Namespace', '
|
|
1075
|
-
const rows =
|
|
1076
|
-
const
|
|
1077
|
-
const
|
|
1078
|
-
const
|
|
1152
|
+
console.log(chalk.blue('\nLibrary sources:'));
|
|
1153
|
+
const header = ['Namespace', 'Type', 'Source', 'Status', 'Contains'];
|
|
1154
|
+
const rows = sources.map((src) => {
|
|
1155
|
+
const isRemote = !!src.remote;
|
|
1156
|
+
const typePlain = isRemote ? 'remote' : 'local';
|
|
1157
|
+
const sourcePlain = isRemote ? (src.remote?.url ?? src.path) : src.path;
|
|
1158
|
+
const exists = fs.existsSync(src.path);
|
|
1159
|
+
const validation = exists ? validateSourcePath(src.path) : { found: [], missing: [] };
|
|
1160
|
+
let statusPlain;
|
|
1161
|
+
if (isRemote) {
|
|
1162
|
+
statusPlain = exists ? 'cached' : 'not cached';
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
statusPlain = exists ? 'ok' : 'missing';
|
|
1166
|
+
}
|
|
1079
1167
|
const containsPlain = validation.found.length > 0 ? validation.found.join(', ') : '-';
|
|
1080
1168
|
return [
|
|
1081
|
-
{ plain:
|
|
1082
|
-
{
|
|
1169
|
+
{ plain: src.namespace, formatted: chalk.cyan(src.namespace) },
|
|
1170
|
+
{
|
|
1171
|
+
plain: typePlain,
|
|
1172
|
+
formatted: isRemote ? chalk.blue(typePlain) : chalk.gray(typePlain),
|
|
1173
|
+
},
|
|
1174
|
+
{ plain: sourcePlain, formatted: chalk.dim(sourcePlain) },
|
|
1083
1175
|
{
|
|
1084
1176
|
plain: statusPlain,
|
|
1085
1177
|
formatted: exists ? chalk.green(statusPlain) : chalk.red(statusPlain),
|
|
@@ -1097,5 +1189,8 @@ program
|
|
|
1097
1189
|
process.exit(1);
|
|
1098
1190
|
}
|
|
1099
1191
|
});
|
|
1192
|
+
sourceRoot.action(() => {
|
|
1193
|
+
sourceRoot.commands.find((c) => c.name() === 'list')?.parse(process.argv);
|
|
1194
|
+
});
|
|
1100
1195
|
program.parse(process.argv);
|
|
1101
1196
|
//# sourceMappingURL=index.js.map
|