@sundial-ai/cli 0.1.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/PLAN.md +497 -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 +112 -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 +42 -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 +127 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/show.d.ts +5 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +81 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/core/agent-detect.d.ts +9 -0
- package/dist/core/agent-detect.d.ts.map +1 -0
- package/dist/core/agent-detect.js +44 -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 +35 -0
- package/dist/core/skill-info.d.ts.map +1 -0
- package/dist/core/skill-info.js +211 -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 +105 -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 +80 -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 +43 -0
- package/publish.sh +2 -0
- package/src/commands/add.ts +137 -0
- package/src/commands/config.ts +49 -0
- package/src/commands/list.ts +68 -0
- package/src/commands/remove.ts +152 -0
- package/src/commands/show.ts +93 -0
- package/src/core/agent-detect.ts +53 -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 +246 -0
- package/src/core/skill-install.ts +165 -0
- package/src/core/skill-source.ts +118 -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 +92 -0
- package/src/utils/registry.ts +16 -0
- package/test/agents.test.ts +86 -0
- package/test/fuzzy-match.test.ts +58 -0
- package/test/registry.test.ts +48 -0
- package/test/skill-hash.test.ts +77 -0
- package/test/skill-info.test.ts +195 -0
- package/test/skill-source.test.ts +89 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +15 -0
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 skill details and installation locations')
|
|
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, DetectedAgent } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Show an interactive checkbox UI for selecting default agents.
|
|
4
|
+
*
|
|
5
|
+
* @param detectedAgents - Agents found in local/global directories
|
|
6
|
+
* @param currentDefaults - Previously saved defaults (empty on first run)
|
|
7
|
+
* @returns Array of selected agent flags (e.g., ['claude', 'codex'])
|
|
8
|
+
*
|
|
9
|
+
* Behavior:
|
|
10
|
+
* - First run (currentDefaults empty): ALL detected agents are pre-selected
|
|
11
|
+
* - Subsequent runs: Only previously saved defaults are pre-selected
|
|
12
|
+
* - If no agents detected: Show all supported agents (none pre-selected)
|
|
13
|
+
*/
|
|
14
|
+
export declare function promptAgentSelection(detectedAgents: DetectedAgent[], 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,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElE;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,cAAc,EAAE,aAAa,EAAE,EAC/B,eAAe,GAAE,SAAS,EAAO,GAChC,OAAO,CAAC,SAAS,EAAE,CAAC,CA2DtB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUrE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { SUPPORTED_AGENTS, getSupportedAgentsMessage } from '../core/agents.js';
|
|
4
|
+
/**
|
|
5
|
+
* Show an interactive checkbox UI for selecting default agents.
|
|
6
|
+
*
|
|
7
|
+
* @param detectedAgents - Agents found in local/global directories
|
|
8
|
+
* @param currentDefaults - Previously saved defaults (empty on first run)
|
|
9
|
+
* @returns Array of selected agent flags (e.g., ['claude', 'codex'])
|
|
10
|
+
*
|
|
11
|
+
* Behavior:
|
|
12
|
+
* - First run (currentDefaults empty): ALL detected agents are pre-selected
|
|
13
|
+
* - Subsequent runs: Only previously saved defaults are pre-selected
|
|
14
|
+
* - If no agents detected: Show all supported agents (none pre-selected)
|
|
15
|
+
*/
|
|
16
|
+
export async function promptAgentSelection(detectedAgents, currentDefaults = []) {
|
|
17
|
+
const isFirstRun = currentDefaults.length === 0;
|
|
18
|
+
// Show what we detected
|
|
19
|
+
if (detectedAgents.length > 0) {
|
|
20
|
+
console.log(chalk.cyan(`\nWe detected ${detectedAgents.length} agent folders installed on your machine:`));
|
|
21
|
+
for (const detected of detectedAgents) {
|
|
22
|
+
const location = detected.isGlobal ? '~/' : './';
|
|
23
|
+
console.log(chalk.white(` • ${detected.agent.name} ${chalk.gray(`(${location}${detected.agent.folderName})`)}`));
|
|
24
|
+
}
|
|
25
|
+
console.log();
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk.gray(getSupportedAgentsMessage()));
|
|
28
|
+
console.log(chalk.gray('Select which agents you want to add skills to by default:\n'));
|
|
29
|
+
// Build choices from detected agents
|
|
30
|
+
const choices = detectedAgents.map(detected => {
|
|
31
|
+
const location = detected.isGlobal ? '(global)' : '(local)';
|
|
32
|
+
const agentFlag = detected.agent.flag;
|
|
33
|
+
return {
|
|
34
|
+
name: `${detected.agent.name} ${chalk.gray(location)}`,
|
|
35
|
+
value: agentFlag,
|
|
36
|
+
// First run: select ALL detected agents
|
|
37
|
+
// Otherwise: only select if it was in previous defaults
|
|
38
|
+
checked: isFirstRun ? true : currentDefaults.includes(agentFlag)
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
// If no agents detected, show all supported agents as options
|
|
42
|
+
if (choices.length === 0) {
|
|
43
|
+
console.log(chalk.yellow('No agent folders detected on your machine.'));
|
|
44
|
+
console.log(chalk.gray('Showing all supported agents - select which ones you plan to use:\n'));
|
|
45
|
+
for (const agent of SUPPORTED_AGENTS) {
|
|
46
|
+
choices.push({
|
|
47
|
+
name: agent.name,
|
|
48
|
+
value: agent.flag,
|
|
49
|
+
checked: false // None pre-selected since we don't know which they'll install
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const { selectedAgents } = await inquirer.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: 'checkbox',
|
|
56
|
+
name: 'selectedAgents',
|
|
57
|
+
message: 'Select default agents:',
|
|
58
|
+
choices,
|
|
59
|
+
validate: (answer) => {
|
|
60
|
+
if (answer.length === 0) {
|
|
61
|
+
return 'You must select at least one agent.';
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]);
|
|
67
|
+
return selectedAgents;
|
|
68
|
+
}
|
|
69
|
+
export async function confirmAction(message) {
|
|
70
|
+
const { confirmed } = await inquirer.prompt([
|
|
71
|
+
{
|
|
72
|
+
type: 'confirm',
|
|
73
|
+
name: 'confirmed',
|
|
74
|
+
message,
|
|
75
|
+
default: true
|
|
76
|
+
}
|
|
77
|
+
]);
|
|
78
|
+
return confirmed;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sundial-ai/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
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": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cli",
|
|
18
|
+
"agents",
|
|
19
|
+
"skills",
|
|
20
|
+
"claude",
|
|
21
|
+
"codex",
|
|
22
|
+
"gemini"
|
|
23
|
+
],
|
|
24
|
+
"author": "Belinda Mo <belinda@sundialscientific.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
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
|
+
"vitest": "^2.1.8"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/publish.sh
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
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 { detectAllAgents, 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: detect agents and show selection dialog
|
|
43
|
+
const detectedAgents = await detectAllAgents();
|
|
44
|
+
const selectedAgents = await promptAgentSelection(detectedAgents);
|
|
45
|
+
await setDefaultAgents(selectedAgents);
|
|
46
|
+
targetAgents = selectedAgents;
|
|
47
|
+
} else {
|
|
48
|
+
// Use saved defaults
|
|
49
|
+
const defaultAgents = await getDefaultAgents();
|
|
50
|
+
if (defaultAgents.length === 0) {
|
|
51
|
+
throw new Error('No default agents configured. Run "sun config" to set up your agents.');
|
|
52
|
+
}
|
|
53
|
+
targetAgents = defaultAgents;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Determine if we should install globally or locally
|
|
57
|
+
if (forceGlobal) {
|
|
58
|
+
return { agents: targetAgents, isGlobal: true };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if any of the target agents have local folders in current directory
|
|
62
|
+
const localAgents = await detectLocalAgents();
|
|
63
|
+
const localAgentFlags = new Set(localAgents.map(a => a.agent.flag));
|
|
64
|
+
|
|
65
|
+
const hasLocalFolders = targetAgents.some(agentFlag => localAgentFlags.has(agentFlag));
|
|
66
|
+
|
|
67
|
+
// If no local folders exist for any configured agent, install globally
|
|
68
|
+
const isGlobal = !hasLocalFolders;
|
|
69
|
+
|
|
70
|
+
return { agents: targetAgents, isGlobal };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface AddResult {
|
|
74
|
+
skill: string;
|
|
75
|
+
installedNames: string[];
|
|
76
|
+
agents: string[];
|
|
77
|
+
isGlobal: boolean;
|
|
78
|
+
error?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Add skill(s) to agent configuration(s).
|
|
83
|
+
*/
|
|
84
|
+
export async function addCommand(skills: string[], flags: CommandFlags): Promise<void> {
|
|
85
|
+
if (skills.length === 0) {
|
|
86
|
+
console.error(chalk.red('Error: No skills specified. Usage: sun add <skill> [skill2] ...'));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Resolve target agents
|
|
91
|
+
const { agents, isGlobal } = await resolveTargetAgents(flags);
|
|
92
|
+
|
|
93
|
+
const results: AddResult[] = [];
|
|
94
|
+
|
|
95
|
+
for (const skill of skills) {
|
|
96
|
+
const spinner = ora(`Adding ${skill}...`).start();
|
|
97
|
+
|
|
98
|
+
const result: AddResult = {
|
|
99
|
+
skill,
|
|
100
|
+
installedNames: [],
|
|
101
|
+
agents: [],
|
|
102
|
+
isGlobal
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Install to each target agent
|
|
107
|
+
for (const agentFlag of agents) {
|
|
108
|
+
const { skillNames } = await installSkill(skill, agentFlag, isGlobal);
|
|
109
|
+
result.installedNames = skillNames;
|
|
110
|
+
result.agents.push(getAgentByFlag(agentFlag)!.name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
spinner.succeed(`Added ${result.installedNames.join(', ')}`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
116
|
+
result.error = message;
|
|
117
|
+
spinner.fail(`Failed to add ${skill}: ${message}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
results.push(result);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Print summary
|
|
124
|
+
const successful = results.filter(r => !r.error);
|
|
125
|
+
if (successful.length > 0) {
|
|
126
|
+
const allSkills = [...new Set(successful.flatMap(r => r.installedNames))];
|
|
127
|
+
const agentFolders = [...new Set(successful.flatMap(r => r.agents))];
|
|
128
|
+
|
|
129
|
+
console.log();
|
|
130
|
+
const location = isGlobal ? '(global)' : '(local)';
|
|
131
|
+
console.log(chalk.green(`Added ${allSkills.join(', ')} to ${agentFolders.join(' and ')} ${chalk.gray(location)}`));
|
|
132
|
+
|
|
133
|
+
if (!isGlobal) {
|
|
134
|
+
console.log(chalk.gray('(use --global to install globally)'));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getSupportedAgentsMessage, SUPPORTED_AGENTS } from '../core/agents.js';
|
|
3
|
+
import { loadConfig, setDefaultAgents, getConfigPath } from '../core/config-manager.js';
|
|
4
|
+
import { detectAllAgents } from '../core/agent-detect.js';
|
|
5
|
+
import { promptAgentSelection } from '../utils/prompts.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Re-open agent selection dialog and update config.
|
|
9
|
+
*/
|
|
10
|
+
export async function configCommand(): Promise<void> {
|
|
11
|
+
const config = await loadConfig();
|
|
12
|
+
|
|
13
|
+
console.log(chalk.cyan('Sundial CLI Configuration'));
|
|
14
|
+
console.log(chalk.gray(`Config file: ${getConfigPath()}`));
|
|
15
|
+
console.log();
|
|
16
|
+
|
|
17
|
+
// Show current defaults
|
|
18
|
+
if (config.defaultAgents.length > 0) {
|
|
19
|
+
console.log('Current default agents:');
|
|
20
|
+
for (const agentFlag of config.defaultAgents) {
|
|
21
|
+
const agent = SUPPORTED_AGENTS.find(a => a.flag === agentFlag);
|
|
22
|
+
if (agent) {
|
|
23
|
+
console.log(` - ${agent.name}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
console.log();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(chalk.gray(getSupportedAgentsMessage()));
|
|
30
|
+
console.log();
|
|
31
|
+
|
|
32
|
+
// Detect agents and show selection dialog
|
|
33
|
+
const detectedAgents = await detectAllAgents();
|
|
34
|
+
const selectedAgents = await promptAgentSelection(detectedAgents, config.defaultAgents);
|
|
35
|
+
|
|
36
|
+
await setDefaultAgents(selectedAgents);
|
|
37
|
+
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(chalk.green('Configuration saved!'));
|
|
40
|
+
|
|
41
|
+
// Show new defaults
|
|
42
|
+
console.log('New default agents:');
|
|
43
|
+
for (const agentFlag of selectedAgents) {
|
|
44
|
+
const agent = SUPPORTED_AGENTS.find(a => a.flag === agentFlag);
|
|
45
|
+
if (agent) {
|
|
46
|
+
console.log(` - ${agent.name}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|