pqm-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 (43) hide show
  1. package/README.md +254 -0
  2. package/bin/pqm.js +6 -0
  3. package/package.json +31 -0
  4. package/src/ai/analyzer/collector.js +191 -0
  5. package/src/ai/analyzer/dependency.js +269 -0
  6. package/src/ai/analyzer/index.js +234 -0
  7. package/src/ai/analyzer/quality.js +241 -0
  8. package/src/ai/analyzer/security.js +302 -0
  9. package/src/ai/index.js +16 -0
  10. package/src/ai/providers/bailian.js +121 -0
  11. package/src/ai/providers/base.js +177 -0
  12. package/src/ai/providers/deepseek.js +100 -0
  13. package/src/ai/providers/index.js +100 -0
  14. package/src/ai/providers/openai.js +100 -0
  15. package/src/builders/base.js +35 -0
  16. package/src/builders/rollup.js +47 -0
  17. package/src/builders/vite.js +47 -0
  18. package/src/cli.js +41 -0
  19. package/src/commands/ai.js +317 -0
  20. package/src/commands/build.js +24 -0
  21. package/src/commands/commit.js +68 -0
  22. package/src/commands/config.js +113 -0
  23. package/src/commands/doctor.js +146 -0
  24. package/src/commands/init.js +61 -0
  25. package/src/commands/login.js +37 -0
  26. package/src/commands/publish.js +250 -0
  27. package/src/commands/release.js +107 -0
  28. package/src/commands/scan.js +239 -0
  29. package/src/commands/status.js +129 -0
  30. package/src/commands/watch.js +170 -0
  31. package/src/commands/webhook.js +240 -0
  32. package/src/config/detector.js +82 -0
  33. package/src/config/global.js +136 -0
  34. package/src/config/loader.js +49 -0
  35. package/src/core/builder.js +88 -0
  36. package/src/index.js +5 -0
  37. package/src/logs/build.js +47 -0
  38. package/src/logs/manager.js +60 -0
  39. package/src/report/formatter.js +282 -0
  40. package/src/utils/http.js +130 -0
  41. package/src/utils/logger.js +24 -0
  42. package/src/utils/prompt.js +132 -0
  43. package/src/utils/spinner.js +134 -0
@@ -0,0 +1,130 @@
1
+ import https from 'https';
2
+ import http from 'http';
3
+
4
+ /**
5
+ * Make an HTTP/HTTPS request
6
+ * @param {Object} options - Request options
7
+ * @returns {Promise<Object>} Response object with data and status
8
+ */
9
+ export function request(options) {
10
+ return new Promise((resolve, reject) => {
11
+ const {
12
+ url,
13
+ method = 'GET',
14
+ headers = {},
15
+ body = null,
16
+ timeout = 30000
17
+ } = options;
18
+
19
+ // Parse URL
20
+ const urlObj = new URL(url);
21
+ const isHttps = urlObj.protocol === 'https:';
22
+ const client = isHttps ? https : http;
23
+
24
+ const reqOptions = {
25
+ hostname: urlObj.hostname,
26
+ port: urlObj.port || (isHttps ? 443 : 80),
27
+ path: urlObj.pathname + urlObj.search,
28
+ method: method.toUpperCase(),
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ ...headers
32
+ },
33
+ timeout
34
+ };
35
+
36
+ const req = client.request(reqOptions, (res) => {
37
+ let data = '';
38
+
39
+ res.on('data', (chunk) => {
40
+ data += chunk;
41
+ });
42
+
43
+ res.on('end', () => {
44
+ resolve({
45
+ status: res.statusCode,
46
+ headers: res.headers,
47
+ data: data
48
+ });
49
+ });
50
+ });
51
+
52
+ req.on('error', (err) => {
53
+ reject(new Error(`HTTP request failed: ${err.message}`));
54
+ });
55
+
56
+ req.on('timeout', () => {
57
+ req.destroy();
58
+ reject(new Error('Request timeout'));
59
+ });
60
+
61
+ if (body) {
62
+ req.write(typeof body === 'string' ? body : JSON.stringify(body));
63
+ }
64
+
65
+ req.end();
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Make a GET request
71
+ * @param {string} url - Request URL
72
+ * @param {Object} headers - Request headers
73
+ * @param {number} timeout - Timeout in milliseconds
74
+ * @returns {Promise<Object>} Parsed JSON response
75
+ */
76
+ export async function get(url, headers = {}, timeout = 30000) {
77
+ const response = await request({ url, method: 'GET', headers, timeout });
78
+
79
+ if (response.status >= 400) {
80
+ throw new Error(`HTTP ${response.status}: ${response.data}`);
81
+ }
82
+
83
+ try {
84
+ return JSON.parse(response.data);
85
+ } catch {
86
+ return response.data;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Make a POST request
92
+ * @param {string} url - Request URL
93
+ * @param {Object} body - Request body
94
+ * @param {Object} headers - Request headers
95
+ * @param {number} timeout - Timeout in milliseconds
96
+ * @returns {Promise<Object>} Parsed JSON response
97
+ */
98
+ export async function post(url, body = {}, headers = {}, timeout = 60000) {
99
+ const response = await request({
100
+ url,
101
+ method: 'POST',
102
+ body,
103
+ headers,
104
+ timeout
105
+ });
106
+
107
+ if (response.status >= 400) {
108
+ const errorData = response.data;
109
+ let errorMessage = `HTTP ${response.status}`;
110
+ try {
111
+ const parsed = JSON.parse(errorData);
112
+ errorMessage += `: ${parsed.message || parsed.error || JSON.stringify(parsed)}`;
113
+ } catch {
114
+ errorMessage += `: ${errorData}`;
115
+ }
116
+ throw new Error(errorMessage);
117
+ }
118
+
119
+ try {
120
+ return JSON.parse(response.data);
121
+ } catch {
122
+ return response.data;
123
+ }
124
+ }
125
+
126
+ export default {
127
+ request,
128
+ get,
129
+ post
130
+ };
@@ -0,0 +1,24 @@
1
+ import chalk from 'chalk';
2
+
3
+ export const logger = {
4
+ info: (msg) => console.log(chalk.blue('ℹ'), msg),
5
+ success: (msg) => console.log(chalk.green('✓'), msg),
6
+ warn: (msg) => console.log(chalk.yellow('⚠'), msg),
7
+ error: (msg) => console.log(chalk.red('✗'), msg),
8
+ watch: (file) => console.log(chalk.cyan('👁'), chalk.gray(file)),
9
+ build: (mode) => console.log(chalk.magenta('⚡'), `Building (${mode})...`),
10
+ debug: (msg) => {
11
+ if (process.env.PQM_DEBUG) {
12
+ console.log(chalk.gray('🔍'), chalk.gray(msg));
13
+ }
14
+ }
15
+ };
16
+
17
+ export function formatTimestamp() {
18
+ return new Date().toISOString().replace('T', ' ').slice(0, 19);
19
+ }
20
+
21
+ export function logWithTime(msg, type = 'info') {
22
+ const timestamp = formatTimestamp();
23
+ logger[type](`[${timestamp}] ${msg}`);
24
+ }
@@ -0,0 +1,132 @@
1
+ import readline from 'readline';
2
+
3
+ /**
4
+ * Create readline interface
5
+ * @returns {readline.Interface}
6
+ */
7
+ function createInterface() {
8
+ return readline.createInterface({
9
+ input: process.stdin,
10
+ output: process.stdout
11
+ });
12
+ }
13
+
14
+ /**
15
+ * Prompt user for input
16
+ * @param {string} question - Question to ask
17
+ * @param {string} defaultValue - Default value if user presses enter
18
+ * @returns {Promise<string>} User's answer
19
+ */
20
+ export function promptUser(question, defaultValue = '') {
21
+ return new Promise((resolve) => {
22
+ const rl = createInterface();
23
+ const displayQuestion = defaultValue
24
+ ? `${question} (${defaultValue}): `
25
+ : `${question}: `;
26
+
27
+ rl.question(displayQuestion, (answer) => {
28
+ rl.close();
29
+ resolve(answer.trim() || defaultValue);
30
+ });
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Prompt user for password input (hidden)
36
+ * @param {string} question - Question to ask
37
+ * @returns {Promise<string>} User's password
38
+ */
39
+ export function promptPassword(question) {
40
+ return new Promise((resolve) => {
41
+ const rl = createInterface();
42
+ // Note: In a real terminal, we'd use mute mode for password
43
+ // For simplicity, we just use regular input
44
+ rl.question(`${question}: `, (answer) => {
45
+ rl.close();
46
+ resolve(answer.trim());
47
+ });
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Prompt user to select from options
53
+ * @param {string} question - Question to ask
54
+ * @param {Array<Object|string>} options - Options to choose from
55
+ * @param {number} defaultIndex - Default selection index
56
+ * @returns {Promise<Object|string>} Selected option
57
+ */
58
+ export async function select(question, options, defaultIndex = 0) {
59
+ console.log(`\n${question}`);
60
+ console.log('');
61
+
62
+ options.forEach((opt, index) => {
63
+ const label = typeof opt === 'string' ? opt : opt.label || opt.name;
64
+ const marker = index === defaultIndex ? '>' : ' ';
65
+ console.log(` ${marker} ${index + 1}. ${label}`);
66
+ });
67
+
68
+ console.log('');
69
+
70
+ const answer = await promptUser('请选择 (输入序号)', String(defaultIndex + 1));
71
+ const index = parseInt(answer, 10) - 1;
72
+
73
+ if (isNaN(index) || index < 0 || index >= options.length) {
74
+ return options[defaultIndex];
75
+ }
76
+
77
+ return options[index];
78
+ }
79
+
80
+ /**
81
+ * Prompt user for confirmation
82
+ * @param {string} question - Question to ask
83
+ * @param {boolean} defaultValue - Default value
84
+ * @returns {Promise<boolean>} True if confirmed
85
+ */
86
+ export async function confirm(question, defaultValue = false) {
87
+ const hint = defaultValue ? 'Y/n' : 'y/N';
88
+ const answer = await promptUser(`${question} (${hint})`, '');
89
+
90
+ if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
91
+ return true;
92
+ }
93
+ if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') {
94
+ return false;
95
+ }
96
+
97
+ return defaultValue;
98
+ }
99
+
100
+ /**
101
+ * Prompt for multiple inputs
102
+ * @param {Array<Object>} prompts - Array of prompt configurations
103
+ * @returns {Promise<Object>} Object with answers
104
+ */
105
+ export async function promptMultiple(prompts) {
106
+ const answers = {};
107
+
108
+ for (const prompt of prompts) {
109
+ const { name, question, defaultValue = '', type = 'text' } = prompt;
110
+
111
+ let answer;
112
+ if (type === 'password') {
113
+ answer = await promptPassword(question);
114
+ } else if (type === 'confirm') {
115
+ answer = await confirm(question, defaultValue);
116
+ } else {
117
+ answer = await promptUser(question, defaultValue);
118
+ }
119
+
120
+ answers[name] = answer;
121
+ }
122
+
123
+ return answers;
124
+ }
125
+
126
+ export default {
127
+ promptUser,
128
+ promptPassword,
129
+ select,
130
+ confirm,
131
+ promptMultiple
132
+ };
@@ -0,0 +1,134 @@
1
+ import chalk from 'chalk';
2
+
3
+ // Try to load ora, fall back to SimpleSpinner if not available
4
+ let ora = null;
5
+ try {
6
+ const oraModule = await import('ora');
7
+ ora = oraModule.default;
8
+ } catch {
9
+ // ora not available, will use SimpleSpinner
10
+ }
11
+
12
+ // Simple spinner without ora dependency
13
+ export class SimpleSpinner {
14
+ constructor(text = '') {
15
+ this.text = text;
16
+ this.interval = null;
17
+ this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
18
+ this.frameIndex = 0;
19
+ }
20
+
21
+ start(text) {
22
+ this.text = text || this.text;
23
+ process.stdout.write('\x1B[?25l'); // Hide cursor
24
+ this.interval = setInterval(() => {
25
+ process.stdout.write(`\r${chalk.cyan(this.frames[this.frameIndex])} ${this.text}`);
26
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
27
+ }, 80);
28
+ return this;
29
+ }
30
+
31
+ stop(clearLine = true) {
32
+ if (this.interval) {
33
+ clearInterval(this.interval);
34
+ this.interval = null;
35
+ }
36
+ if (clearLine) {
37
+ process.stdout.write('\r\x1B[K');
38
+ }
39
+ process.stdout.write('\x1B[?25h'); // Show cursor
40
+ return this;
41
+ }
42
+
43
+ succeed(text) {
44
+ this.stop(false);
45
+ process.stdout.write(`\r${chalk.green('✓')} ${text || this.text}\n`);
46
+ return this;
47
+ }
48
+
49
+ fail(text) {
50
+ this.stop(false);
51
+ process.stdout.write(`\r${chalk.red('✗')} ${text || this.text}\n`);
52
+ return this;
53
+ }
54
+
55
+ warn(text) {
56
+ this.stop(false);
57
+ process.stdout.write(`\r${chalk.yellow('!')} ${text || this.text}\n`);
58
+ return this;
59
+ }
60
+
61
+ info(text) {
62
+ this.stop(false);
63
+ process.stdout.write(`\r${chalk.blue('i')} ${text || this.text}\n`);
64
+ return this;
65
+ }
66
+
67
+ update(text) {
68
+ this.text = text;
69
+ return this;
70
+ }
71
+ }
72
+
73
+ // Ora-based spinner (used when ora is available)
74
+ class OraSpinner {
75
+ constructor(text = '') {
76
+ this.spinner = null;
77
+ this.text = text;
78
+ }
79
+
80
+ start(text) {
81
+ this.spinner = ora({
82
+ text: text || this.text,
83
+ spinner: 'dots'
84
+ }).start();
85
+ return this;
86
+ }
87
+
88
+ stop() {
89
+ if (this.spinner) {
90
+ this.spinner.stop();
91
+ }
92
+ return this;
93
+ }
94
+
95
+ succeed(text) {
96
+ if (this.spinner) {
97
+ this.spinner.succeed(chalk.green(text || this.text));
98
+ }
99
+ return this;
100
+ }
101
+
102
+ fail(text) {
103
+ if (this.spinner) {
104
+ this.spinner.fail(chalk.red(text || this.text));
105
+ }
106
+ return this;
107
+ }
108
+
109
+ warn(text) {
110
+ if (this.spinner) {
111
+ this.spinner.warn(chalk.yellow(text || this.text));
112
+ }
113
+ return this;
114
+ }
115
+
116
+ info(text) {
117
+ if (this.spinner) {
118
+ this.spinner.info(chalk.blue(text || this.text));
119
+ }
120
+ return this;
121
+ }
122
+
123
+ update(text) {
124
+ if (this.spinner) {
125
+ this.spinner.text = text;
126
+ }
127
+ return this;
128
+ }
129
+ }
130
+
131
+ // Export appropriate Spinner based on ora availability
132
+ export const Spinner = ora ? OraSpinner : SimpleSpinner;
133
+
134
+ export default Spinner;