@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.
Files changed (97) hide show
  1. package/PLAN.md +497 -0
  2. package/README.md +30 -0
  3. package/dist/commands/add.d.ts +13 -0
  4. package/dist/commands/add.d.ts.map +1 -0
  5. package/dist/commands/add.js +112 -0
  6. package/dist/commands/add.js.map +1 -0
  7. package/dist/commands/config.d.ts +5 -0
  8. package/dist/commands/config.d.ts.map +1 -0
  9. package/dist/commands/config.js +42 -0
  10. package/dist/commands/config.js.map +1 -0
  11. package/dist/commands/list.d.ts +5 -0
  12. package/dist/commands/list.d.ts.map +1 -0
  13. package/dist/commands/list.js +53 -0
  14. package/dist/commands/list.js.map +1 -0
  15. package/dist/commands/remove.d.ts +13 -0
  16. package/dist/commands/remove.d.ts.map +1 -0
  17. package/dist/commands/remove.js +127 -0
  18. package/dist/commands/remove.js.map +1 -0
  19. package/dist/commands/show.d.ts +5 -0
  20. package/dist/commands/show.d.ts.map +1 -0
  21. package/dist/commands/show.js +81 -0
  22. package/dist/commands/show.js.map +1 -0
  23. package/dist/core/agent-detect.d.ts +9 -0
  24. package/dist/core/agent-detect.d.ts.map +1 -0
  25. package/dist/core/agent-detect.js +44 -0
  26. package/dist/core/agent-detect.js.map +1 -0
  27. package/dist/core/agents.d.ts +8 -0
  28. package/dist/core/agents.d.ts.map +1 -0
  29. package/dist/core/agents.js +34 -0
  30. package/dist/core/agents.js.map +1 -0
  31. package/dist/core/config-manager.d.ts +9 -0
  32. package/dist/core/config-manager.d.ts.map +1 -0
  33. package/dist/core/config-manager.js +47 -0
  34. package/dist/core/config-manager.js.map +1 -0
  35. package/dist/core/skill-hash.d.ts +12 -0
  36. package/dist/core/skill-hash.d.ts.map +1 -0
  37. package/dist/core/skill-hash.js +53 -0
  38. package/dist/core/skill-hash.js.map +1 -0
  39. package/dist/core/skill-info.d.ts +35 -0
  40. package/dist/core/skill-info.d.ts.map +1 -0
  41. package/dist/core/skill-info.js +211 -0
  42. package/dist/core/skill-info.js.map +1 -0
  43. package/dist/core/skill-install.d.ts +24 -0
  44. package/dist/core/skill-install.d.ts.map +1 -0
  45. package/dist/core/skill-install.js +123 -0
  46. package/dist/core/skill-install.js.map +1 -0
  47. package/dist/core/skill-source.d.ts +29 -0
  48. package/dist/core/skill-source.d.ts.map +1 -0
  49. package/dist/core/skill-source.js +105 -0
  50. package/dist/core/skill-source.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +104 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/types/index.d.ts +57 -0
  56. package/dist/types/index.d.ts.map +1 -0
  57. package/dist/types/index.js +2 -0
  58. package/dist/types/index.js.map +1 -0
  59. package/dist/utils/fuzzy-match.d.ts +16 -0
  60. package/dist/utils/fuzzy-match.d.ts.map +1 -0
  61. package/dist/utils/fuzzy-match.js +37 -0
  62. package/dist/utils/fuzzy-match.js.map +1 -0
  63. package/dist/utils/prompts.d.ts +16 -0
  64. package/dist/utils/prompts.d.ts.map +1 -0
  65. package/dist/utils/prompts.js +80 -0
  66. package/dist/utils/prompts.js.map +1 -0
  67. package/dist/utils/registry.d.ts +6 -0
  68. package/dist/utils/registry.d.ts.map +1 -0
  69. package/dist/utils/registry.js +14 -0
  70. package/dist/utils/registry.js.map +1 -0
  71. package/package.json +43 -0
  72. package/publish.sh +2 -0
  73. package/src/commands/add.ts +137 -0
  74. package/src/commands/config.ts +49 -0
  75. package/src/commands/list.ts +68 -0
  76. package/src/commands/remove.ts +152 -0
  77. package/src/commands/show.ts +93 -0
  78. package/src/core/agent-detect.ts +53 -0
  79. package/src/core/agents.ts +40 -0
  80. package/src/core/config-manager.ts +55 -0
  81. package/src/core/skill-hash.ts +61 -0
  82. package/src/core/skill-info.ts +246 -0
  83. package/src/core/skill-install.ts +165 -0
  84. package/src/core/skill-source.ts +118 -0
  85. package/src/index.ts +116 -0
  86. package/src/types/index.ts +64 -0
  87. package/src/utils/fuzzy-match.ts +48 -0
  88. package/src/utils/prompts.ts +92 -0
  89. package/src/utils/registry.ts +16 -0
  90. package/test/agents.test.ts +86 -0
  91. package/test/fuzzy-match.test.ts +58 -0
  92. package/test/registry.test.ts +48 -0
  93. package/test/skill-hash.test.ts +77 -0
  94. package/test/skill-info.test.ts +195 -0
  95. package/test/skill-source.test.ts +89 -0
  96. package/tsconfig.json +20 -0
  97. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ npm config set //registry.npmjs.org/:_authToken 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
2
+ npm publish --access public
@@ -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
+ }