kitstore-cli 1.0.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 (71) hide show
  1. package/.env.test +4 -0
  2. package/.eslintrc.js +29 -0
  3. package/dist/__tests__/commands/init.test.js +76 -0
  4. package/dist/__tests__/commands/install.test.js +422 -0
  5. package/dist/__tests__/commands/list.test.js +173 -0
  6. package/dist/__tests__/commands/login.test.js +281 -0
  7. package/dist/__tests__/commands/rule-check.test.js +72 -0
  8. package/dist/__tests__/commands/search.test.js +175 -0
  9. package/dist/__tests__/commands/upload.test.js +367 -0
  10. package/dist/__tests__/config.test.js +179 -0
  11. package/dist/__tests__/setup.js +8 -0
  12. package/dist/api/client.js +18 -0
  13. package/dist/api/generated/api.js +912 -0
  14. package/dist/api/generated/base.js +48 -0
  15. package/dist/api/generated/common.js +108 -0
  16. package/dist/api/generated/configuration.js +48 -0
  17. package/dist/api/generated/index.js +31 -0
  18. package/dist/commands/init.js +79 -0
  19. package/dist/commands/install.js +150 -0
  20. package/dist/commands/list.js +70 -0
  21. package/dist/commands/login.js +64 -0
  22. package/dist/commands/rule-check.js +81 -0
  23. package/dist/commands/search.js +59 -0
  24. package/dist/commands/upload.js +138 -0
  25. package/dist/config.js +84 -0
  26. package/dist/index.js +71 -0
  27. package/e2e/install.e2e.test.ts +237 -0
  28. package/e2e/integration.e2e.test.ts +346 -0
  29. package/e2e/login.e2e.test.ts +188 -0
  30. package/jest.config.js +24 -0
  31. package/openapitools.json +7 -0
  32. package/package.json +41 -0
  33. package/src/__tests__/commands/init.test.ts +52 -0
  34. package/src/__tests__/commands/install.test.ts +449 -0
  35. package/src/__tests__/commands/list.test.ts +164 -0
  36. package/src/__tests__/commands/login.test.ts +293 -0
  37. package/src/__tests__/commands/rule-check.test.ts +52 -0
  38. package/src/__tests__/commands/search.test.ts +168 -0
  39. package/src/__tests__/commands/upload.test.ts +404 -0
  40. package/src/__tests__/config.test.ts +181 -0
  41. package/src/__tests__/setup.ts +11 -0
  42. package/src/api/client.ts +20 -0
  43. package/src/api/generated/.openapi-generator/FILES +17 -0
  44. package/src/api/generated/.openapi-generator/VERSION +1 -0
  45. package/src/api/generated/.openapi-generator-ignore +23 -0
  46. package/src/api/generated/api.ts +1171 -0
  47. package/src/api/generated/base.ts +62 -0
  48. package/src/api/generated/common.ts +113 -0
  49. package/src/api/generated/configuration.ts +121 -0
  50. package/src/api/generated/docs/AuthApi.md +158 -0
  51. package/src/api/generated/docs/AuthResponseDto.md +22 -0
  52. package/src/api/generated/docs/AuthUserDto.md +24 -0
  53. package/src/api/generated/docs/HealthApi.md +183 -0
  54. package/src/api/generated/docs/LoginDto.md +22 -0
  55. package/src/api/generated/docs/RegisterDto.md +24 -0
  56. package/src/api/generated/docs/RuleAuthorDto.md +22 -0
  57. package/src/api/generated/docs/RuleResponseDto.md +36 -0
  58. package/src/api/generated/docs/RulesApi.md +289 -0
  59. package/src/api/generated/git_push.sh +57 -0
  60. package/src/api/generated/index.ts +18 -0
  61. package/src/commands/init.ts +46 -0
  62. package/src/commands/install.ts +129 -0
  63. package/src/commands/list.ts +71 -0
  64. package/src/commands/login.ts +65 -0
  65. package/src/commands/rule-check.ts +49 -0
  66. package/src/commands/search.ts +66 -0
  67. package/src/commands/upload.ts +117 -0
  68. package/src/config.ts +66 -0
  69. package/src/index.ts +79 -0
  70. package/test-cli-config.js +118 -0
  71. package/tsconfig.json +24 -0
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchCommand = searchCommand;
4
+ const config_1 = require("../config");
5
+ const client_1 = require("../api/client");
6
+ async function searchCommand(query, options) {
7
+ try {
8
+ const config = await (0, config_1.getConfig)();
9
+ if (!config.token) {
10
+ console.error('❌ Not logged in. Please run `kitstore login` first.');
11
+ process.exit(1);
12
+ }
13
+ if (!query || query.trim() === '') {
14
+ console.error('❌ Search query is required.');
15
+ process.exit(1);
16
+ }
17
+ const { rulesApi } = await (0, client_1.createApi)({ server: config.server });
18
+ const queryParams = {
19
+ name: query, // Search by name using the name filter
20
+ };
21
+ if (options.type)
22
+ queryParams.type = options.type;
23
+ if (options.tag)
24
+ queryParams.tag = options.tag;
25
+ if (options.limit)
26
+ queryParams.limit = parseInt(options.limit);
27
+ const response = await rulesApi.rulesControllerFindAll(queryParams);
28
+ if (response.data && response.data.length > 0) {
29
+ console.log(`🔍 Search results for "${query}":`);
30
+ console.log(`📋 Found ${response.data.length} matching rules/commands:`);
31
+ console.log('');
32
+ response.data.forEach((rule) => {
33
+ console.log(`📄 ${rule.name}`);
34
+ console.log(` ID: ${rule.id}`);
35
+ console.log(` Tag: ${rule.tag}`);
36
+ console.log(` Type: ${rule.type}`);
37
+ console.log(` Version: ${rule.version}`);
38
+ console.log(` Author: ${rule.author.username}`);
39
+ if (rule.description) {
40
+ console.log(` Description: ${rule.description}`);
41
+ }
42
+ console.log('');
43
+ });
44
+ }
45
+ else {
46
+ console.log(`🔍 No rules or commands found matching "${query}".`);
47
+ }
48
+ }
49
+ catch (err) {
50
+ if (err && typeof err === 'object' && 'response' in err) {
51
+ const axiosError = err;
52
+ console.error('❌ Search failed:', axiosError.response?.data?.error || axiosError.message);
53
+ }
54
+ else {
55
+ console.error('❌ Search failed:', err);
56
+ }
57
+ process.exit(1);
58
+ }
59
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.uploadCommand = uploadCommand;
40
+ const axios_1 = __importDefault(require("axios"));
41
+ const fs = __importStar(require("fs-extra"));
42
+ const path = __importStar(require("path"));
43
+ const form_data_1 = __importDefault(require("form-data"));
44
+ const config_1 = require("../config");
45
+ /**
46
+ * @requirement TC-UNIT-CLI-004, TC-UNIT-CLI-005, TC-E2E-CLI-003
47
+ */
48
+ async function uploadCommand(filePath, options) {
49
+ try {
50
+ const config = await (0, config_1.getConfig)();
51
+ if (!config.token) {
52
+ console.error('❌ Not logged in. Please run: kitstore login');
53
+ process.exit(1);
54
+ }
55
+ // Validate file exists
56
+ if (!(await fs.pathExists(filePath))) {
57
+ console.error(`❌ File not found: ${filePath}`);
58
+ process.exit(1);
59
+ }
60
+ // Validate file size (max 10MB)
61
+ const stats = await fs.stat(filePath);
62
+ const fileSizeInMB = stats.size / (1024 * 1024);
63
+ const maxSizeInMB = 10;
64
+ if (fileSizeInMB > maxSizeInMB) {
65
+ console.error(`❌ File size exceeds limit (${fileSizeInMB.toFixed(2)} MB > ${maxSizeInMB} MB)`);
66
+ process.exit(1);
67
+ }
68
+ // Validate file type
69
+ const allowedExtensions = ['.md', '.txt', '.json', '.cursorrules'];
70
+ const fileExtension = path.extname(filePath).toLowerCase();
71
+ if (!allowedExtensions.includes(fileExtension)) {
72
+ console.error(`❌ Invalid file type. Allowed types: ${allowedExtensions.join(', ')}`);
73
+ process.exit(1);
74
+ }
75
+ // Auto-detect type from path if not provided
76
+ let type = options.type;
77
+ if (!type) {
78
+ const normalizedPath = path.normalize(filePath);
79
+ if (normalizedPath.includes('.cursor/rules') || normalizedPath.includes('.cursorrules')) {
80
+ type = 'rule';
81
+ }
82
+ else if (normalizedPath.includes('.cursor/commands')) {
83
+ type = 'command';
84
+ }
85
+ }
86
+ if (!type) {
87
+ console.error('❌ Type is required. Use --type rule or --type command');
88
+ process.exit(1);
89
+ }
90
+ if (!options.tag) {
91
+ console.error('❌ Tag is required. Use --tag <tag>');
92
+ process.exit(1);
93
+ }
94
+ // Prepare form data
95
+ const form = new form_data_1.default();
96
+ form.append('file', fs.createReadStream(filePath));
97
+ form.append('name', options.name || path.basename(filePath));
98
+ form.append('tag', options.tag);
99
+ form.append('type', type);
100
+ if (options.description)
101
+ form.append('description', options.description);
102
+ form.append('version', options.version || '1.0.0');
103
+ console.log('Uploading...');
104
+ const response = await axios_1.default.post(`${config.server}/api/rules`, form, {
105
+ headers: {
106
+ ...form.getHeaders(),
107
+ Authorization: `Bearer ${config.token}`,
108
+ },
109
+ maxContentLength: Infinity,
110
+ maxBodyLength: Infinity,
111
+ });
112
+ if (response.data && response.data.id) {
113
+ console.log(`✅ Uploaded successfully!`);
114
+ console.log(` Name: ${response.data.name}`);
115
+ console.log(` Tag: ${response.data.tag}`);
116
+ console.log(` Type: ${response.data.type}`);
117
+ console.log(` Version: ${response.data.version}`);
118
+ }
119
+ else {
120
+ console.error('❌ Upload failed');
121
+ process.exit(1);
122
+ }
123
+ }
124
+ catch (err) {
125
+ if (axios_1.default.isAxiosError(err)) {
126
+ const errorData = err.response?.data;
127
+ const errorMessage = errorData?.message || errorData?.error || err.message;
128
+ console.error('❌ Upload failed:', errorMessage);
129
+ if (errorData && typeof errorData === 'object') {
130
+ console.error(' Details:', JSON.stringify(errorData, null, 2));
131
+ }
132
+ }
133
+ else {
134
+ console.error('❌ Upload failed:', err);
135
+ }
136
+ process.exit(1);
137
+ }
138
+ }
package/dist/config.js ADDED
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getConfig = getConfig;
37
+ exports.saveConfig = saveConfig;
38
+ exports.getInstallPaths = getInstallPaths;
39
+ /**
40
+ * @requirement TC-E2E-CLI-001
41
+ */
42
+ const fs = __importStar(require("fs-extra"));
43
+ const path = __importStar(require("path"));
44
+ const os = __importStar(require("os"));
45
+ const CONFIG_DIR = process.env.KITSTORE_CONFIG_DIR || path.join(os.homedir(), '.kitstore');
46
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
47
+ async function getConfig() {
48
+ try {
49
+ await fs.ensureDir(CONFIG_DIR);
50
+ if (await fs.pathExists(CONFIG_FILE)) {
51
+ const content = await fs.readJson(CONFIG_FILE);
52
+ // Environment variable takes precedence over config file, fallback to localhost for development
53
+ const server = process.env.AGENTKIT_BACKEND_URL || content.server || 'http://localhost:3000';
54
+ return { ...content, server };
55
+ }
56
+ }
57
+ catch (err) {
58
+ // Ignore errors
59
+ }
60
+ // Use production backend URL if available, fallback to localhost for development
61
+ const server = process.env.AGENTKIT_BACKEND_URL || 'http://localhost:3000';
62
+ return { server };
63
+ }
64
+ async function saveConfig(config) {
65
+ await fs.ensureDir(CONFIG_DIR);
66
+ await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
67
+ }
68
+ function getInstallPaths() {
69
+ const cwd = process.cwd();
70
+ const home = os.homedir();
71
+ // Check for .cursor in current directory first
72
+ const cwdCursor = path.join(cwd, '.cursor');
73
+ if (fs.existsSync(cwdCursor)) {
74
+ return {
75
+ rules: path.join(cwdCursor, 'rules'),
76
+ commands: path.join(cwdCursor, 'commands'),
77
+ };
78
+ }
79
+ // Fallback to home directory
80
+ return {
81
+ rules: path.join(home, '.cursor', 'rules'),
82
+ commands: path.join(home, '.cursor', 'commands'),
83
+ };
84
+ }
package/dist/index.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const login_1 = require("./commands/login");
9
+ const install_1 = require("./commands/install");
10
+ const upload_1 = require("./commands/upload");
11
+ const list_1 = require("./commands/list");
12
+ const search_1 = require("./commands/search");
13
+ const init_1 = require("./commands/init");
14
+ const rule_check_1 = require("./commands/rule-check");
15
+ const package_json_1 = __importDefault(require("../package.json"));
16
+ const program = new commander_1.Command();
17
+ program
18
+ .name('kitstore')
19
+ .description('CLI tool for managing Cursor rules and commands')
20
+ .version(package_json_1.default.version);
21
+ program
22
+ .command('init')
23
+ .description('Initialize Cursor Kit in the current directory')
24
+ .action(init_1.initCommand);
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(rule_check_1.ruleCheckCommand);
30
+ program
31
+ .command('login')
32
+ .description('Login to Cursor Kit backend')
33
+ .option('-e, --email <email>', 'Email address')
34
+ .option('-p, --password <password>', 'Password')
35
+ .option('-s, --server <url>', 'Backend server URL', 'http://localhost:3000')
36
+ .action(login_1.loginCommand);
37
+ program
38
+ .command('install')
39
+ .description('Install rules or commands')
40
+ .option('-t, --tag <tag>', 'Install by tag')
41
+ .option('-n, --name <name>', 'Install by name')
42
+ .option('--type <type>', 'Filter by type (rule|command)')
43
+ .option('-f, --force', 'Overwrite existing files')
44
+ .action(install_1.installCommand);
45
+ program
46
+ .command('upload')
47
+ .description('Upload a rule or command file')
48
+ .argument('<file-path>', 'Path to the file to upload')
49
+ .option('-n, --name <name>', 'Name of the rule/command')
50
+ .option('-t, --tag <tag>', 'Tag (required)')
51
+ .option('--type <type>', 'Type: rule or command (required)')
52
+ .option('-d, --description <desc>', 'Description')
53
+ .option('--rule-version <version>', 'Version', '1.0.0')
54
+ .action(upload_1.uploadCommand);
55
+ program
56
+ .command('list')
57
+ .description('List available rules and commands')
58
+ .option('--type <type>', 'Filter by type (rule|command)')
59
+ .option('-t, --tag <tag>', 'Filter by tag')
60
+ .option('-n, --name <name>', 'Filter by name')
61
+ .option('-l, --limit <number>', 'Limit results', '20')
62
+ .action(list_1.listCommand);
63
+ program
64
+ .command('search')
65
+ .description('Search for rules and commands')
66
+ .argument('<query>', 'Search query')
67
+ .option('--type <type>', 'Filter by type (rule|command)')
68
+ .option('-t, --tag <tag>', 'Filter by tag')
69
+ .option('-l, --limit <number>', 'Limit results', '20')
70
+ .action(search_1.searchCommand);
71
+ program.parse();
@@ -0,0 +1,237 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, unlinkSync, readFileSync, mkdirSync, rmSync } from 'fs';
3
+ import * as path from 'path';
4
+
5
+ // TC-E2E-CLI-002: Install rule flow
6
+ // TC-E2E-CLI-005: List rules
7
+ // TC-E2E-CLI-011: Search rules
8
+ describe('CLI Install E2E Tests', () => {
9
+ const tempHome = path.join(process.cwd(), 'temp-home-install');
10
+ const configDir = path.join(tempHome, '.kitstore');
11
+ const configPath = path.join(configDir, 'config.json');
12
+ const installDir = path.join(tempHome, '.cursor');
13
+ const rulesDir = path.join(installDir, 'rules');
14
+ const cliPath = path.join(process.cwd(), 'dist', 'index.js');
15
+
16
+ beforeAll(() => {
17
+ if (!existsSync(tempHome)) {
18
+ mkdirSync(tempHome, { recursive: true });
19
+ }
20
+ });
21
+
22
+ beforeEach(() => {
23
+ // Setup mock config for authenticated user
24
+ const config = {
25
+ server: 'http://localhost:3000',
26
+ token: 'test-token',
27
+ user: { id: 'test-user-id', username: 'testuser' },
28
+ lastLogin: new Date().toISOString(),
29
+ };
30
+
31
+ // Ensure config directory exists
32
+ if (!existsSync(configDir)) {
33
+ mkdirSync(configDir, { recursive: true });
34
+ }
35
+ require('fs').writeFileSync(configPath, JSON.stringify(config, null, 2));
36
+ });
37
+
38
+ afterAll(() => {
39
+ // Clean up temp home
40
+ if (existsSync(tempHome)) {
41
+ rmSync(tempHome, { recursive: true, force: true });
42
+ }
43
+ });
44
+
45
+ afterEach(() => {
46
+ // Clean up config and installed rules
47
+ if (existsSync(configPath)) {
48
+ unlinkSync(configPath);
49
+ }
50
+ if (existsSync(rulesDir)) {
51
+ rmSync(rulesDir, { recursive: true, force: true });
52
+ }
53
+ });
54
+
55
+ // TC-E2E-CLI-005: List rules
56
+ test('should list rules successfully', () => {
57
+ // Skip if CLI is not built
58
+ if (!existsSync(cliPath)) {
59
+ console.warn('Skipping test: CLI not built');
60
+ return;
61
+ }
62
+
63
+ try {
64
+ // Run list command
65
+ const result = execSync(`node ${cliPath} list`, {
66
+ encoding: 'utf8',
67
+ timeout: 10000,
68
+ env: {
69
+ ...process.env,
70
+ NODE_ENV: 'test',
71
+ AGENTKIT_CONFIG_DIR: configDir,
72
+ HOME: tempHome,
73
+ USERPROFILE: tempHome,
74
+ HOMEDRIVE: tempHome.substring(0, 2),
75
+ HOMEPATH: tempHome.substring(2)
76
+ }
77
+ });
78
+
79
+ // Should show found rules or empty message
80
+ expect(result).toMatch(/📋 Found \d+ rules\/commands|📭 No rules or commands found/);
81
+
82
+ } catch (error: any) {
83
+ // If backend not running, skip test
84
+ const output = (error.stdout || '') + (error.stderr || '');
85
+ if (
86
+ output.includes('ECONNREFUSED') ||
87
+ output.includes('ENOTFOUND') ||
88
+ output.includes('Not logged in') ||
89
+ output.includes('Failed to list rules') ||
90
+ output.includes('failed: Error')
91
+ ) {
92
+ console.warn('Skipping test: Backend server not running or unauthorized');
93
+ return;
94
+ } else {
95
+ throw error;
96
+ }
97
+ }
98
+ });
99
+
100
+ // TC-E2E-CLI-011: Search rules
101
+ test('should search rules successfully', () => {
102
+ // Skip if CLI is not built
103
+ if (!existsSync(cliPath)) {
104
+ console.warn('Skipping test: CLI not built');
105
+ return;
106
+ }
107
+
108
+ try {
109
+ // Run search command
110
+ const result = execSync(`node ${cliPath} search "test"`, {
111
+ encoding: 'utf8',
112
+ timeout: 10000,
113
+ env: {
114
+ ...process.env,
115
+ NODE_ENV: 'test',
116
+ AGENTKIT_CONFIG_DIR: configDir,
117
+ HOME: tempHome,
118
+ USERPROFILE: tempHome,
119
+ HOMEDRIVE: tempHome.substring(0, 2),
120
+ HOMEPATH: tempHome.substring(2)
121
+ }
122
+ });
123
+
124
+ // Should show search results
125
+ expect(result).toMatch(/🔍 Search results for "test"|🔍 No rules or commands found matching "test"/);
126
+
127
+ } catch (error: any) {
128
+ // If backend not running, skip test
129
+ const output = (error.stdout || '') + (error.stderr || '');
130
+ if (
131
+ output.includes('ECONNREFUSED') ||
132
+ output.includes('ENOTFOUND') ||
133
+ output.includes('Not logged in') ||
134
+ output.includes('Failed to list rules') ||
135
+ output.includes('failed: Error')
136
+ ) {
137
+ console.warn('Skipping test: Backend server not running or unauthorized');
138
+ return;
139
+ } else {
140
+ throw error;
141
+ }
142
+ }
143
+ });
144
+
145
+ // TC-E2E-CLI-012: List with filters
146
+ test('should list rules with tag filter', () => {
147
+ // Skip if CLI is not built
148
+ if (!existsSync(cliPath)) {
149
+ console.warn('Skipping test: CLI not built');
150
+ return;
151
+ }
152
+
153
+ try {
154
+ // Run list command with tag filter
155
+ const result = execSync(`node ${cliPath} list --tag test`, {
156
+ encoding: 'utf8',
157
+ timeout: 10000,
158
+ env: {
159
+ ...process.env,
160
+ NODE_ENV: 'test',
161
+ AGENTKIT_CONFIG_DIR: configDir,
162
+ HOME: tempHome,
163
+ USERPROFILE: tempHome,
164
+ HOMEDRIVE: tempHome.substring(0, 2),
165
+ HOMEPATH: tempHome.substring(2)
166
+ }
167
+ });
168
+
169
+ // Should show filtered results
170
+ expect(result).toMatch(/📋 Found \d+ rules\/commands|📭 No rules or commands found/);
171
+
172
+ } catch (error: any) {
173
+ // If backend not running, skip test
174
+ const output = (error.stdout || '') + (error.stderr || '');
175
+ if (
176
+ output.includes('ECONNREFUSED') ||
177
+ output.includes('ENOTFOUND') ||
178
+ output.includes('Not logged in') ||
179
+ output.includes('Failed to list rules') ||
180
+ output.includes('failed: Error')
181
+ ) {
182
+ console.warn('Skipping test: Backend server not running or unauthorized');
183
+ return;
184
+ } else {
185
+ throw error;
186
+ }
187
+ }
188
+ });
189
+
190
+ // TC-E2E-CLI-013: List with type filter
191
+ test('should list rules with type filter', () => {
192
+ // Skip if CLI is not built
193
+ if (!existsSync(cliPath)) {
194
+ console.warn('Skipping test: CLI not built');
195
+ return;
196
+ }
197
+
198
+ try {
199
+ // Run list command with type filter
200
+ const result = execSync(`node ${cliPath} list --type rule`, {
201
+ encoding: 'utf8',
202
+ timeout: 10000,
203
+ env: {
204
+ ...process.env,
205
+ NODE_ENV: 'test',
206
+ AGENTKIT_CONFIG_DIR: configDir,
207
+ HOME: tempHome,
208
+ USERPROFILE: tempHome,
209
+ HOMEDRIVE: tempHome.substring(0, 2),
210
+ HOMEPATH: tempHome.substring(2)
211
+ }
212
+ });
213
+
214
+ // Should show filtered results
215
+ expect(result).toMatch(/📋 Found \d+ rules\/commands|📭 No rules or commands found/);
216
+
217
+ } catch (error: any) {
218
+ // If backend not running, skip test
219
+ const output = (error.stdout || '') + (error.stderr || '');
220
+ if (
221
+ output.includes('ECONNREFUSED') ||
222
+ output.includes('ENOTFOUND') ||
223
+ output.includes('Not logged in') ||
224
+ output.includes('Failed to list rules') ||
225
+ output.includes('failed: Error')
226
+ ) {
227
+ console.warn('Skipping test: Backend server not running or unauthorized');
228
+ return;
229
+ } else {
230
+ throw error;
231
+ }
232
+ }
233
+ });
234
+ });
235
+
236
+
237
+