@sundial-ai/cli 0.0.1
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/DEV.md +58 -0
- package/README.md +30 -0
- package/dist/commands/add.d.ts +13 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +111 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +40 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +53 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/remove.d.ts +13 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +129 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/show-dev.d.ts +20 -0
- package/dist/commands/show-dev.d.ts.map +1 -0
- package/dist/commands/show-dev.js +195 -0
- package/dist/commands/show-dev.js.map +1 -0
- package/dist/commands/show.d.ts +11 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +175 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/core/agent-detect.d.ts +22 -0
- package/dist/core/agent-detect.d.ts.map +1 -0
- package/dist/core/agent-detect.js +107 -0
- package/dist/core/agent-detect.js.map +1 -0
- package/dist/core/agents.d.ts +8 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/core/agents.js +34 -0
- package/dist/core/agents.js.map +1 -0
- package/dist/core/config-manager.d.ts +9 -0
- package/dist/core/config-manager.d.ts.map +1 -0
- package/dist/core/config-manager.js +47 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/skill-hash.d.ts +12 -0
- package/dist/core/skill-hash.d.ts.map +1 -0
- package/dist/core/skill-hash.js +53 -0
- package/dist/core/skill-hash.js.map +1 -0
- package/dist/core/skill-info.d.ts +34 -0
- package/dist/core/skill-info.d.ts.map +1 -0
- package/dist/core/skill-info.js +213 -0
- package/dist/core/skill-info.js.map +1 -0
- package/dist/core/skill-install.d.ts +24 -0
- package/dist/core/skill-install.d.ts.map +1 -0
- package/dist/core/skill-install.js +123 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/skill-source.d.ts +29 -0
- package/dist/core/skill-source.d.ts.map +1 -0
- package/dist/core/skill-source.js +111 -0
- package/dist/core/skill-source.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +57 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/fuzzy-match.d.ts +16 -0
- package/dist/utils/fuzzy-match.d.ts.map +1 -0
- package/dist/utils/fuzzy-match.js +37 -0
- package/dist/utils/fuzzy-match.js.map +1 -0
- package/dist/utils/prompts.d.ts +16 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +46 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/registry.d.ts +6 -0
- package/dist/utils/registry.d.ts.map +1 -0
- package/dist/utils/registry.js +14 -0
- package/dist/utils/registry.js.map +1 -0
- package/package.json +42 -0
- package/src/commands/add.ts +136 -0
- package/src/commands/config.ts +47 -0
- package/src/commands/list.ts +68 -0
- package/src/commands/remove.ts +154 -0
- package/src/commands/show-dev.ts +223 -0
- package/src/commands/show.ts +203 -0
- package/src/core/agent-detect.ts +125 -0
- package/src/core/agents.ts +40 -0
- package/src/core/config-manager.ts +55 -0
- package/src/core/skill-hash.ts +61 -0
- package/src/core/skill-info.ts +248 -0
- package/src/core/skill-install.ts +165 -0
- package/src/core/skill-source.ts +125 -0
- package/src/index.ts +116 -0
- package/src/types/index.ts +64 -0
- package/src/utils/fuzzy-match.ts +48 -0
- package/src/utils/prompts.ts +54 -0
- package/src/utils/registry.ts +16 -0
- package/test/README.md +123 -0
- package/test/fixtures/multi-skills/skill-one/SKILL.md +8 -0
- package/test/fixtures/multi-skills/skill-two/SKILL.md +8 -0
- package/test/fixtures/sample-skill/SKILL.md +8 -0
- package/test/logs/add-remove.log +108 -0
- package/test/logs/config.log +72 -0
- package/test/logs/fuzzy-match.log +64 -0
- package/test/logs/show.log +110 -0
- package/test/run-all.sh +83 -0
- package/test/test-add-remove.sh +245 -0
- package/test/test-config.sh +208 -0
- package/test/test-fuzzy-match.sh +166 -0
- package/test/test-show.sh +179 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { isShortcut, getShortcutUrl } from '../utils/registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Check if input looks like a GitHub URL or reference.
|
|
6
|
+
* Matches: github.com/user/repo, https://github.com/..., etc.
|
|
7
|
+
*/
|
|
8
|
+
export function isGithubUrl(input) {
|
|
9
|
+
return input.includes('github.com');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a GitHub URL to degit format.
|
|
13
|
+
* Converts: https://github.com/user/repo/tree/branch/path -> user/repo/path#branch
|
|
14
|
+
*/
|
|
15
|
+
function normalizeGithubUrl(url) {
|
|
16
|
+
let location = url;
|
|
17
|
+
// Remove https:// or http:// prefix if present
|
|
18
|
+
location = location.replace(/^https?:\/\//, '');
|
|
19
|
+
// Handle github.com/user/repo/tree/branch/path format
|
|
20
|
+
// Convert to degit format: user/repo/path#branch
|
|
21
|
+
const treeMatch = location.match(/^github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
22
|
+
if (treeMatch) {
|
|
23
|
+
const [, user, repo, branch, subpath] = treeMatch;
|
|
24
|
+
location = `${user}/${repo}/${subpath}#${branch}`;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Simple format: github.com/user/repo -> user/repo
|
|
28
|
+
location = location.replace(/^github\.com\//, '');
|
|
29
|
+
}
|
|
30
|
+
return location;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if input looks like a local file path.
|
|
34
|
+
* Matches: ./path, ../path, ~/path, /absolute/path
|
|
35
|
+
*/
|
|
36
|
+
export function isLocalPath(input) {
|
|
37
|
+
// Check if it starts with path indicators
|
|
38
|
+
if (input.startsWith('./') ||
|
|
39
|
+
input.startsWith('../') ||
|
|
40
|
+
input.startsWith('~/') ||
|
|
41
|
+
input.startsWith('/')) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
// Check if it's an existing path on disk
|
|
45
|
+
const resolved = path.resolve(input);
|
|
46
|
+
return fs.pathExistsSync(resolved);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a skill input to its source type and location.
|
|
50
|
+
*
|
|
51
|
+
* Resolution order:
|
|
52
|
+
* 1. Check if it's a registered shortcut (e.g., "tinker")
|
|
53
|
+
* 2. Check if it contains "github.com" (treat as GitHub URL)
|
|
54
|
+
* 3. Check if it's a valid local path
|
|
55
|
+
* 4. Otherwise, throw error
|
|
56
|
+
*/
|
|
57
|
+
export function resolveSkillSource(input) {
|
|
58
|
+
// 1. Check shortcuts first
|
|
59
|
+
if (isShortcut(input)) {
|
|
60
|
+
const url = getShortcutUrl(input);
|
|
61
|
+
return {
|
|
62
|
+
type: 'shortcut',
|
|
63
|
+
location: normalizeGithubUrl(url),
|
|
64
|
+
originalInput: input
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// 2. Check if it's a GitHub URL
|
|
68
|
+
if (isGithubUrl(input)) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'github',
|
|
71
|
+
location: normalizeGithubUrl(input),
|
|
72
|
+
originalInput: input
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// 3. Check if it's a local path
|
|
76
|
+
if (isLocalPath(input)) {
|
|
77
|
+
// Resolve to absolute path, handling ~
|
|
78
|
+
let location = input;
|
|
79
|
+
if (location.startsWith('~/')) {
|
|
80
|
+
location = path.join(process.env.HOME || '', location.slice(2));
|
|
81
|
+
}
|
|
82
|
+
location = path.resolve(location);
|
|
83
|
+
return {
|
|
84
|
+
type: 'local',
|
|
85
|
+
location,
|
|
86
|
+
originalInput: input
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// 4. Not found
|
|
90
|
+
throw new Error(`Skill not found: "${input}". Expected a shortcut name, GitHub URL, or local path.`);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract skill name from a source.
|
|
94
|
+
* For GitHub: last path segment
|
|
95
|
+
* For local: folder name
|
|
96
|
+
* For shortcut: the shortcut name itself
|
|
97
|
+
*/
|
|
98
|
+
export function getSkillNameFromSource(source) {
|
|
99
|
+
if (source.type === 'shortcut') {
|
|
100
|
+
return source.originalInput;
|
|
101
|
+
}
|
|
102
|
+
if (source.type === 'local') {
|
|
103
|
+
return path.basename(source.location);
|
|
104
|
+
}
|
|
105
|
+
// GitHub: extract from path (e.g., "user/repo/skills/tinker#main" -> "tinker")
|
|
106
|
+
const parts = source.location.split('/');
|
|
107
|
+
const lastPart = parts[parts.length - 1];
|
|
108
|
+
// Remove branch suffix if present (e.g., "tinker#main" -> "tinker")
|
|
109
|
+
return lastPart.split('#')[0];
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=skill-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-source.js","sourceRoot":"","sources":["../../src/core/skill-source.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { SUPPORTED_AGENTS } from './core/agents.js';
|
|
5
|
+
import { addCommand } from './commands/add.js';
|
|
6
|
+
import { removeCommand } from './commands/remove.js';
|
|
7
|
+
import { listCommand } from './commands/list.js';
|
|
8
|
+
import { showCommand } from './commands/show.js';
|
|
9
|
+
import { configCommand } from './commands/config.js';
|
|
10
|
+
import { suggestCommand, getValidCommands } from './utils/fuzzy-match.js';
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name('sun')
|
|
14
|
+
.description('Sundial CLI - Manage skills for your AI agents')
|
|
15
|
+
.version('0.1.0');
|
|
16
|
+
// Add command
|
|
17
|
+
const add = program
|
|
18
|
+
.command('add <skills...>')
|
|
19
|
+
.description('Add skill(s) to agent configuration(s)')
|
|
20
|
+
.option('--global', 'Install to global agent config (~/.claude/, ~/.codex/, etc.)');
|
|
21
|
+
// Add agent flags dynamically
|
|
22
|
+
for (const agent of SUPPORTED_AGENTS) {
|
|
23
|
+
add.option(`--${agent.flag}`, `Install to ${agent.name}`);
|
|
24
|
+
}
|
|
25
|
+
add.action(async (skills, options) => {
|
|
26
|
+
try {
|
|
27
|
+
await addCommand(skills, options);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
// Remove command
|
|
35
|
+
const remove = program
|
|
36
|
+
.command('remove <skills...>')
|
|
37
|
+
.description('Remove skill(s) from agent configuration(s)')
|
|
38
|
+
.option('--global', 'Remove from global config');
|
|
39
|
+
for (const agent of SUPPORTED_AGENTS) {
|
|
40
|
+
remove.option(`--${agent.flag}`, `Remove from ${agent.name}`);
|
|
41
|
+
}
|
|
42
|
+
remove.action(async (skills, options) => {
|
|
43
|
+
try {
|
|
44
|
+
await removeCommand(skills, options);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// List command
|
|
52
|
+
program
|
|
53
|
+
.command('list')
|
|
54
|
+
.description('List all installed skills for each agent')
|
|
55
|
+
.action(async () => {
|
|
56
|
+
try {
|
|
57
|
+
await listCommand();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// Show command
|
|
65
|
+
program
|
|
66
|
+
.command('show [skill]')
|
|
67
|
+
.description('Show all agent folders and packages, or details for a specific skill')
|
|
68
|
+
.action(async (skill) => {
|
|
69
|
+
try {
|
|
70
|
+
await showCommand(skill);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Config command
|
|
78
|
+
program
|
|
79
|
+
.command('config')
|
|
80
|
+
.description('Configure default agents')
|
|
81
|
+
.action(async () => {
|
|
82
|
+
try {
|
|
83
|
+
await configCommand();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// Handle unknown commands with fuzzy matching
|
|
91
|
+
program.on('command:*', (operands) => {
|
|
92
|
+
const unknownCommand = operands[0];
|
|
93
|
+
const suggestion = suggestCommand(unknownCommand);
|
|
94
|
+
console.error(chalk.red(`Error: Unknown command '${unknownCommand}'`));
|
|
95
|
+
if (suggestion) {
|
|
96
|
+
console.error(chalk.yellow(`Did you mean '${suggestion}'?`));
|
|
97
|
+
}
|
|
98
|
+
console.error();
|
|
99
|
+
console.error(`Valid commands: ${getValidCommands().join(', ')}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
// Parse and execute
|
|
103
|
+
program.parse();
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type AgentType = 'claude' | 'codex' | 'gemini';
|
|
2
|
+
export interface AgentConfig {
|
|
3
|
+
name: string;
|
|
4
|
+
flag: string;
|
|
5
|
+
folderName: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SunConfig {
|
|
8
|
+
defaultAgents: AgentType[];
|
|
9
|
+
firstRunComplete: boolean;
|
|
10
|
+
skillRegistryUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Skill metadata parsed from SKILL.md frontmatter.
|
|
14
|
+
* Per spec: https://agentskills.io/specification#skill-md-format
|
|
15
|
+
*/
|
|
16
|
+
export interface SkillMetadata {
|
|
17
|
+
/** Required: Max 64 chars, lowercase letters, numbers, hyphens only */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Required: Max 1024 chars, describes what the skill does */
|
|
20
|
+
description: string;
|
|
21
|
+
/** Optional: License name or reference to bundled license file */
|
|
22
|
+
license?: string;
|
|
23
|
+
/** Optional: Max 500 chars, environment requirements */
|
|
24
|
+
compatibility?: string;
|
|
25
|
+
/** Optional: Arbitrary key-value mapping (includes author, version, etc.) */
|
|
26
|
+
metadata?: Record<string, string>;
|
|
27
|
+
/** Optional: Space-delimited list of pre-approved tools (experimental) */
|
|
28
|
+
allowedTools?: string;
|
|
29
|
+
}
|
|
30
|
+
export type SkillSourceType = 'shortcut' | 'github' | 'local';
|
|
31
|
+
/** Used at install time to determine how to fetch a skill */
|
|
32
|
+
export interface SkillSource {
|
|
33
|
+
type: SkillSourceType;
|
|
34
|
+
/** The resolved location (URL for github/shortcut, path for local) */
|
|
35
|
+
location: string;
|
|
36
|
+
/** Original input string from user (e.g., "tinker" or "github.com/user/skill") */
|
|
37
|
+
originalInput: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SkillInstallation {
|
|
40
|
+
agent: AgentType;
|
|
41
|
+
path: string;
|
|
42
|
+
isGlobal: boolean;
|
|
43
|
+
metadata: SkillMetadata;
|
|
44
|
+
contentHash: string;
|
|
45
|
+
}
|
|
46
|
+
export interface CommandFlags {
|
|
47
|
+
global?: boolean;
|
|
48
|
+
claude?: boolean;
|
|
49
|
+
codex?: boolean;
|
|
50
|
+
gemini?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface DetectedAgent {
|
|
53
|
+
agent: AgentConfig;
|
|
54
|
+
path: string;
|
|
55
|
+
isGlobal: boolean;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,SAAS,EAAE,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE9D,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface FuzzyMatch {
|
|
2
|
+
command: string;
|
|
3
|
+
score: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Find the closest matching command using fuzzy search.
|
|
7
|
+
* fuzzysort scores: 0 = perfect match, negative = worse match
|
|
8
|
+
*/
|
|
9
|
+
export declare function findClosestCommand(input: string): FuzzyMatch | null;
|
|
10
|
+
/**
|
|
11
|
+
* Suggest a command if the input is close enough to a valid command.
|
|
12
|
+
* Returns null if no good suggestion (avoids suggesting "add" for "xyz").
|
|
13
|
+
*/
|
|
14
|
+
export declare function suggestCommand(input: string): string | null;
|
|
15
|
+
export declare function getValidCommands(): string[];
|
|
16
|
+
//# sourceMappingURL=fuzzy-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy-match.d.ts","sourceRoot":"","sources":["../../src/utils/fuzzy-match.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAenE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS3D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAE3C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fuzzysort from 'fuzzysort';
|
|
2
|
+
const VALID_COMMANDS = ['add', 'remove', 'list', 'show', 'config'];
|
|
3
|
+
/**
|
|
4
|
+
* Find the closest matching command using fuzzy search.
|
|
5
|
+
* fuzzysort scores: 0 = perfect match, negative = worse match
|
|
6
|
+
*/
|
|
7
|
+
export function findClosestCommand(input) {
|
|
8
|
+
const results = fuzzysort.go(input, VALID_COMMANDS, {
|
|
9
|
+
// Allow any match (we'll filter by score later)
|
|
10
|
+
threshold: -Infinity
|
|
11
|
+
});
|
|
12
|
+
if (results.length === 0) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const best = results[0];
|
|
16
|
+
return {
|
|
17
|
+
command: best.target,
|
|
18
|
+
score: best.score
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Suggest a command if the input is close enough to a valid command.
|
|
23
|
+
* Returns null if no good suggestion (avoids suggesting "add" for "xyz").
|
|
24
|
+
*/
|
|
25
|
+
export function suggestCommand(input) {
|
|
26
|
+
const match = findClosestCommand(input);
|
|
27
|
+
// Only suggest if score is reasonable (0=perfect, -100 is still decent for typos)
|
|
28
|
+
// Examples: "ad" -> "add" (good), "addd" -> "add" (good), "xyz" -> null (no suggestion)
|
|
29
|
+
if (match && match.score > -100) {
|
|
30
|
+
return match.command;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export function getValidCommands() {
|
|
35
|
+
return [...VALID_COMMANDS];
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=fuzzy-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy-match.js","sourceRoot":"","sources":["../../src/utils/fuzzy-match.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAOnE;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE;QAClD,gDAAgD;QAChD,SAAS,EAAE,CAAC,QAAQ;KACrB,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAExC,kFAAkF;IAClF,wFAAwF;IACxF,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentType } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Show an interactive checkbox UI for selecting default agents.
|
|
4
|
+
*
|
|
5
|
+
* @param currentDefaults - Previously saved defaults (empty on first run)
|
|
6
|
+
* @returns Array of selected agent flags (e.g., ['claude', 'codex'])
|
|
7
|
+
*
|
|
8
|
+
* Behavior:
|
|
9
|
+
* - Shows all supported agent TYPES from SUPPORTED_AGENTS
|
|
10
|
+
* - First run (currentDefaults empty): ALL agents are pre-selected
|
|
11
|
+
* - Subsequent runs: Only previously saved defaults are pre-selected
|
|
12
|
+
* - Must select at least one agent
|
|
13
|
+
*/
|
|
14
|
+
export declare function promptAgentSelection(currentDefaults?: AgentType[]): Promise<AgentType[]>;
|
|
15
|
+
export declare function confirmAction(message: string): Promise<boolean>;
|
|
16
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,eAAe,GAAE,SAAS,EAAO,GAChC,OAAO,CAAC,SAAS,EAAE,CAAC,CA2BtB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKrE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { checkbox, confirm } from '@inquirer/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { SUPPORTED_AGENTS } from '../core/agents.js';
|
|
4
|
+
/**
|
|
5
|
+
* Show an interactive checkbox UI for selecting default agents.
|
|
6
|
+
*
|
|
7
|
+
* @param currentDefaults - Previously saved defaults (empty on first run)
|
|
8
|
+
* @returns Array of selected agent flags (e.g., ['claude', 'codex'])
|
|
9
|
+
*
|
|
10
|
+
* Behavior:
|
|
11
|
+
* - Shows all supported agent TYPES from SUPPORTED_AGENTS
|
|
12
|
+
* - First run (currentDefaults empty): ALL agents are pre-selected
|
|
13
|
+
* - Subsequent runs: Only previously saved defaults are pre-selected
|
|
14
|
+
* - Must select at least one agent
|
|
15
|
+
*/
|
|
16
|
+
export async function promptAgentSelection(currentDefaults = []) {
|
|
17
|
+
const isFirstRun = currentDefaults.length === 0;
|
|
18
|
+
console.log(chalk.gray('Use space to toggle, enter to confirm:\n'));
|
|
19
|
+
// Build choices from SUPPORTED_AGENTS constants
|
|
20
|
+
// Show paths so users understand what each agent refers to
|
|
21
|
+
const choices = SUPPORTED_AGENTS.map(agent => ({
|
|
22
|
+
name: `${agent.name} (~/${agent.folderName}/ and ./${agent.folderName}/)`,
|
|
23
|
+
value: agent.flag,
|
|
24
|
+
// First run: select ALL agents
|
|
25
|
+
// Otherwise: only select if it was in previous defaults
|
|
26
|
+
checked: isFirstRun ? true : currentDefaults.includes(agent.flag)
|
|
27
|
+
}));
|
|
28
|
+
const selectedAgents = await checkbox({
|
|
29
|
+
message: 'Select default agents:',
|
|
30
|
+
choices,
|
|
31
|
+
required: true,
|
|
32
|
+
theme: {
|
|
33
|
+
style: {
|
|
34
|
+
keysHelpTip: () => undefined
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return selectedAgents;
|
|
39
|
+
}
|
|
40
|
+
export async function confirmAction(message) {
|
|
41
|
+
return confirm({
|
|
42
|
+
message,
|
|
43
|
+
default: true
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,kBAA+B,EAAE;IAEjC,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,KAAK,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC,CAAC;IAEpE,gDAAgD;IAChD,2DAA2D;IAC3D,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,UAAU,WAAW,KAAK,CAAC,UAAU,IAAI;QACzE,KAAK,EAAE,KAAK,CAAC,IAAiB;QAC9B,+BAA+B;QAC/B,wDAAwD;QACxD,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAiB,CAAC;KAC/E,CAAC,CAAC,CAAC;IAEJ,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC;QACpC,OAAO,EAAE,wBAAwB;QACjC,OAAO;QACP,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,WAAW,EAAE,GAAG,EAAE,CAAC,SAAS;aAC7B;SACF;KACF,CAAC,CAAC;IAEH,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,OAAO,OAAO,CAAC;QACb,OAAO;QACP,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Skill shortcuts registry - maps short names to GitHub URLs */
|
|
2
|
+
export declare const SKILL_SHORTCUTS: Record<string, string>;
|
|
3
|
+
export declare function isShortcut(skill: string): boolean;
|
|
4
|
+
export declare function getShortcutUrl(skill: string): string | undefined;
|
|
5
|
+
export declare function listShortcuts(): string[];
|
|
6
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAElD,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEhE;AAED,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Skill shortcuts registry - maps short names to GitHub URLs */
|
|
2
|
+
export const SKILL_SHORTCUTS = {
|
|
3
|
+
tinker: 'https://github.com/sundial-org/skills/tree/main/skills/tinker'
|
|
4
|
+
};
|
|
5
|
+
export function isShortcut(skill) {
|
|
6
|
+
return skill in SKILL_SHORTCUTS;
|
|
7
|
+
}
|
|
8
|
+
export function getShortcutUrl(skill) {
|
|
9
|
+
return SKILL_SHORTCUTS[skill];
|
|
10
|
+
}
|
|
11
|
+
export function listShortcuts() {
|
|
12
|
+
return Object.keys(SKILL_SHORTCUTS);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,MAAM,EAAE,+DAA+D;CACxE,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK,IAAI,eAAe,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AACtC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sundial-ai/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Customize your agent's skills",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sun": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"test": "./test/run-all.sh"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cli",
|
|
17
|
+
"agents",
|
|
18
|
+
"skills",
|
|
19
|
+
"claude",
|
|
20
|
+
"codex",
|
|
21
|
+
"gemini"
|
|
22
|
+
],
|
|
23
|
+
"author": "Belinda Mo <belinda@sundialscientific.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@inquirer/prompts": "^8.2.0",
|
|
27
|
+
"chalk": "^5.3.0",
|
|
28
|
+
"commander": "^12.1.0",
|
|
29
|
+
"degit": "^2.8.4",
|
|
30
|
+
"fs-extra": "^11.2.0",
|
|
31
|
+
"fuzzysort": "^3.0.2",
|
|
32
|
+
"inquirer": "^9.3.7",
|
|
33
|
+
"ora": "^8.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/fs-extra": "^11.0.4",
|
|
37
|
+
"@types/inquirer": "^9.0.9",
|
|
38
|
+
"@types/node": "^22.10.2",
|
|
39
|
+
"tsx": "^4.19.2",
|
|
40
|
+
"typescript": "^5.7.2"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getAgentByFlag, SUPPORTED_AGENTS } from '../core/agents.js';
|
|
4
|
+
import { isFirstRun, getDefaultAgents, setDefaultAgents } from '../core/config-manager.js';
|
|
5
|
+
import { detectLocalAgents } from '../core/agent-detect.js';
|
|
6
|
+
import { installSkill } from '../core/skill-install.js';
|
|
7
|
+
import { promptAgentSelection } from '../utils/prompts.js';
|
|
8
|
+
import type { AgentType, CommandFlags } from '../types/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Determine which agents to install to and whether to install globally.
|
|
12
|
+
*
|
|
13
|
+
* Logic:
|
|
14
|
+
* 1. If --global flag is set, always install globally
|
|
15
|
+
* 2. If agent flags (--claude, --codex, etc.) are set, use those agents
|
|
16
|
+
* 3. If first run, detect agents and prompt for selection
|
|
17
|
+
* 4. Otherwise use saved default agents
|
|
18
|
+
*
|
|
19
|
+
* For local vs global:
|
|
20
|
+
* - If --global: always global
|
|
21
|
+
* - Otherwise: check if any configured agents have local folders
|
|
22
|
+
* - If yes: install locally to those
|
|
23
|
+
* - If no local folders exist for configured agents: install globally
|
|
24
|
+
*/
|
|
25
|
+
async function resolveTargetAgents(flags: CommandFlags): Promise<{ agents: AgentType[]; isGlobal: boolean }> {
|
|
26
|
+
const forceGlobal = flags.global ?? false;
|
|
27
|
+
|
|
28
|
+
// Check if any agent flags were explicitly set
|
|
29
|
+
const explicitAgents: AgentType[] = [];
|
|
30
|
+
for (const agent of SUPPORTED_AGENTS) {
|
|
31
|
+
if (flags[agent.flag as keyof CommandFlags]) {
|
|
32
|
+
explicitAgents.push(agent.flag as AgentType);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let targetAgents: AgentType[];
|
|
37
|
+
|
|
38
|
+
// Determine which agents to target
|
|
39
|
+
if (explicitAgents.length > 0) {
|
|
40
|
+
targetAgents = explicitAgents;
|
|
41
|
+
} else if (await isFirstRun()) {
|
|
42
|
+
// First run: show agent type selection dialog
|
|
43
|
+
const selectedAgents = await promptAgentSelection();
|
|
44
|
+
await setDefaultAgents(selectedAgents);
|
|
45
|
+
targetAgents = selectedAgents;
|
|
46
|
+
} else {
|
|
47
|
+
// Use saved defaults
|
|
48
|
+
const defaultAgents = await getDefaultAgents();
|
|
49
|
+
if (defaultAgents.length === 0) {
|
|
50
|
+
throw new Error('No default agents configured. Run "sun config" to set up your agents.');
|
|
51
|
+
}
|
|
52
|
+
targetAgents = defaultAgents;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Determine if we should install globally or locally
|
|
56
|
+
if (forceGlobal) {
|
|
57
|
+
return { agents: targetAgents, isGlobal: true };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if any of the target agents have local folders in current directory
|
|
61
|
+
const localAgents = await detectLocalAgents();
|
|
62
|
+
const localAgentFlags = new Set(localAgents.map(a => a.agent.flag));
|
|
63
|
+
|
|
64
|
+
const hasLocalFolders = targetAgents.some(agentFlag => localAgentFlags.has(agentFlag));
|
|
65
|
+
|
|
66
|
+
// If no local folders exist for any configured agent, install globally
|
|
67
|
+
const isGlobal = !hasLocalFolders;
|
|
68
|
+
|
|
69
|
+
return { agents: targetAgents, isGlobal };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface AddResult {
|
|
73
|
+
skill: string;
|
|
74
|
+
installedNames: string[];
|
|
75
|
+
agents: string[];
|
|
76
|
+
isGlobal: boolean;
|
|
77
|
+
error?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Add skill(s) to agent configuration(s).
|
|
82
|
+
*/
|
|
83
|
+
export async function addCommand(skills: string[], flags: CommandFlags): Promise<void> {
|
|
84
|
+
if (skills.length === 0) {
|
|
85
|
+
console.error(chalk.red('Error: No skills specified. Usage: sun add <skill> [skill2] ...'));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Resolve target agents
|
|
90
|
+
const { agents, isGlobal } = await resolveTargetAgents(flags);
|
|
91
|
+
|
|
92
|
+
const results: AddResult[] = [];
|
|
93
|
+
|
|
94
|
+
for (const skill of skills) {
|
|
95
|
+
const spinner = ora(`Adding ${skill}...`).start();
|
|
96
|
+
|
|
97
|
+
const result: AddResult = {
|
|
98
|
+
skill,
|
|
99
|
+
installedNames: [],
|
|
100
|
+
agents: [],
|
|
101
|
+
isGlobal
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Install to each target agent
|
|
106
|
+
for (const agentFlag of agents) {
|
|
107
|
+
const { skillNames } = await installSkill(skill, agentFlag, isGlobal);
|
|
108
|
+
result.installedNames = skillNames;
|
|
109
|
+
result.agents.push(getAgentByFlag(agentFlag)!.name);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
spinner.succeed(`Added ${result.installedNames.join(', ')}`);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
115
|
+
result.error = message;
|
|
116
|
+
spinner.fail(`Failed to add ${skill}: ${message}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
results.push(result);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Print summary
|
|
123
|
+
const successful = results.filter(r => !r.error);
|
|
124
|
+
if (successful.length > 0) {
|
|
125
|
+
const allSkills = [...new Set(successful.flatMap(r => r.installedNames))];
|
|
126
|
+
const agentFolders = [...new Set(successful.flatMap(r => r.agents))];
|
|
127
|
+
|
|
128
|
+
console.log();
|
|
129
|
+
const location = isGlobal ? '(global)' : '(local)';
|
|
130
|
+
console.log(chalk.green(`Added ${allSkills.join(', ')} to ${agentFolders.join(' and ')} ${chalk.gray(location)}`));
|
|
131
|
+
|
|
132
|
+
if (!isGlobal) {
|
|
133
|
+
console.log(chalk.gray('(use --global to install globally)'));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|