@sysprompthub/cli 1.0.0 → 1.1.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/README.md +3 -34
- package/dist/commands/init.js +72 -46
- package/dist/commands/init.test.js +13 -5
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Command-line interface for syncing system prompts from SysPromptHub to your projects.
|
|
4
4
|
|
|
5
|
-
For programmatic usage, see [@sysprompthub/sdk](
|
|
5
|
+
For programmatic usage, see [@sysprompthub/sdk](https://www.npmjs.com/package/@sysprompthub/sdk).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@ sysprompthub init
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
This interactive command will:
|
|
24
|
-
1. Prompt for your API key (
|
|
24
|
+
1. Prompt for your API key (find yours at https://app.sysprompthub.com/api-keys)
|
|
25
25
|
2. Let you search and select a prompt pack
|
|
26
26
|
3. Let you select your AI assistants (Copilot, Claude, or custom path)
|
|
27
27
|
4. Save workspace configuration to `.sysprompthub.json`
|
|
@@ -34,37 +34,6 @@ Sync prompts from your configured pack:
|
|
|
34
34
|
sysprompthub sync
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
Options:
|
|
38
|
-
- `-e, --environment <ENV>` - API environment to use (local, development, staging, production)
|
|
39
|
-
|
|
40
|
-
### Configuration
|
|
41
|
-
|
|
42
|
-
#### User-level Config (`~/.sysprompthub/config.json`)
|
|
43
|
-
Stores your API key:
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"apiKey": "your-40-character-api-key"
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
#### Workspace-level Config (`.sysprompthub.json`)
|
|
51
|
-
Stores project-specific settings:
|
|
52
|
-
```json
|
|
53
|
-
{
|
|
54
|
-
"packName": "owner/pack/latest",
|
|
55
|
-
"assistants": ["copilot", "claude"],
|
|
56
|
-
"environment": "production"
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
You can also use a custom path instead of assistants:
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"packName": "owner/pack/latest",
|
|
64
|
-
"path": "prompts/system-prompt.md"
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
37
|
## Workflow
|
|
69
38
|
|
|
70
39
|
1. **First time setup**: Run `sysprompthub init` to configure your API key and project
|
|
@@ -77,4 +46,4 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development instructions.
|
|
|
77
46
|
|
|
78
47
|
## License
|
|
79
48
|
|
|
80
|
-
|
|
49
|
+
See [LICENSE](./LICENSE)
|
package/dist/commands/init.js
CHANGED
|
@@ -21,7 +21,6 @@ export class InitCommand extends BaseCommand {
|
|
|
21
21
|
let workspaceConfig = { ...existingWorkspaceConfig };
|
|
22
22
|
if (environment)
|
|
23
23
|
workspaceConfig.environment = environment;
|
|
24
|
-
// console.info('env', environment, 'ws', workspaceConfig);
|
|
25
24
|
// Step 2: Pack Name (workspace-level)
|
|
26
25
|
const client = new SysPromptHubClient(apiKey, workspaceConfig.environment);
|
|
27
26
|
workspaceConfig.packName = await this.promptForPackName(client, workspaceConfig.packName);
|
|
@@ -35,7 +34,20 @@ export class InitCommand extends BaseCommand {
|
|
|
35
34
|
await this.workspaceConfigManager.update(this.fs, workspaceConfig);
|
|
36
35
|
console.log('\n✓ User config saved to ~/.sysprompthub/config.json');
|
|
37
36
|
console.log('✓ Project config saved to .sysprompthub.json');
|
|
38
|
-
|
|
37
|
+
// Ask if user wants to sync now
|
|
38
|
+
const shouldSync = await confirm({
|
|
39
|
+
message: 'Would you like to sync your prompts now?',
|
|
40
|
+
default: true
|
|
41
|
+
});
|
|
42
|
+
if (shouldSync) {
|
|
43
|
+
console.log('\nSyncing prompts...');
|
|
44
|
+
const { SyncCommand } = await import('./sync.js');
|
|
45
|
+
const syncCommand = new SyncCommand();
|
|
46
|
+
await syncCommand.run({});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log('Run "sysprompthub sync" to download your prompts.\n');
|
|
50
|
+
}
|
|
39
51
|
}
|
|
40
52
|
async promptForApiKey(existing) {
|
|
41
53
|
const apiKey = await input({
|
|
@@ -77,64 +89,78 @@ export class InitCommand extends BaseCommand {
|
|
|
77
89
|
return existing;
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
92
|
+
const packInfo = await this.searchAndSelectPack(client);
|
|
93
|
+
const version = await this.selectPackVersion(packInfo.name, packInfo.latestVersion);
|
|
94
|
+
return `${packInfo.name}/${version}`;
|
|
95
|
+
}
|
|
96
|
+
async searchAndSelectPack(client) {
|
|
97
|
+
const packResult = await search({
|
|
98
|
+
message: 'Type to search for packs...',
|
|
82
99
|
source: async (term) => {
|
|
83
|
-
if (!term || term.length <
|
|
100
|
+
if (!term || term.length < 3) {
|
|
84
101
|
return [];
|
|
85
102
|
}
|
|
86
103
|
try {
|
|
87
104
|
const results = await client.searchPacks(term);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
choices.push({
|
|
94
|
-
name: `${result.name}/latest`,
|
|
95
|
-
value: `${result.name}/latest`,
|
|
96
|
-
description: `${baseDescription} (always use latest)`
|
|
97
|
-
});
|
|
98
|
-
// Option 2: /current_version
|
|
99
|
-
choices.push({
|
|
100
|
-
name: `${result.name}/${result.version}`,
|
|
101
|
-
value: `${result.name}/${result.version}`,
|
|
102
|
-
description: `${baseDescription} (pin to current)`
|
|
103
|
-
});
|
|
104
|
-
// Option 3: Custom version
|
|
105
|
-
choices.push({
|
|
106
|
-
name: `${result.name}/[custom version]`,
|
|
107
|
-
value: `custom:${result.name}:${result.version}`,
|
|
108
|
-
description: `${baseDescription} (enter specific version)`
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return choices;
|
|
105
|
+
return results.map(result => ({
|
|
106
|
+
name: result.name,
|
|
107
|
+
value: JSON.stringify({ name: result.name, latestVersion: result.version }),
|
|
108
|
+
description: `Current version: ${result.version}`
|
|
109
|
+
}));
|
|
112
110
|
}
|
|
113
111
|
catch (error) {
|
|
114
|
-
console.error('Error searching for packs:', error);
|
|
115
112
|
return [];
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
116
|
+
return JSON.parse(packResult);
|
|
117
|
+
}
|
|
118
|
+
async selectPackVersion(packName, latestVersion) {
|
|
119
|
+
const versionChoice = await search({
|
|
120
|
+
message: `Select version for ${packName}:`,
|
|
121
|
+
source: async () => {
|
|
122
|
+
const choices = [
|
|
123
|
+
{
|
|
124
|
+
name: 'latest',
|
|
125
|
+
value: 'latest',
|
|
126
|
+
description: 'Always use the latest version'
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: `current (v${latestVersion})`,
|
|
130
|
+
value: String(latestVersion),
|
|
131
|
+
description: 'Use current version and manually update'
|
|
131
132
|
}
|
|
132
|
-
|
|
133
|
+
];
|
|
134
|
+
if (latestVersion > 1) {
|
|
135
|
+
choices.push({
|
|
136
|
+
name: '(other)',
|
|
137
|
+
value: 'custom',
|
|
138
|
+
description: 'Choose an older version'
|
|
139
|
+
});
|
|
133
140
|
}
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
return choices;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
if (versionChoice === 'custom') {
|
|
145
|
+
return await this.promptForCustomVersion(packName, latestVersion);
|
|
136
146
|
}
|
|
137
|
-
return
|
|
147
|
+
return versionChoice === 'latest' ? 'latest' : Number(versionChoice);
|
|
148
|
+
}
|
|
149
|
+
async promptForCustomVersion(packName, latestVersion) {
|
|
150
|
+
const customVersion = await input({
|
|
151
|
+
message: `Enter version number for ${packName} (1-${latestVersion}):`,
|
|
152
|
+
default: String(latestVersion),
|
|
153
|
+
validate: (value) => {
|
|
154
|
+
if (!value) {
|
|
155
|
+
return 'Version is required';
|
|
156
|
+
}
|
|
157
|
+
if (!SysPromptHubClient.validateVersion(value, latestVersion)) {
|
|
158
|
+
return `Version must be between 1 and ${latestVersion}`;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return Number(customVersion);
|
|
138
164
|
}
|
|
139
165
|
async promptForAssistants(existing) {
|
|
140
166
|
const am = new AssistantManager();
|
|
@@ -114,11 +114,13 @@ describe('InitCommand', () => {
|
|
|
114
114
|
describe('run', () => {
|
|
115
115
|
it('should prompt for API key, pack name, and assistants', async () => {
|
|
116
116
|
promptMocks.input.mockResolvedValue('a'.repeat(40));
|
|
117
|
-
promptMocks.search
|
|
117
|
+
promptMocks.search
|
|
118
|
+
.mockResolvedValueOnce(JSON.stringify({ name: 'owner/pack', version: 5 })) // Pack search
|
|
119
|
+
.mockResolvedValueOnce('latest'); // Version selection
|
|
118
120
|
promptMocks.checkbox.mockResolvedValue(['copilot']);
|
|
119
121
|
await initCommand.run({});
|
|
120
122
|
expect(promptMocks.input).toHaveBeenCalled();
|
|
121
|
-
expect(promptMocks.search).
|
|
123
|
+
expect(promptMocks.search).toHaveBeenCalledTimes(2);
|
|
122
124
|
expect(promptMocks.checkbox).toHaveBeenCalled();
|
|
123
125
|
expect(mocks.mockUserConfig.save).toHaveBeenCalledWith(expect.any(Object), { apiKey: 'a'.repeat(40) });
|
|
124
126
|
expect(mocks.mockWorkspaceConfig.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
|
|
@@ -129,7 +131,9 @@ describe('InitCommand', () => {
|
|
|
129
131
|
it('should keep existing API key if blank entered', async () => {
|
|
130
132
|
mocks.mockUserConfig.load.mockResolvedValue({ apiKey: 'existing'.padEnd(40, 'x') });
|
|
131
133
|
promptMocks.input.mockResolvedValueOnce(''); // API key blank
|
|
132
|
-
promptMocks.search
|
|
134
|
+
promptMocks.search
|
|
135
|
+
.mockResolvedValueOnce(JSON.stringify({ name: 'owner/pack', version: 5 })) // Pack search
|
|
136
|
+
.mockResolvedValueOnce('latest'); // Version selection
|
|
133
137
|
promptMocks.checkbox.mockResolvedValue(['copilot']);
|
|
134
138
|
await initCommand.run({});
|
|
135
139
|
expect(mocks.mockUserConfig.save).toHaveBeenCalledWith(expect.any(Object), { apiKey: 'existing'.padEnd(40, 'x') });
|
|
@@ -137,7 +141,9 @@ describe('InitCommand', () => {
|
|
|
137
141
|
it('should prompt for custom path when no assistants selected', async () => {
|
|
138
142
|
promptMocks.input.mockResolvedValueOnce('a'.repeat(40)); // API key
|
|
139
143
|
promptMocks.input.mockResolvedValueOnce('custom/path'); // custom path
|
|
140
|
-
promptMocks.search
|
|
144
|
+
promptMocks.search
|
|
145
|
+
.mockResolvedValueOnce(JSON.stringify({ name: 'owner/pack', version: 5 })) // Pack search
|
|
146
|
+
.mockResolvedValueOnce('latest'); // Version selection
|
|
141
147
|
promptMocks.checkbox.mockResolvedValue([]);
|
|
142
148
|
await initCommand.run({});
|
|
143
149
|
expect(mocks.mockWorkspaceConfig.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
|
|
@@ -147,7 +153,9 @@ describe('InitCommand', () => {
|
|
|
147
153
|
it('should not set path when blank entered for custom path', async () => {
|
|
148
154
|
promptMocks.input.mockResolvedValueOnce('a'.repeat(40)); // API key
|
|
149
155
|
promptMocks.input.mockResolvedValueOnce(''); // blank custom path
|
|
150
|
-
promptMocks.search
|
|
156
|
+
promptMocks.search
|
|
157
|
+
.mockResolvedValueOnce(JSON.stringify({ name: 'owner/pack', version: 5 })) // Pack search
|
|
158
|
+
.mockResolvedValueOnce('latest'); // Version selection
|
|
151
159
|
promptMocks.checkbox.mockResolvedValue([]);
|
|
152
160
|
await initCommand.run({});
|
|
153
161
|
expect(mocks.mockWorkspaceConfig.update).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sysprompthub/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI for syncing system prompts from SysPromptHub",
|
|
5
5
|
"authorEmail": "info@sysprompthub.com",
|
|
6
6
|
"authorName": "SysPromptHub",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
10
|
-
"sysprompthub": "dist/index.js"
|
|
11
|
-
"sysprompthub-cli": "dist/index.js"
|
|
10
|
+
"sysprompthub": "dist/index.js"
|
|
12
11
|
},
|
|
13
12
|
"files": [
|
|
14
13
|
"dist",
|
|
@@ -31,7 +30,7 @@
|
|
|
31
30
|
},
|
|
32
31
|
"dependencies": {
|
|
33
32
|
"@inquirer/prompts": "^8.1.0",
|
|
34
|
-
"@sysprompthub/sdk": "1.
|
|
33
|
+
"@sysprompthub/sdk": "^1.1.0",
|
|
35
34
|
"commander": "^14.0.2",
|
|
36
35
|
"is-valid-path": "^0.1.1",
|
|
37
36
|
"open": "^11.0.0"
|