@mandors/cli 0.0.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.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * @fileoverview Mandor configuration management
3
+ * @description Reads and writes Mandor configuration using .mandorrc.json
4
+ * @version 0.0.1
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ /** @type {string} Default config filename */
11
+ const CONFIG_FILENAME = '.mandorrc.json';
12
+
13
+ /**
14
+ * Configuration management class for Mandor projects
15
+ * @class
16
+ * @version 0.0.1
17
+ * @example
18
+ * const config = new MandorConfig('/path/to/project');
19
+ * const defaultPriority = config.get('priority.default', 'P3');
20
+ * config.set('theme', 'dark');
21
+ */
22
+ class MandorConfig {
23
+ /**
24
+ * Creates a new MandorConfig instance
25
+ * @constructor
26
+ * @param {string} projectRoot - Root directory of the project
27
+ * @example
28
+ * const config = new MandorConfig('/my/project');
29
+ */
30
+ constructor(projectRoot) {
31
+ /** @type {string} Path to config file */
32
+ this.configPath = path.join(projectRoot, CONFIG_FILENAME);
33
+ /** @type {string} Project root directory */
34
+ this.projectRoot = projectRoot;
35
+ }
36
+
37
+ /**
38
+ * Gets a configuration value
39
+ * @param {string} key - Configuration key (supports dot notation, e.g., 'priority.default')
40
+ * @param {*} [defaultValue] - Default value if key not found
41
+ * @returns {*} The configuration value or default
42
+ * @example
43
+ * const priority = config.get('priority.default', 'P3');
44
+ * const strict = config.get('strictMode', false);
45
+ */
46
+ get(key, defaultValue = undefined) {
47
+ if (!fs.existsSync(this.configPath)) return defaultValue;
48
+ const config = JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));
49
+
50
+ // Support dot notation for nested keys
51
+ const keys = key.split('.');
52
+ let value = config;
53
+ for (const k of keys) {
54
+ if (value && typeof value === 'object' && k in value) {
55
+ value = value[k];
56
+ } else {
57
+ return defaultValue;
58
+ }
59
+ }
60
+ return value !== undefined ? value : defaultValue;
61
+ }
62
+
63
+ /**
64
+ * Sets a configuration value
65
+ * @param {string} key - Configuration key (supports dot notation)
66
+ * @param {*} value - Value to set
67
+ * @returns {void}
68
+ * @example
69
+ * config.set('priority.default', 'P2');
70
+ * config.set('theme', 'dark');
71
+ */
72
+ set(key, value) {
73
+ const config = fs.existsSync(this.configPath)
74
+ ? JSON.parse(fs.readFileSync(this.configPath, 'utf-8'))
75
+ : {};
76
+
77
+ // Support dot notation for nested keys
78
+ .split('.');
79
+ const keys = key let current = config;
80
+ for (let i = 0; i < keys.length - 1; i++) {
81
+ const k = keys[i];
82
+ if (!current[k] || typeof current[k] !== 'object') {
83
+ current[k] = {};
84
+ }
85
+ current = current[k];
86
+ }
87
+ current[keys[keys.length - 1]] = value;
88
+
89
+ fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
90
+ }
91
+
92
+ /**
93
+ * Deletes a configuration key
94
+ * @param {string} key - Configuration key to delete
95
+ * @returns {boolean} True if key was deleted
96
+ * @example
97
+ * config.delete('theme');
98
+ */
99
+ delete(key) {
100
+ if (!fs.existsSync(this.configPath)) return false;
101
+ const config = JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));
102
+
103
+ const keys = key.split('.');
104
+ let current = config;
105
+ for (let i = 0; i < keys.length - 1; i++) {
106
+ if (!current[keys[i]]) return false;
107
+ current = current[keys[i]];
108
+ }
109
+
110
+ if (current[keys[keys.length - 1]] !== undefined) {
111
+ delete current[keys[keys.length - 1]];
112
+ fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
113
+ return true;
114
+ }
115
+ return false;
116
+ }
117
+
118
+ /**
119
+ * Checks if a configuration key exists
120
+ * @param {string} key - Configuration key to check
121
+ * @returns {boolean} True if key exists
122
+ * @example
123
+ * if (config.has('theme')) { console.log('Theme is set'); }
124
+ */
125
+ has(key) {
126
+ return this.get(key) !== undefined;
127
+ }
128
+
129
+ /**
130
+ * Gets all configuration as an object
131
+ * @returns {Object} Full configuration object
132
+ * @example
133
+ * const allConfig = config.getAll();
134
+ */
135
+ getAll() {
136
+ if (!fs.existsSync(this.configPath)) return {};
137
+ return JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));
138
+ }
139
+
140
+ /**
141
+ * Clears all configuration
142
+ * @returns {void}
143
+ * @example
144
+ * config.clear();
145
+ */
146
+ clear() {
147
+ if (fs.existsSync(this.configPath)) {
148
+ fs.unlinkSync(this.configPath);
149
+ }
150
+ }
151
+ }
152
+
153
+ module.exports = MandorConfig;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @fileoverview Binary download module for Mandor CLI
3
+ * @description Handles downloading and caching Mandor binaries for the current platform
4
+ * @version 0.0.1
5
+ */
6
+
7
+ const https = require('https');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ /** @type {string} GitHub releases API URL */
13
+ const RELEASES_URL = 'https://api.github.com/repos/sanxzy/mandor/releases';
14
+
15
+ /**
16
+ * Downloads the Mandor binary for the specified platform and architecture
17
+ * @async
18
+ * @param {string} version - The Mandor version to download (e.g., '1.0.0', 'latest')
19
+ * @param {string} platform - Target platform (e.g., 'darwin', 'linux', 'win32')
20
+ * @param {string} arch - Target architecture (e.g., 'x64', 'arm64')
21
+ * @returns {Promise<string>} Path to the downloaded and executable binary
22
+ * @throws {Error} If download fails or platform is unsupported
23
+ * @example
24
+ * // Download Mandor v1.0.0 for macOS x64
25
+ * const binaryPath = await downloadBinary('1.0.0', 'darwin', 'x64');
26
+ * console.log(`Binary downloaded to: ${binaryPath}`);
27
+ */
28
+ async function downloadBinary(version, platform, arch) {
29
+ const filename = `mandor-${platform}-${arch}`;
30
+ const url = `${RELEASES_URL}/download/${version}/${filename}`;
31
+ const dest = path.join(os.homedir(), '.mandor', 'bin', filename);
32
+
33
+ // Download and make executable
34
+ return new Promise((resolve, reject) => {
35
+ https.get(url, (response) => {
36
+ if (response.statusCode === 302) {
37
+ return downloadBinary(response.headers.location, platform, arch);
38
+ }
39
+ const file = fs.createWriteStream(dest);
40
+ response.pipe(file);
41
+ file.on('finish', () => {
42
+ fs.chmodSync(dest, '755');
43
+ resolve(dest);
44
+ });
45
+ }).on('error', reject);
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Gets the platform identifier for the current system
51
+ * @returns {{platform: string, arch: string}} Platform and architecture info
52
+ * @example
53
+ * const { platform, arch } = getCurrentPlatform();
54
+ * console.log(`Running on ${platform}-${arch}`);
55
+ */
56
+ function getCurrentPlatform() {
57
+ const platform = os.platform(); // 'darwin', 'linux', 'win32'
58
+ const arch = os.arch(); // 'x64', 'arm64'
59
+ return { platform, arch };
60
+ }
61
+
62
+ /**
63
+ * Checks if a binary already exists and is up-to-date
64
+ * @param {string} version - Expected version
65
+ * @param {string} platform - Target platform
66
+ * @param {string} arch - Target architecture
67
+ * @returns {Promise<boolean>} True if binary exists and is valid
68
+ * @example
69
+ * const exists = await binaryExists('1.0.0', 'darwin', 'x64');
70
+ * if (exists) { console.log('Binary cached'); }
71
+ */
72
+ async function binaryExists(version, platform, arch) {
73
+ const filename = `mandor-${platform}-${arch}`;
74
+ const dest = path.join(os.homedir(), '.mandor', 'bin', filename);
75
+ return fs.existsSync(dest);
76
+ }
77
+
78
+ module.exports = {
79
+ downloadBinary,
80
+ getCurrentPlatform,
81
+ binaryExists,
82
+ RELEASES_URL
83
+ };
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @fileoverview Mandor CLI npm package entry point
3
+ * @description Main export for programmatic usage and CLI access
4
+ * @version 0.0.1
5
+ */
6
+
7
+ const Mandor = require('./api');
8
+ const MandorConfig = require('./config');
9
+ const { resolve, listCachedBinaries, clearCache } = require('./resolve');
10
+ const { downloadBinary, getCurrentPlatform } = require('./download');
11
+ const { install } = require('./install');
12
+
13
+ /**
14
+ * Main export object containing all public APIs
15
+ * @namespace mandor
16
+ * @version 0.0.1
17
+ * @example
18
+ * const mandor = require('@mandor/cli');
19
+ *
20
+ * // CLI access via npx or npm script
21
+ * // $ npx mandor init "My Project"
22
+ *
23
+ * // Programmatic usage
24
+ * const cli = new mandor.Mandor({ json: true });
25
+ * await cli.init('My Project');
26
+ */
27
+ module.exports = {
28
+ /**
29
+ * Mandor CLI wrapper class for programmatic access
30
+ * @type {typeof Mandor}
31
+ * @memberof mandor
32
+ * @example
33
+ * const mandor = require('@mandor/cli');
34
+ * const cli = new mandor.Mandor({ json: true });
35
+ */
36
+ Mandor,
37
+
38
+ /**
39
+ * Configuration management class
40
+ * @type {typeof MandorConfig}
41
+ * @memberof mandor
42
+ * @example
43
+ * const config = new mandor.MandorConfig('/path/to/project');
44
+ * const priority = config.get('priority.default', 'P3');
45
+ */
46
+ MandorConfig,
47
+
48
+ /**
49
+ * Resolves the Mandor binary path
50
+ * @function
51
+ * @param {Object} [options] - Resolution options
52
+ * @param {string} [options.version] - Version to use
53
+ * @returns {Promise<string>} Path to binary
54
+ * @memberof mandor
55
+ * @example
56
+ * const binaryPath = await mandor.resolve({ version: 'latest' });
57
+ */
58
+ resolve,
59
+
60
+ /**
61
+ * Lists all cached binary versions
62
+ * @function
63
+ * @returns {Object[]} Cached binary info
64
+ * @memberof mandor
65
+ * @example
66
+ * const cached = mandor.listCachedBinaries();
67
+ */
68
+ listCachedBinaries,
69
+
70
+ /**
71
+ * Clears all cached binaries
72
+ * @function
73
+ * @returns {number} Number of binaries removed
74
+ * @memberof mandor
75
+ * @example
76
+ * const removed = mandor.clearCache();
77
+ */
78
+ clearCache,
79
+
80
+ /**
81
+ * Downloads a Mandor binary
82
+ * @function
83
+ * @param {string} version - Version to download
84
+ * @param {string} [platform] - Target platform
85
+ * @param {string} [arch] - Target architecture
86
+ * @returns {Promise<string>} Path to downloaded binary
87
+ * @memberof mandor
88
+ * @example
89
+ * const binary = await mandor.downloadBinary('1.0.0', 'darwin', 'x64');
90
+ */
91
+ downloadBinary,
92
+
93
+ /**
94
+ * Gets current platform information
95
+ * @function
96
+ * @returns {{platform: string, arch: string}} Platform info
97
+ * @memberof mandor
98
+ * @example
99
+ * const { platform, arch } = mandor.getCurrentPlatform();
100
+ */
101
+ getCurrentPlatform,
102
+
103
+ /**
104
+ * Runs post-install setup
105
+ * @function
106
+ * @param {Object} [options] - Install options
107
+ * @returns {Promise<string>} Path to installed binary
108
+ * @memberof mandor
109
+ * @example
110
+ * await mandor.install({ version: 'latest' });
111
+ */
112
+ install,
113
+
114
+ // Version info
115
+ /** @type {string} Package version */
116
+ version: '0.0.1',
117
+
118
+ /** @type {string} Supported Mandor version range */
119
+ mandorVersionRange: '>=0.0.1'
120
+ };
121
+
122
+ // CLI entry point when bin/mandor is executed
123
+ if (require.main === module) {
124
+ resolve()
125
+ .then(binaryPath => {
126
+ const { spawn } = require('child_process');
127
+ const args = process.argv.slice(2);
128
+ const proc = spawn(binaryPath, args, {
129
+ stdio: 'inherit',
130
+ cwd: process.cwd()
131
+ });
132
+ proc.on('exit', process.exit);
133
+ })
134
+ .catch(error => {
135
+ console.error('Failed to start Mandor:', error.message);
136
+ process.exit(1);
137
+ });
138
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @fileoverview Post-install hook for Mandor CLI
3
+ * @description Handles binary download and caching during npm install
4
+ * @version 0.0.1
5
+ */
6
+
7
+ const { downloadBinary, getCurrentPlatform } = require('./download');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /** @type {string} Cache directory for binaries */
12
+ const CACHE_DIR = path.join(__dirname, '..', '.cache');
13
+
14
+ /**
15
+ * Installs the Mandor binary for the current platform
16
+ * @async
17
+ * @param {Object} [options] - Installation options
18
+ * @param {string} [options.version] - Version to install (default: 'latest')
19
+ * @returns {Promise<string>} Path to the installed binary
20
+ * @throws {Error} If download fails
21
+ * @example
22
+ * // Called automatically by npm postinstall
23
+ * await install();
24
+ */
25
+ async function install(options = {}) {
26
+ const version = options.version || 'latest';
27
+ const { platform, arch } = getCurrentPlatform();
28
+
29
+ console.log(`Installing Mandor ${version} for ${platform}-${arch}...`);
30
+
31
+ const binaryPath = await downloadBinary(version, platform, arch);
32
+ console.log(`✓ Mandor installed: ${binaryPath}`);
33
+
34
+ return binaryPath;
35
+ }
36
+
37
+ /**
38
+ * Cleans up old binary caches
39
+ * @returns {number} Number of files removed
40
+ * @example
41
+ * const removed = cleanupCache();
42
+ * console.log(`Removed ${removed} old binary files`);
43
+ */
44
+ function cleanupCache() {
45
+ if (!fs.existsSync(CACHE_DIR)) return 0;
46
+
47
+ const files = fs.readdirSync(CACHE_DIR);
48
+ let removed = 0;
49
+
50
+ for (const file of files) {
51
+ const filePath = path.join(CACHE_DIR, file);
52
+ const stats = fs.statSync(filePath);
53
+
54
+ // Remove files older than 30 days
55
+ const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
56
+ if (stats.mtimeMs < thirtyDaysAgo) {
57
+ fs.unlinkSync(filePath);
58
+ removed++;
59
+ }
60
+ }
61
+
62
+ return removed;
63
+ }
64
+
65
+ /**
66
+ * Gets the installed binary version
67
+ * @returns {string|null} Version string or null if not installed
68
+ * @example
69
+ * const version = getInstalledVersion();
70
+ * if (version) { console.log(`Using Mandor ${version}`); }
71
+ */
72
+ function getInstalledVersion() {
73
+ const versionPath = path.join(CACHE_DIR, 'version.txt');
74
+ if (fs.existsSync(versionPath)) {
75
+ return fs.readFileSync(versionPath, 'utf-8').trim();
76
+ }
77
+ return null;
78
+ }
79
+
80
+ // Run install on postinstall
81
+ if (require.main === module || process.env.npm_lifecycle_event === 'postinstall') {
82
+ install().catch(error => {
83
+ console.error('Failed to install Mandor:', error.message);
84
+ process.exit(1);
85
+ });
86
+ }
87
+
88
+ module.exports = {
89
+ install,
90
+ cleanupCache,
91
+ getInstalledVersion
92
+ };
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @fileoverview Version resolution module for Mandor CLI
3
+ * @description Resolves the correct binary path based on version and platform
4
+ * @version 0.0.1
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const os = require('os');
10
+ const { downloadBinary, getCurrentPlatform, binaryExists } = require('./download');
11
+
12
+ /** @type {string} Default version to use */
13
+ const DEFAULT_VERSION = 'latest';
14
+
15
+ /**
16
+ * Resolves the binary path for the requested version
17
+ * @async
18
+ * @param {Object} [options] - Resolution options
19
+ * @param {string} [options.version] - Requested version (default: 'latest')
20
+ * @param {boolean} [options.forceDownload] - Force re-download even if cached
21
+ * @returns {Promise<string>} Path to the Mandor binary
22
+ * @throws {Error} If binary cannot be resolved or downloaded
23
+ * @example
24
+ * const binaryPath = await resolve({ version: '1.0.0' });
25
+ * console.log(`Using: ${binaryPath}`);
26
+ */
27
+ async function resolve(options = {}) {
28
+ const version = options.version || DEFAULT_VERSION;
29
+ const { platform, arch } = getCurrentPlatform();
30
+
31
+ // Check cache first
32
+ const cachedPath = getCachedBinary(version, platform, arch);
33
+ if (cachedPath && !options.forceDownload) {
34
+ return cachedPath;
35
+ }
36
+
37
+ // Download if not cached
38
+ const binaryPath = await downloadBinary(version, platform, arch);
39
+ cacheBinary(binaryPath, version, platform, arch);
40
+
41
+ return binaryPath;
42
+ }
43
+
44
+ /**
45
+ * Gets the cached binary path for a specific version
46
+ * @param {string} version - Version to look for
47
+ * @param {string} platform - Target platform
48
+ * @param {string} arch - Target architecture
49
+ * @returns {string|null} Path to cached binary or null
50
+ * @example
51
+ * const cached = getCachedBinary('1.0.0', 'darwin', 'x64');
52
+ */
53
+ function getCachedBinary(version, platform, arch) {
54
+ const cacheDir = path.join(os.homedir(), '.mandor', 'bin');
55
+ const binaryName = platform === 'win32' ? 'mandor.exe' : 'mandor';
56
+ const binaryPath = path.join(cacheDir, `${version}-${platform}-${arch}`, binaryName);
57
+
58
+ if (fs.existsSync(binaryPath)) {
59
+ return binaryPath;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ /**
65
+ * Caches a binary for future use
66
+ * @param {string} binaryPath - Path to the binary
67
+ * @param {string} version - Version identifier
68
+ * @param {string} platform - Target platform
69
+ * @param {string} arch - Target architecture
70
+ * @returns {void}
71
+ * @example
72
+ * cacheBinary('/home/user/.mandor/bin/1.0.0-darwin-x64/mandor', '1.0.0', 'darwin', 'x64');
73
+ */
74
+ function cacheBinary(binaryPath, version, platform, arch) {
75
+ const cacheDir = path.join(os.homedir(), '.mandor', 'bin', `${version}-${platform}-${arch}`);
76
+ fs.mkdirSync(cacheDir, { recursive: true });
77
+
78
+ const binaryName = platform === 'win32' ? 'mandor.exe' : 'mandor';
79
+ const destPath = path.join(cacheDir, binaryName);
80
+
81
+ fs.copyFileSync(binaryPath, destPath);
82
+ fs.chmodSync(destPath, '755');
83
+ }
84
+
85
+ /**
86
+ * Lists all cached binary versions
87
+ * @returns {Object[]} Array of cached binary info
88
+ * @example
89
+ * const cached = listCachedBinaries();
90
+ * cached.forEach(b => console.log(`${b.version} (${b.platform}-${b.arch})`));
91
+ */
92
+ function listCachedBinaries() {
93
+ const cacheDir = path.join(os.homedir(), '.mandor', 'bin');
94
+
95
+ if (!fs.existsSync(cacheDir)) {
96
+ return [];
97
+ }
98
+
99
+ const versions = [];
100
+ const entries = fs.readdirSync(cacheDir);
101
+
102
+ for (const entry of entries) {
103
+ const entryPath = path.join(cacheDir, entry);
104
+ if (fs.statSync(entryPath).isDirectory()) {
105
+ const [version, platform, arch] = entry.split('-');
106
+ versions.push({ version, platform, arch, path: entryPath });
107
+ }
108
+ }
109
+
110
+ return versions;
111
+ }
112
+
113
+ /**
114
+ * Clears all cached binaries
115
+ * @returns {number} Number of binaries removed
116
+ * @example
117
+ * const removed = clearCache();
118
+ * console.log(`Cleared ${removed} cached binaries`);
119
+ */
120
+ function clearCache() {
121
+ const cacheDir = path.join(os.homedir(), '.mandor', 'bin');
122
+
123
+ if (!fs.existsSync(cacheDir)) {
124
+ return 0;
125
+ }
126
+
127
+ const entries = fs.readdirSync(cacheDir);
128
+ let removed = 0;
129
+
130
+ for (const entry of entries) {
131
+ const entryPath = path.join(cacheDir, entry);
132
+ if (fs.statSync(entryPath).isDirectory()) {
133
+ fs.rmSync(entryPath, { recursive: true, force: true });
134
+ removed++;
135
+ }
136
+ }
137
+
138
+ return removed;
139
+ }
140
+
141
+ module.exports = {
142
+ resolve,
143
+ getCachedBinary,
144
+ cacheBinary,
145
+ listCachedBinaries,
146
+ clearCache,
147
+ DEFAULT_VERSION
148
+ };