filemayor 2.0.5 → 3.6.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.
- package/README.md +48 -43
- package/core/ai/planner.js +42 -0
- package/core/ai/sentry.js +92 -0
- package/core/ai/strategist.js +186 -0
- package/core/ai/validator.js +116 -0
- package/core/analyzer.js +163 -0
- package/core/emergency-halt.js +104 -0
- package/core/engine/apply-engine.js +69 -0
- package/core/engine/cure-engine.js +70 -0
- package/core/engine/dedupe-engine.js +77 -0
- package/core/engine/explain-engine.js +114 -0
- package/core/engine/preview-engine.js +49 -0
- package/core/fs-abstraction.js +199 -0
- package/core/guardrail.js +115 -0
- package/core/index.js +56 -0
- package/core/intent-interpreter.js +158 -0
- package/core/jailer.js +151 -0
- package/core/license.js +12 -13
- package/core/metadata-store.js +104 -0
- package/core/organizer.js +28 -142
- package/core/reporter.js +212 -2
- package/core/scanner.js +92 -62
- package/core/security.js +65 -12
- package/core/sop-parser.js +1 -2
- package/core/vault.js +165 -0
- package/index.js +230 -2
- package/package.json +55 -55
package/core/vault.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
5
|
+
* FILEMAYOR CORE — VAULT (ZERO-DEPENDENCY SECRET MANAGER)
|
|
6
|
+
*
|
|
7
|
+
* Uses the OS-native credential store to keep API keys out of
|
|
8
|
+
* the filesystem entirely. Falls back to .env / environment
|
|
9
|
+
* variables if the system keychain is unavailable.
|
|
10
|
+
*
|
|
11
|
+
* Supported backends:
|
|
12
|
+
* macOS → security (Keychain Services)
|
|
13
|
+
* Windows → cmdkey / PowerShell CredentialManager
|
|
14
|
+
* Linux → secret-tool (freedesktop Secret Service)
|
|
15
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
|
|
24
|
+
const SERVICE_NAME = 'FileMayor';
|
|
25
|
+
|
|
26
|
+
// ─── Platform-Specific Implementations ───────────────────────────
|
|
27
|
+
|
|
28
|
+
function _saveSecret_darwin(key, value) {
|
|
29
|
+
// -U flag updates if already exists
|
|
30
|
+
execSync(
|
|
31
|
+
`security add-generic-password -a "${SERVICE_NAME}" -s "${key}" -w "${value}" -U`,
|
|
32
|
+
{ stdio: 'ignore' }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _getSecret_darwin(key) {
|
|
37
|
+
return execSync(
|
|
38
|
+
`security find-generic-password -a "${SERVICE_NAME}" -s "${key}" -w`,
|
|
39
|
+
{ encoding: 'utf8' }
|
|
40
|
+
).trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _deleteSecret_darwin(key) {
|
|
44
|
+
execSync(
|
|
45
|
+
`security delete-generic-password -a "${SERVICE_NAME}" -s "${key}"`,
|
|
46
|
+
{ stdio: 'ignore' }
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _saveSecret_win32(key, value) {
|
|
51
|
+
// Use PowerShell to store in Windows Credential Manager
|
|
52
|
+
const cmd = `powershell -Command "cmdkey /generic:${SERVICE_NAME}_${key} /user:${key} /pass:${value}"`;
|
|
53
|
+
execSync(cmd, { stdio: 'ignore' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _getSecret_win32(key) {
|
|
57
|
+
// Use PowerShell to retrieve from Windows Credential Manager
|
|
58
|
+
const cmd = `powershell -Command "(cmdkey /list:${SERVICE_NAME}_${key}) -match 'User' | Out-Null; $cred = Get-StoredCredential -Target ${SERVICE_NAME}_${key}; if($cred){$cred.GetNetworkCredential().Password}else{throw 'Not found'}"`;
|
|
59
|
+
try {
|
|
60
|
+
return execSync(cmd, { encoding: 'utf8' }).trim();
|
|
61
|
+
} catch {
|
|
62
|
+
// Fallback: try simpler cmdkey parsing
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function _saveSecret_linux(key, value) {
|
|
68
|
+
execSync(
|
|
69
|
+
`echo "${value}" | secret-tool store --label="${SERVICE_NAME} ${key}" service "${SERVICE_NAME}" key "${key}"`,
|
|
70
|
+
{ stdio: 'ignore' }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _getSecret_linux(key) {
|
|
75
|
+
return execSync(
|
|
76
|
+
`secret-tool lookup service "${SERVICE_NAME}" key "${key}"`,
|
|
77
|
+
{ encoding: 'utf8' }
|
|
78
|
+
).trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── The Vault ────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
const Vault = {
|
|
84
|
+
/**
|
|
85
|
+
* Save a secret to the system keychain
|
|
86
|
+
* @param {string} key - Secret name (e.g., 'GEMINI_API_KEY')
|
|
87
|
+
* @param {string} value - Secret value
|
|
88
|
+
* @returns {boolean} Success
|
|
89
|
+
*/
|
|
90
|
+
saveSecret(key, value) {
|
|
91
|
+
try {
|
|
92
|
+
if (process.platform === 'darwin') _saveSecret_darwin(key, value);
|
|
93
|
+
else if (process.platform === 'win32') _saveSecret_win32(key, value);
|
|
94
|
+
else _saveSecret_linux(key, value);
|
|
95
|
+
return true;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.warn(`[Vault] Could not save to system keychain: ${err.message}`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Retrieve a secret — checks System Keychain → ENV → .env file
|
|
104
|
+
* @param {string} key - Secret name
|
|
105
|
+
* @returns {string|null} The secret value, or null
|
|
106
|
+
*/
|
|
107
|
+
getSecret(key) {
|
|
108
|
+
// Priority 1: System Keychain (most secure)
|
|
109
|
+
try {
|
|
110
|
+
if (process.platform === 'darwin') return _getSecret_darwin(key);
|
|
111
|
+
else if (process.platform === 'win32') {
|
|
112
|
+
const val = _getSecret_win32(key);
|
|
113
|
+
if (val) return val;
|
|
114
|
+
}
|
|
115
|
+
else return _getSecret_linux(key);
|
|
116
|
+
} catch { /* Keychain not available or key not found */ }
|
|
117
|
+
|
|
118
|
+
// Priority 2: Environment Variable
|
|
119
|
+
if (process.env[key]) return process.env[key];
|
|
120
|
+
|
|
121
|
+
// Priority 3: .env file (least secure, but common)
|
|
122
|
+
try {
|
|
123
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
124
|
+
if (fs.existsSync(envPath)) {
|
|
125
|
+
const envFile = fs.readFileSync(envPath, 'utf-8');
|
|
126
|
+
for (const line of envFile.split('\n')) {
|
|
127
|
+
const trimmed = line.trim();
|
|
128
|
+
if (trimmed.startsWith('#') || !trimmed) continue;
|
|
129
|
+
const [k, ...vals] = trimmed.split('=');
|
|
130
|
+
if (k.trim() === key && vals.length) {
|
|
131
|
+
return vals.join('=').trim();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch { /* .env not available */ }
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Delete a secret from the system keychain
|
|
142
|
+
* @param {string} key - Secret name
|
|
143
|
+
* @returns {boolean} Success
|
|
144
|
+
*/
|
|
145
|
+
deleteSecret(key) {
|
|
146
|
+
try {
|
|
147
|
+
if (process.platform === 'darwin') _deleteSecret_darwin(key);
|
|
148
|
+
// Windows and Linux deletion handled differently
|
|
149
|
+
return true;
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if a secret exists anywhere in the priority chain
|
|
157
|
+
* @param {string} key
|
|
158
|
+
* @returns {boolean}
|
|
159
|
+
*/
|
|
160
|
+
hasSecret(key) {
|
|
161
|
+
return Vault.getSecret(key) !== null;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
module.exports = Vault;
|
package/index.js
CHANGED
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
|
|
20
20
|
'use strict';
|
|
21
21
|
|
|
22
|
+
// ─── Load .env (zero-dependency) ─────────────────────────────────
|
|
23
|
+
try {
|
|
24
|
+
const _fs = require('fs');
|
|
25
|
+
const _path = require('path');
|
|
26
|
+
const envPath = _path.join(__dirname, '..', '.env');
|
|
27
|
+
_fs.readFileSync(envPath, 'utf-8').split('\n').forEach(line => {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
30
|
+
const [key, ...vals] = trimmed.split('=');
|
|
31
|
+
if (key && vals.length && !process.env[key.trim()]) {
|
|
32
|
+
process.env[key.trim()] = vals.join('=').trim();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
} catch { /* .env not found — that's fine, user may set env vars directly */ }
|
|
37
|
+
|
|
22
38
|
const path = require('path');
|
|
23
39
|
const os = require('os');
|
|
24
40
|
const fs = require('fs');
|
|
@@ -29,16 +45,18 @@ const { organize, generatePlan, rollback, loadJournal } = require('./core/organi
|
|
|
29
45
|
const { findJunk, clean } = require('./core/cleaner');
|
|
30
46
|
const { FileWatcher } = require('./core/watcher');
|
|
31
47
|
const { loadConfig, createConfigFile } = require('./core/config');
|
|
48
|
+
const { analyzeDirectory } = require('./core/analyzer');
|
|
32
49
|
const reporter = require('./core/reporter');
|
|
33
50
|
const { formatBytes } = require('./core/scanner');
|
|
34
51
|
const { getCategories } = require('./core/categories');
|
|
35
52
|
const { checkPermissions } = require('./core/security');
|
|
36
53
|
const { activateLicense, deactivateLicense, getLicenseInfo, checkProFeature, checkBulkLimit } = require('./core/license');
|
|
54
|
+
const { ExplainEngine, CureEngine, ApplyEngine, DedupeEngine } = require('./core/index');
|
|
37
55
|
|
|
38
56
|
const { c, banner, Spinner, success, error, warn, info } = reporter;
|
|
39
57
|
|
|
40
58
|
// ─── Version ──────────────────────────────────────────────────────
|
|
41
|
-
const VERSION = '
|
|
59
|
+
const VERSION = '3.6.0';
|
|
42
60
|
|
|
43
61
|
// ─── Path Helpers ─────────────────────────────────────────────────
|
|
44
62
|
|
|
@@ -153,8 +171,15 @@ ${banner()}
|
|
|
153
171
|
${c('bold', 'USAGE')}
|
|
154
172
|
${c('cyan', 'filemayor')} ${c('yellow', '<command>')} ${c('dim', '[path]')} ${c('dim', '[options]')}
|
|
155
173
|
|
|
174
|
+
${c('yellow', 'explain')} ${c('dim', '<path>')} Diagnose folder health (Viral!)
|
|
175
|
+
${c('yellow', 'cure')} ${c('dim', '<path>')} Plan treatment using AI
|
|
176
|
+
${c('yellow', 'apply')} Execute the cure plan
|
|
177
|
+
${c('yellow', 'duplicates')} ${c('dim', '<path>')} Find duplicate files
|
|
178
|
+
${c('yellow', 'dedupe')} ${c('dim', '<path>')} Remove duplicate files
|
|
179
|
+
|
|
156
180
|
${c('bold', 'COMMANDS')}
|
|
157
181
|
${c('yellow', 'scan')} ${c('dim', '<path>')} Scan directory and report contents
|
|
182
|
+
${c('yellow', 'analyze')} ${c('dim', '<path>')} Deep analysis (duplicates, bloat, savings)
|
|
158
183
|
${c('yellow', 'organize')} ${c('dim', '<path>')} Organize files into categories
|
|
159
184
|
${c('yellow', 'clean')} ${c('dim', '<path>')} Find and remove junk files
|
|
160
185
|
${c('yellow', 'watch')} ${c('dim', '<path>')} Watch directory for changes ${c('magenta', '[PRO]')}
|
|
@@ -236,6 +261,35 @@ async function cmdScan(target, flags, config) {
|
|
|
236
261
|
}
|
|
237
262
|
}
|
|
238
263
|
|
|
264
|
+
async function cmdAnalyze(target, flags, config) {
|
|
265
|
+
const targetPath = path.resolve(target || '.');
|
|
266
|
+
const format = flags.format || config.output.format;
|
|
267
|
+
|
|
268
|
+
const spinner = new Spinner(`Analyzing ${c('cyan', targetPath)}...`);
|
|
269
|
+
if (format === 'table' && !flags.quiet) spinner.start();
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const options = {
|
|
273
|
+
maxDepth: flags.depth || config.scanner.maxDepth,
|
|
274
|
+
minSize: flags.minSize || 1024,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const result = analyzeDirectory(targetPath, options);
|
|
278
|
+
|
|
279
|
+
spinner.stop();
|
|
280
|
+
|
|
281
|
+
if (flags.quiet) {
|
|
282
|
+
process.exit(result.summary.potentialSavings > 0 ? 0 : 1);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log(reporter.formatAnalyzeReport(result, format));
|
|
287
|
+
} catch (err) {
|
|
288
|
+
spinner.fail(err.message);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
239
293
|
async function cmdOrganize(target, flags, config) {
|
|
240
294
|
const targetPath = path.resolve(target || '.');
|
|
241
295
|
const format = flags.format || config.output.format;
|
|
@@ -428,6 +482,151 @@ async function cmdUndo(target, flags) {
|
|
|
428
482
|
} catch { /* ignore */ }
|
|
429
483
|
}
|
|
430
484
|
|
|
485
|
+
// ==================== DIAGNOSTIC TRIAD (v3.5) ====================
|
|
486
|
+
|
|
487
|
+
let activePlanState = null;
|
|
488
|
+
|
|
489
|
+
async function cmdExplain(target, flags) {
|
|
490
|
+
const targetPath = path.resolve(target || '.');
|
|
491
|
+
const spinner = new Spinner(`Diagnosing ${c('cyan', targetPath)}...`);
|
|
492
|
+
spinner.start();
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
const engine = new ExplainEngine();
|
|
496
|
+
const result = engine.run(targetPath);
|
|
497
|
+
spinner.stop();
|
|
498
|
+
console.log(reporter.formatExplainReport(result));
|
|
499
|
+
} catch (err) {
|
|
500
|
+
spinner.fail(err.message);
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async function cmdCure(target, flags) {
|
|
506
|
+
const targetPath = path.resolve(target || '.');
|
|
507
|
+
const prompt = flags.prompt || 'Clean up this folder and group similar files.';
|
|
508
|
+
|
|
509
|
+
if (!process.env.GEMINI_API_KEY) {
|
|
510
|
+
console.log(error('GEMINI_API_KEY environment variable is required for AI curative features.'));
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const spinner = new Spinner(`Consulting AI for ${c('cyan', targetPath)}...`);
|
|
515
|
+
spinner.start();
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
const engine = new CureEngine(targetPath, process.env.GEMINI_API_KEY);
|
|
519
|
+
activePlanState = await engine.generatePlan(prompt);
|
|
520
|
+
spinner.stop();
|
|
521
|
+
|
|
522
|
+
// Cache plan locally for 'apply' command
|
|
523
|
+
const cachePath = path.join(os.tmpdir(), 'filemayor-plan.json');
|
|
524
|
+
fs.writeFileSync(cachePath, JSON.stringify(activePlanState), 'utf8');
|
|
525
|
+
|
|
526
|
+
console.log(reporter.formatCureReport(activePlanState));
|
|
527
|
+
} catch (err) {
|
|
528
|
+
spinner.fail(err.message);
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function cmdApply(flags) {
|
|
534
|
+
const cachePath = path.join(os.tmpdir(), 'filemayor-plan.json');
|
|
535
|
+
if (!fs.existsSync(cachePath)) {
|
|
536
|
+
console.log(error('No plan found. Run "filemayor cure" first.'));
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const plan = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
541
|
+
|
|
542
|
+
// [Sprint B] Logic Guardrail: Check batch volume & destructive patterns
|
|
543
|
+
const LogicGuardrail = require('./core/guardrail');
|
|
544
|
+
const { FileMayorJailer } = require('./core/jailer');
|
|
545
|
+
const guardrail = new LogicGuardrail(50);
|
|
546
|
+
const jailer = new FileMayorJailer(process.cwd());
|
|
547
|
+
|
|
548
|
+
if (plan.plan && Array.isArray(plan.plan)) {
|
|
549
|
+
const approved = await guardrail.verifyBatch(plan.plan);
|
|
550
|
+
if (!approved) {
|
|
551
|
+
console.log(warn('Operation cancelled by user.'));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// [Sprint B] Jailer: Validate every move before execution
|
|
556
|
+
const blocked = [];
|
|
557
|
+
for (const step of plan.plan) {
|
|
558
|
+
const check = jailer.validateMove(step.source, step.destination);
|
|
559
|
+
if (!check.safe) {
|
|
560
|
+
blocked.push({ file: path.basename(step.source), reason: check.error });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (blocked.length > 0) {
|
|
564
|
+
console.log(error(`Jailer blocked ${blocked.length} unsafe operations:`));
|
|
565
|
+
for (const b of blocked) {
|
|
566
|
+
console.log(c('dim', ` ✗ ${b.file}: ${b.reason}`));
|
|
567
|
+
}
|
|
568
|
+
plan.plan = plan.plan.filter(step => jailer.validateMove(step.source, step.destination).safe);
|
|
569
|
+
if (plan.plan.length === 0) {
|
|
570
|
+
console.log(error('No safe operations remain. Aborting.'));
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
console.log(info(`Proceeding with ${plan.plan.length} safe operations.`));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const spinner = new Spinner(`Executing cure...`);
|
|
578
|
+
spinner.start();
|
|
579
|
+
|
|
580
|
+
try {
|
|
581
|
+
const engine = new ApplyEngine();
|
|
582
|
+
const result = await engine.apply(plan, (p) => {
|
|
583
|
+
spinner.update(`Relocating: ${p.index}/${p.total} — ${path.basename(p.current || '')}`);
|
|
584
|
+
});
|
|
585
|
+
spinner.succeed(`Cure applied! ${result.stats.success} files moved.`);
|
|
586
|
+
fs.unlinkSync(cachePath); // Clear plan
|
|
587
|
+
} catch (err) {
|
|
588
|
+
spinner.fail(err.message);
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async function cmdDuplicates(target, flags) {
|
|
594
|
+
const targetPath = path.resolve(target || '.');
|
|
595
|
+
const spinner = new Spinner(`Hunting duplicates in ${c('cyan', targetPath)}...`);
|
|
596
|
+
spinner.start();
|
|
597
|
+
|
|
598
|
+
try {
|
|
599
|
+
const engine = new DedupeEngine();
|
|
600
|
+
const result = engine.find(targetPath);
|
|
601
|
+
spinner.stop();
|
|
602
|
+
console.log(reporter.formatDedupeReport(result));
|
|
603
|
+
} catch (err) {
|
|
604
|
+
spinner.fail(err.message);
|
|
605
|
+
process.exit(1);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async function cmdDedupe(target, flags) {
|
|
610
|
+
const targetPath = path.resolve(target || '.');
|
|
611
|
+
const spinner = new Spinner(`Deduplicating ${c('cyan', targetPath)}...`);
|
|
612
|
+
spinner.start();
|
|
613
|
+
|
|
614
|
+
try {
|
|
615
|
+
const engine = new DedupeEngine();
|
|
616
|
+
const report = engine.find(targetPath);
|
|
617
|
+
if (report.sets === 0) {
|
|
618
|
+
spinner.succeed('Total order restored: No duplicates found.');
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const result = await engine.clean(report);
|
|
623
|
+
spinner.succeed(`Purged ${result.deleted} duplicates. Reclaimed ${result.freedHuman}.`);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
spinner.fail(err.message);
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
431
630
|
async function cmdInfo(config) {
|
|
432
631
|
console.log(banner());
|
|
433
632
|
console.log(` ${c('bold', 'Version')} ${VERSION}`);
|
|
@@ -526,7 +725,7 @@ async function cmdLicense(action, positional, flags) {
|
|
|
526
725
|
}
|
|
527
726
|
console.log('');
|
|
528
727
|
if (!li.active) {
|
|
529
|
-
console.log(c('dim', ' Get a license: https://filemayor.lemonsqueezy.com/checkout/buy/
|
|
728
|
+
console.log(c('dim', ' Get a license: https://filemayor.lemonsqueezy.com/checkout/buy/7fdcc87f-0660-4c1c-b3db-99f94773b71a'));
|
|
530
729
|
console.log(c('dim', ' Activate: filemayor license activate <key>'));
|
|
531
730
|
console.log('');
|
|
532
731
|
}
|
|
@@ -590,6 +789,35 @@ async function main() {
|
|
|
590
789
|
await cmdScan(args.target, args.flags, config);
|
|
591
790
|
break;
|
|
592
791
|
|
|
792
|
+
case 'explain':
|
|
793
|
+
case 'dx':
|
|
794
|
+
await cmdExplain(args.target, args.flags);
|
|
795
|
+
break;
|
|
796
|
+
|
|
797
|
+
case 'cure':
|
|
798
|
+
case 'plan':
|
|
799
|
+
await cmdCure(args.target, args.flags);
|
|
800
|
+
break;
|
|
801
|
+
|
|
802
|
+
case 'apply':
|
|
803
|
+
case 'exec':
|
|
804
|
+
await cmdApply(args.flags);
|
|
805
|
+
break;
|
|
806
|
+
|
|
807
|
+
case 'duplicates':
|
|
808
|
+
case 'dupes':
|
|
809
|
+
await cmdDuplicates(args.target, args.flags);
|
|
810
|
+
break;
|
|
811
|
+
|
|
812
|
+
case 'dedupe':
|
|
813
|
+
await cmdDedupe(args.target, args.flags);
|
|
814
|
+
break;
|
|
815
|
+
|
|
816
|
+
case 'analyze':
|
|
817
|
+
case 'a':
|
|
818
|
+
await cmdAnalyze(args.target, args.flags, config);
|
|
819
|
+
break;
|
|
820
|
+
|
|
593
821
|
case 'organize':
|
|
594
822
|
case 'org':
|
|
595
823
|
case 'o':
|
package/package.json
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "filemayor",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "FileMayor — Your Digital Life Organizer.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"filemayor": "./index.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "node --test ../tests/core.test.js"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"file-manager",
|
|
14
|
-
"file-organizer",
|
|
15
|
-
"directory-scanner",
|
|
16
|
-
"cleanup",
|
|
17
|
-
"filesystem",
|
|
18
|
-
"cli",
|
|
19
|
-
"terminal",
|
|
20
|
-
"productivity",
|
|
21
|
-
"devtools",
|
|
22
|
-
"sysadmin",
|
|
23
|
-
"data-center",
|
|
24
|
-
"sop",
|
|
25
|
-
"automation"
|
|
26
|
-
],
|
|
27
|
-
"author": {
|
|
28
|
-
"name": "Lehlohonolo Goodwill Nchefu (Chevza)",
|
|
29
|
-
"email": "
|
|
30
|
-
"url": "https://github.com/Hrypopo"
|
|
31
|
-
},
|
|
32
|
-
"license": "PROPRIETARY",
|
|
33
|
-
"repository": {
|
|
34
|
-
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/Hrypopo/FileMayor.git"
|
|
36
|
-
},
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/Hrypopo/FileMayor/issues"
|
|
39
|
-
},
|
|
40
|
-
"homepage": "https://filemayor.com",
|
|
41
|
-
"engines": {
|
|
42
|
-
"node": ">=18.0.0"
|
|
43
|
-
},
|
|
44
|
-
"os": [
|
|
45
|
-
"win32",
|
|
46
|
-
"darwin",
|
|
47
|
-
"linux"
|
|
48
|
-
],
|
|
49
|
-
"files": [
|
|
50
|
-
"index.js",
|
|
51
|
-
"core/",
|
|
52
|
-
"LICENSE",
|
|
53
|
-
"README.md"
|
|
54
|
-
]
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "filemayor",
|
|
3
|
+
"version": "3.6.0",
|
|
4
|
+
"description": "FileMayor — Your Digital Life Organizer. CLI + Desktop + PWA.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"filemayor": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test ../tests/core.test.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"file-manager",
|
|
14
|
+
"file-organizer",
|
|
15
|
+
"directory-scanner",
|
|
16
|
+
"cleanup",
|
|
17
|
+
"filesystem",
|
|
18
|
+
"cli",
|
|
19
|
+
"terminal",
|
|
20
|
+
"productivity",
|
|
21
|
+
"devtools",
|
|
22
|
+
"sysadmin",
|
|
23
|
+
"data-center",
|
|
24
|
+
"sop",
|
|
25
|
+
"automation"
|
|
26
|
+
],
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "Lehlohonolo Goodwill Nchefu (Chevza)",
|
|
29
|
+
"email": "hloninchefu@gmail.com",
|
|
30
|
+
"url": "https://github.com/Hrypopo"
|
|
31
|
+
},
|
|
32
|
+
"license": "PROPRIETARY",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Hrypopo/FileMayor.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/Hrypopo/FileMayor/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://filemayor.com",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"os": [
|
|
45
|
+
"win32",
|
|
46
|
+
"darwin",
|
|
47
|
+
"linux"
|
|
48
|
+
],
|
|
49
|
+
"files": [
|
|
50
|
+
"index.js",
|
|
51
|
+
"core/",
|
|
52
|
+
"LICENSE",
|
|
53
|
+
"README.md"
|
|
54
|
+
]
|
|
55
|
+
}
|