kitstore-cli 1.0.0 → 1.0.2

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 (49) hide show
  1. package/default-config.json +1 -0
  2. package/dist/config.js +20 -4
  3. package/package.json +5 -1
  4. package/.env.test +0 -4
  5. package/.eslintrc.js +0 -29
  6. package/e2e/install.e2e.test.ts +0 -237
  7. package/e2e/integration.e2e.test.ts +0 -346
  8. package/e2e/login.e2e.test.ts +0 -188
  9. package/jest.config.js +0 -24
  10. package/openapitools.json +0 -7
  11. package/src/__tests__/commands/init.test.ts +0 -52
  12. package/src/__tests__/commands/install.test.ts +0 -449
  13. package/src/__tests__/commands/list.test.ts +0 -164
  14. package/src/__tests__/commands/login.test.ts +0 -293
  15. package/src/__tests__/commands/rule-check.test.ts +0 -52
  16. package/src/__tests__/commands/search.test.ts +0 -168
  17. package/src/__tests__/commands/upload.test.ts +0 -404
  18. package/src/__tests__/config.test.ts +0 -181
  19. package/src/__tests__/setup.ts +0 -11
  20. package/src/api/client.ts +0 -20
  21. package/src/api/generated/.openapi-generator/FILES +0 -17
  22. package/src/api/generated/.openapi-generator/VERSION +0 -1
  23. package/src/api/generated/.openapi-generator-ignore +0 -23
  24. package/src/api/generated/api.ts +0 -1171
  25. package/src/api/generated/base.ts +0 -62
  26. package/src/api/generated/common.ts +0 -113
  27. package/src/api/generated/configuration.ts +0 -121
  28. package/src/api/generated/docs/AuthApi.md +0 -158
  29. package/src/api/generated/docs/AuthResponseDto.md +0 -22
  30. package/src/api/generated/docs/AuthUserDto.md +0 -24
  31. package/src/api/generated/docs/HealthApi.md +0 -183
  32. package/src/api/generated/docs/LoginDto.md +0 -22
  33. package/src/api/generated/docs/RegisterDto.md +0 -24
  34. package/src/api/generated/docs/RuleAuthorDto.md +0 -22
  35. package/src/api/generated/docs/RuleResponseDto.md +0 -36
  36. package/src/api/generated/docs/RulesApi.md +0 -289
  37. package/src/api/generated/git_push.sh +0 -57
  38. package/src/api/generated/index.ts +0 -18
  39. package/src/commands/init.ts +0 -46
  40. package/src/commands/install.ts +0 -129
  41. package/src/commands/list.ts +0 -71
  42. package/src/commands/login.ts +0 -65
  43. package/src/commands/rule-check.ts +0 -49
  44. package/src/commands/search.ts +0 -66
  45. package/src/commands/upload.ts +0 -117
  46. package/src/config.ts +0 -66
  47. package/src/index.ts +0 -79
  48. package/test-cli-config.js +0 -118
  49. package/tsconfig.json +0 -24
@@ -1,65 +0,0 @@
1
- import inquirer from 'inquirer';
2
- import axios from 'axios';
3
- import { getConfig, saveConfig } from '../config';
4
- import { createApi } from '../api/client';
5
-
6
- /**
7
- * @requirement TC-UNIT-CLI-001, TC-UNIT-CLI-002, TC-E2E-CLI-001
8
- */
9
- export async function loginCommand(options: {
10
- email?: string;
11
- password?: string;
12
- server?: string;
13
- }) {
14
- try {
15
- const config = await getConfig();
16
- const server = options.server || config.server;
17
-
18
- let email = options.email;
19
- let password = options.password;
20
-
21
- if (!email) {
22
- const answer = await inquirer.prompt([
23
- { type: 'input', name: 'email', message: 'Email:' }
24
- ]);
25
- email = answer.email;
26
- }
27
-
28
- if (!password) {
29
- const answer = await inquirer.prompt([
30
- { type: 'password', name: 'password', message: 'Password:' }
31
- ]);
32
- password = answer.password;
33
- }
34
-
35
- const { authApi } = await createApi({ server });
36
- if (!email || !password) {
37
- throw new Error('Email and password are required');
38
- }
39
- const response = await authApi.authControllerLogin({ email, password });
40
- const data = response.data;
41
-
42
- if ((data as any).token) {
43
- await saveConfig({
44
- token: (data as any).token,
45
- server,
46
- user: (data as any).user,
47
- lastLogin: new Date().toISOString(),
48
- });
49
-
50
- console.log(`✅ Logged in as ${(data as any).user.username}`);
51
- } else {
52
- console.error('❌ Login failed');
53
- process.exit(1);
54
- }
55
- } catch (err: unknown) {
56
- if (axios.isAxiosError(err)) {
57
- console.error(`❌ Login failed: ${err.response?.data?.error || err.message}`);
58
- } else if (err instanceof Error) {
59
- console.error(`❌ Login failed: ${err.message}`);
60
- } else {
61
- console.error(`❌ Login failed: ${err}`);
62
- }
63
- process.exit(1);
64
- }
65
- }
@@ -1,49 +0,0 @@
1
- import * as fs from 'fs-extra';
2
- import chalk from 'chalk';
3
-
4
- /**
5
- * @requirement TC-UNIT-CLI-CHECK-001
6
- */
7
- export async function ruleCheckCommand(filePath: string) {
8
- try {
9
- if (!(await fs.pathExists(filePath))) {
10
- console.error(chalk.red(`❌ File not found: ${filePath}`));
11
- process.exit(1);
12
- }
13
-
14
- const content = await fs.readFile(filePath, 'utf-8');
15
- const lines = content.split('\n');
16
-
17
- let errors = 0;
18
- let warnings = 0;
19
-
20
- console.log(chalk.blue(`Checking rule: ${filePath}...`));
21
-
22
- // Basic checks
23
- if (content.length === 0) {
24
- console.error(chalk.red(' - Error: File is empty'));
25
- errors++;
26
- }
27
-
28
- if (!content.includes('@requirement')) {
29
- console.warn(chalk.yellow(' - Warning: Missing @requirement tag'));
30
- warnings++;
31
- }
32
-
33
- if (lines.length > 500) {
34
- console.warn(chalk.yellow(' - Warning: File is very long (>500 lines)'));
35
- warnings++;
36
- }
37
-
38
- if (errors === 0 && warnings === 0) {
39
- console.log(chalk.green('✅ Rule validation passed!'));
40
- } else {
41
- console.log('');
42
- console.log(`Validation finished with ${chalk.red(errors + ' errors')} and ${chalk.yellow(warnings + ' warnings')}.`);
43
- }
44
- } catch (err: any) {
45
- console.error(chalk.red(`❌ Check failed: ${err.message}`));
46
- process.exit(1);
47
- }
48
- }
49
-
@@ -1,66 +0,0 @@
1
- import { getConfig } from '../config';
2
- import { createApi } from '../api/client';
3
-
4
- export async function searchCommand(
5
- query: string,
6
- options: {
7
- type?: string;
8
- tag?: string;
9
- limit?: string;
10
- }
11
- ) {
12
- try {
13
- const config = await getConfig();
14
-
15
- if (!config.token) {
16
- console.error('❌ Not logged in. Please run `kitstore login` first.');
17
- process.exit(1);
18
- }
19
-
20
- if (!query || query.trim() === '') {
21
- console.error('❌ Search query is required.');
22
- process.exit(1);
23
- }
24
-
25
- const { rulesApi } = await createApi({ server: config.server });
26
-
27
- const queryParams: any = {
28
- name: query, // Search by name using the name filter
29
- };
30
- if (options.type) queryParams.type = options.type;
31
- if (options.tag) queryParams.tag = options.tag;
32
- if (options.limit) queryParams.limit = parseInt(options.limit);
33
-
34
- const response = await rulesApi.rulesControllerFindAll(queryParams);
35
-
36
- if (response.data && response.data.length > 0) {
37
- console.log(`🔍 Search results for "${query}":`);
38
- console.log(`📋 Found ${response.data.length} matching rules/commands:`);
39
- console.log('');
40
-
41
- response.data.forEach((rule: any) => {
42
- console.log(`📄 ${rule.name}`);
43
- console.log(` ID: ${rule.id}`);
44
- console.log(` Tag: ${rule.tag}`);
45
- console.log(` Type: ${rule.type}`);
46
- console.log(` Version: ${rule.version}`);
47
- console.log(` Author: ${rule.author.username}`);
48
- if (rule.description) {
49
- console.log(` Description: ${rule.description}`);
50
- }
51
- console.log('');
52
- });
53
- } else {
54
- console.log(`🔍 No rules or commands found matching "${query}".`);
55
- }
56
- } catch (err: unknown) {
57
- if (err && typeof err === 'object' && 'response' in err) {
58
- const axiosError = err as any;
59
- console.error('❌ Search failed:', axiosError.response?.data?.error || axiosError.message);
60
- } else {
61
- console.error('❌ Search failed:', err);
62
- }
63
- process.exit(1);
64
- }
65
- }
66
-
@@ -1,117 +0,0 @@
1
- import axios from 'axios';
2
- import * as fs from 'fs-extra';
3
- import * as path from 'path';
4
- import FormData from 'form-data';
5
- import { getConfig } from '../config';
6
-
7
- /**
8
- * @requirement TC-UNIT-CLI-004, TC-UNIT-CLI-005, TC-E2E-CLI-003
9
- */
10
- export async function uploadCommand(
11
- filePath: string,
12
- options: {
13
- name?: string;
14
- tag?: string;
15
- type?: string;
16
- description?: string;
17
- version?: string;
18
- }
19
- ) {
20
- try {
21
- const config = await getConfig();
22
-
23
- if (!config.token) {
24
- console.error('❌ Not logged in. Please run: kitstore login');
25
- process.exit(1);
26
- }
27
-
28
- // Validate file exists
29
- if (!(await fs.pathExists(filePath))) {
30
- console.error(`❌ File not found: ${filePath}`);
31
- process.exit(1);
32
- }
33
-
34
- // Validate file size (max 10MB)
35
- const stats = await fs.stat(filePath);
36
- const fileSizeInMB = stats.size / (1024 * 1024);
37
- const maxSizeInMB = 10;
38
- if (fileSizeInMB > maxSizeInMB) {
39
- console.error(`❌ File size exceeds limit (${fileSizeInMB.toFixed(2)} MB > ${maxSizeInMB} MB)`);
40
- process.exit(1);
41
- }
42
-
43
- // Validate file type
44
- const allowedExtensions = ['.md', '.txt', '.json', '.cursorrules'];
45
- const fileExtension = path.extname(filePath).toLowerCase();
46
- if (!allowedExtensions.includes(fileExtension)) {
47
- console.error(`❌ Invalid file type. Allowed types: ${allowedExtensions.join(', ')}`);
48
- process.exit(1);
49
- }
50
-
51
- // Auto-detect type from path if not provided
52
- let type = options.type;
53
- if (!type) {
54
- const normalizedPath = path.normalize(filePath);
55
- if (normalizedPath.includes('.cursor/rules') || normalizedPath.includes('.cursorrules')) {
56
- type = 'rule';
57
- } else if (normalizedPath.includes('.cursor/commands')) {
58
- type = 'command';
59
- }
60
- }
61
-
62
- if (!type) {
63
- console.error('❌ Type is required. Use --type rule or --type command');
64
- process.exit(1);
65
- }
66
-
67
- if (!options.tag) {
68
- console.error('❌ Tag is required. Use --tag <tag>');
69
- process.exit(1);
70
- }
71
-
72
- // Prepare form data
73
- const form = new FormData();
74
- form.append('file', fs.createReadStream(filePath));
75
- form.append('name', options.name || path.basename(filePath));
76
- form.append('tag', options.tag);
77
- form.append('type', type);
78
- if (options.description) form.append('description', options.description);
79
- form.append('version', options.version || '1.0.0');
80
-
81
- console.log('Uploading...');
82
-
83
- const response = await axios.post(`${config.server}/api/rules`, form, {
84
- headers: {
85
- ...form.getHeaders(),
86
- Authorization: `Bearer ${config.token}`,
87
- },
88
- maxContentLength: Infinity,
89
- maxBodyLength: Infinity,
90
- });
91
-
92
- if (response.data && response.data.id) {
93
- console.log(`✅ Uploaded successfully!`);
94
- console.log(` Name: ${response.data.name}`);
95
- console.log(` Tag: ${response.data.tag}`);
96
- console.log(` Type: ${response.data.type}`);
97
- console.log(` Version: ${response.data.version}`);
98
- } else {
99
- console.error('❌ Upload failed');
100
- process.exit(1);
101
- }
102
- } catch (err: unknown) {
103
- if (axios.isAxiosError(err)) {
104
- const errorData = err.response?.data;
105
- const errorMessage = errorData?.message || errorData?.error || err.message;
106
- console.error('❌ Upload failed:', errorMessage);
107
- if (errorData && typeof errorData === 'object') {
108
- console.error(' Details:', JSON.stringify(errorData, null, 2));
109
- }
110
- } else {
111
- console.error('❌ Upload failed:', err);
112
- }
113
- process.exit(1);
114
- }
115
- }
116
-
117
-
package/src/config.ts DELETED
@@ -1,66 +0,0 @@
1
- /**
2
- * @requirement TC-E2E-CLI-001
3
- */
4
- import * as fs from 'fs-extra';
5
- import * as path from 'path';
6
- import * as os from 'os';
7
-
8
- const CONFIG_DIR = process.env.KITSTORE_CONFIG_DIR || path.join(os.homedir(), '.kitstore');
9
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
10
-
11
- export interface Config {
12
- token?: string;
13
- server: string;
14
- user?: {
15
- id: string;
16
- username: string;
17
- email?: string;
18
- };
19
- lastLogin?: string;
20
- }
21
-
22
- export async function getConfig(): Promise<Config> {
23
- try {
24
- await fs.ensureDir(CONFIG_DIR);
25
- if (await fs.pathExists(CONFIG_FILE)) {
26
- const content = await fs.readJson(CONFIG_FILE);
27
- // Environment variable takes precedence over config file, fallback to localhost for development
28
- const server = process.env.AGENTKIT_BACKEND_URL || content.server || 'http://localhost:3000';
29
- return { ...content, server };
30
- }
31
- } catch (err) {
32
- // Ignore errors
33
- }
34
- // Use production backend URL if available, fallback to localhost for development
35
- const server = process.env.AGENTKIT_BACKEND_URL || 'http://localhost:3000';
36
- return { server };
37
- }
38
-
39
- export async function saveConfig(config: Config): Promise<void> {
40
- await fs.ensureDir(CONFIG_DIR);
41
- await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
42
- }
43
-
44
- export function getInstallPaths(): { rules: string; commands: string } {
45
- const cwd = process.cwd();
46
- const home = os.homedir();
47
-
48
- // Check for .cursor in current directory first
49
- const cwdCursor = path.join(cwd, '.cursor');
50
- if (fs.existsSync(cwdCursor)) {
51
- return {
52
- rules: path.join(cwdCursor, 'rules'),
53
- commands: path.join(cwdCursor, 'commands'),
54
- };
55
- }
56
-
57
- // Fallback to home directory
58
- return {
59
- rules: path.join(home, '.cursor', 'rules'),
60
- commands: path.join(home, '.cursor', 'commands'),
61
- };
62
- }
63
-
64
-
65
-
66
-
package/src/index.ts DELETED
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { loginCommand } from './commands/login';
5
- import { installCommand } from './commands/install';
6
- import { uploadCommand } from './commands/upload';
7
- import { listCommand } from './commands/list';
8
- import { searchCommand } from './commands/search';
9
- import { initCommand } from './commands/init';
10
- import { ruleCheckCommand } from './commands/rule-check';
11
- import pkg from '../package.json';
12
-
13
- const program = new Command();
14
-
15
- program
16
- .name('kitstore')
17
- .description('CLI tool for managing Cursor rules and commands')
18
- .version(pkg.version);
19
-
20
- program
21
- .command('init')
22
- .description('Initialize Cursor Kit in the current directory')
23
- .action(initCommand);
24
-
25
- program
26
- .command('rule-check')
27
- .description('Check and validate a rule file')
28
- .argument('<file-path>', 'Path to the rule file')
29
- .action(ruleCheckCommand);
30
-
31
- program
32
- .command('login')
33
- .description('Login to Cursor Kit backend')
34
- .option('-e, --email <email>', 'Email address')
35
- .option('-p, --password <password>', 'Password')
36
- .option('-s, --server <url>', 'Backend server URL', 'http://localhost:3000')
37
- .action(loginCommand);
38
-
39
- program
40
- .command('install')
41
- .description('Install rules or commands')
42
- .option('-t, --tag <tag>', 'Install by tag')
43
- .option('-n, --name <name>', 'Install by name')
44
- .option('--type <type>', 'Filter by type (rule|command)')
45
- .option('-f, --force', 'Overwrite existing files')
46
- .action(installCommand);
47
-
48
- program
49
- .command('upload')
50
- .description('Upload a rule or command file')
51
- .argument('<file-path>', 'Path to the file to upload')
52
- .option('-n, --name <name>', 'Name of the rule/command')
53
- .option('-t, --tag <tag>', 'Tag (required)')
54
- .option('--type <type>', 'Type: rule or command (required)')
55
- .option('-d, --description <desc>', 'Description')
56
- .option('--rule-version <version>', 'Version', '1.0.0')
57
- .action(uploadCommand);
58
-
59
- program
60
- .command('list')
61
- .description('List available rules and commands')
62
- .option('--type <type>', 'Filter by type (rule|command)')
63
- .option('-t, --tag <tag>', 'Filter by tag')
64
- .option('-n, --name <name>', 'Filter by name')
65
- .option('-l, --limit <number>', 'Limit results', '20')
66
- .action(listCommand);
67
-
68
- program
69
- .command('search')
70
- .description('Search for rules and commands')
71
- .argument('<query>', 'Search query')
72
- .option('--type <type>', 'Filter by type (rule|command)')
73
- .option('-t, --tag <tag>', 'Filter by tag')
74
- .option('-l, --limit <number>', 'Limit results', '20')
75
- .action(searchCommand);
76
-
77
- program.parse();
78
-
79
-
@@ -1,118 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Test script to verify CLI backend URL configuration mechanism
4
-
5
- const { execSync } = require('child_process');
6
- const fs = require('fs-extra');
7
- const path = require('path');
8
- const os = require('os');
9
-
10
- console.log('🧪 Testing CLI Backend URL Configuration Mechanism...\n');
11
-
12
- // Test different configuration scenarios
13
- const testCases = [
14
- {
15
- name: 'Default configuration (no env var, no config file)',
16
- env: {},
17
- expectedServer: 'http://localhost:3000'
18
- },
19
- {
20
- name: 'Environment variable override',
21
- env: { AGENTKIT_BACKEND_URL: 'https://test-env.example.com' },
22
- expectedServer: 'https://test-env.example.com'
23
- },
24
- {
25
- name: 'Config file override',
26
- env: {},
27
- config: { server: 'https://test-config.example.com' },
28
- expectedServer: 'https://test-config.example.com'
29
- },
30
- {
31
- name: 'Environment variable takes precedence over config file',
32
- env: { AGENTKIT_BACKEND_URL: 'https://test-env-precedence.example.com' },
33
- config: { server: 'https://test-config-precedence.example.com' },
34
- expectedServer: 'https://test-env-precedence.example.com'
35
- }
36
- ];
37
-
38
- const configDir = path.join(os.homedir(), '.agentkit');
39
- const configFile = path.join(configDir, 'config.json');
40
-
41
- // Helper function to run CLI config test
42
- async function testConfig(testCase) {
43
- console.log(`📋 Testing: ${testCase.name}`);
44
-
45
- // Set environment variables
46
- const originalEnv = { ...process.env };
47
- Object.assign(process.env, testCase.env);
48
-
49
- try {
50
- // Create config file if specified
51
- if (testCase.config) {
52
- fs.ensureDirSync(configDir);
53
- fs.writeJsonSync(configFile, testCase.config);
54
- } else {
55
- // Remove config file
56
- if (fs.existsSync(configFile)) {
57
- fs.removeSync(configFile);
58
- }
59
- }
60
-
61
- // Import and test the config module
62
- delete require.cache[require.resolve('./dist/config.js')];
63
- const config = require('./dist/config.js');
64
-
65
- // Test getConfig function (async)
66
- const result = await config.getConfig();
67
- console.log(` Expected: ${testCase.expectedServer}`);
68
- console.log(` Got: ${result.server}`);
69
-
70
- if (result.server === testCase.expectedServer) {
71
- console.log(' ✅ PASS\n');
72
- return true;
73
- } else {
74
- console.log(' ❌ FAIL\n');
75
- return false;
76
- }
77
-
78
- } catch (error) {
79
- console.log(` ❌ ERROR: ${error.message}\n`);
80
- return false;
81
- } finally {
82
- // Restore environment
83
- process.env = originalEnv;
84
-
85
- // Clean up config file
86
- if (fs.existsSync(configFile)) {
87
- fs.removeSync(configFile);
88
- }
89
- }
90
- }
91
-
92
- // Run all test cases
93
- async function runTests() {
94
- let passed = 0;
95
- let total = testCases.length;
96
-
97
- for (const testCase of testCases) {
98
- if (await testConfig(testCase)) {
99
- passed++;
100
- }
101
- }
102
-
103
- console.log(`🎯 Test Results: ${passed}/${total} passed`);
104
-
105
- if (passed === total) {
106
- console.log('✅ All backend URL configuration tests passed!');
107
- console.log('📋 Configuration precedence verified:');
108
- console.log(' 1. AGENTKIT_BACKEND_URL environment variable');
109
- console.log(' 2. Configuration file (~/.agentkit/config.json)');
110
- console.log(' 3. Default localhost:3000');
111
- } else {
112
- console.log('❌ Some configuration tests failed!');
113
- process.exit(1);
114
- }
115
- }
116
-
117
- // Run the tests
118
- runTests().catch(console.error);
package/tsconfig.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "lib": ["ES2020"],
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "resolveJsonModule": true,
13
- "moduleResolution": "node"
14
- },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules", "dist"]
17
- }
18
-
19
-
20
-
21
-
22
-
23
-
24
-