g360-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.
- package/README.md +53 -0
- package/package.json +27 -0
- package/src/assets/components/G360DragModal.jsx +40 -0
- package/src/assets/components/G360Signature.jsx +14 -0
- package/src/assets/config/project-types.json +32 -0
- package/src/assets/config/skills.json +54 -0
- package/src/assets/engine/g360-data-validator.js +44 -0
- package/src/assets/engine/g360-engine.js +12 -0
- package/src/assets/engine/g360-field-mapper.js +35 -0
- package/src/assets/engine/g360-skill-audit.mjs +37 -0
- package/src/assets/engine/g360-skill-meta-evaluator.mjs +33 -0
- package/src/assets/snippets/snippets.json +24 -0
- package/src/assets/templates/python-cli/package.json +7 -0
- package/src/assets/templates/python-cli/src/core/skill.json +6 -0
- package/src/assets/templates/python-cli/src/main.py +13 -0
- package/src/assets/templates/vba-excel/src/Module_Main.bas +6 -0
- package/src/assets/templates/vba-excel/src/g360-datamap.bas +46 -0
- package/src/assets/templates/vba-excel/src/skill.json +9 -0
- package/src/assets/templates/web-pwa/app.js +1 -0
- package/src/assets/templates/web-pwa/index.html +24 -0
- package/src/assets/templates/web-pwa/package.json +5 -0
- package/src/assets/templates/web-pwa/styles.css +28 -0
- package/src/cli.js +74 -0
- package/src/commands/audit.js +43 -0
- package/src/commands/bring.js +57 -0
- package/src/commands/clean.js +65 -0
- package/src/commands/health.js +66 -0
- package/src/commands/init.js +63 -0
- package/src/commands/list.js +71 -0
- package/src/commands/present.js +54 -0
- package/src/commands/update.js +29 -0
- package/src/lib/assets.js +38 -0
- package/src/lib/auditor.js +67 -0
- package/src/lib/checksum.js +27 -0
- package/src/lib/config.js +23 -0
- package/src/lib/manifest.js +43 -0
- package/src/lib/offline.js +33 -0
- package/src/lib/presenter.js +24 -0
- package/src/lib/progress.js +35 -0
- package/src/lib/rollback.js +49 -0
- package/src/lib/theme.js +30 -0
- package/src/lib/validator.js +41 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { auditor } from '../lib/auditor.js';
|
|
5
|
+
|
|
6
|
+
export async function audit(projectPath, options) {
|
|
7
|
+
const { fix = false, verbose = false } = options;
|
|
8
|
+
const targetDir = path.join(process.cwd(), projectPath);
|
|
9
|
+
|
|
10
|
+
console.log(chalk.bold.cyan('\nš G360 Project Audit\n'));
|
|
11
|
+
console.log(chalk.gray(`Path: ${targetDir}\n`));
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(targetDir)) {
|
|
14
|
+
console.error(chalk.red(`ā Path not found: ${targetDir}`));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const results = await auditor.audit(targetDir, { verbose });
|
|
19
|
+
|
|
20
|
+
console.log(chalk.yellow('š Audit Results:'));
|
|
21
|
+
console.log(chalk.green(` Passed: ${results.passed}`));
|
|
22
|
+
console.log(chalk.red(` Failed: ${results.failed}`));
|
|
23
|
+
console.log(chalk.cyan(` Warnings: ${results.warnings}\n`));
|
|
24
|
+
|
|
25
|
+
if (results.issues.length > 0) {
|
|
26
|
+
console.log(chalk.bold.yellow('Issues Found:'));
|
|
27
|
+
results.issues.forEach(issue => {
|
|
28
|
+
const icon = issue.severity === 'error' ? chalk.red('ā') : chalk.yellow('!');
|
|
29
|
+
console.log(` ${icon} ${chalk.gray(issue.file)}: ${issue.message}`);
|
|
30
|
+
});
|
|
31
|
+
console.log();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (results.passed === results.total && results.warnings === 0) {
|
|
35
|
+
console.log(chalk.green('ā
Project is G360 compliant!\n'));
|
|
36
|
+
} else {
|
|
37
|
+
console.log(chalk.yellow('ā ļø Some issues need attention.\n'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (fix) {
|
|
41
|
+
console.log(chalk.cyan('Auto-fix not yet implemented.\n'));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { progress } from '../lib/progress.js';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
export async function bring(asset, options) {
|
|
10
|
+
const { path: targetPath = '.', dryRun = false, force = false } = options;
|
|
11
|
+
const targetDir = path.join(process.cwd(), targetPath);
|
|
12
|
+
const assetsPath = path.join(__dirname, '../assets');
|
|
13
|
+
|
|
14
|
+
console.log(chalk.bold.cyan('\nš¦ G360 Asset Manager\n'));
|
|
15
|
+
|
|
16
|
+
if (!asset || asset === 'all') {
|
|
17
|
+
console.log(chalk.yellow('Bringing all G360 assets...\n'));
|
|
18
|
+
return copyAssets(assetsPath, targetDir, dryRun, force);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const assetPath = path.join(assetsPath, asset);
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(assetPath)) {
|
|
24
|
+
console.error(chalk.red(`ā Asset "${asset}" not found.`));
|
|
25
|
+
console.log(chalk.gray('\nRun "g360 list assets" to see available assets.'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
await copyAssets(assetPath, targetDir, dryRun, force);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function copyAssets(src, dest, dryRun, force) {
|
|
33
|
+
if (dryRun) {
|
|
34
|
+
console.log(chalk.yellow('š DRY RUN - No files will be copied\n'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const progressBar = progress('Copying assets...');
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const items = fs.readdirSync(src);
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
const srcPath = path.join(src, item);
|
|
44
|
+
const destPath = path.join(dest, 'g360', item);
|
|
45
|
+
|
|
46
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
47
|
+
await fs.copy(srcPath, destPath, { overwrite: force });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
progressBar.stop();
|
|
52
|
+
console.log(chalk.green('\nā
Assets copied successfully!\n'));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
progressBar.stop();
|
|
55
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { manifest } from '../lib/manifest.js';
|
|
5
|
+
|
|
6
|
+
export async function clean(projectPath, options) {
|
|
7
|
+
const { dryRun = false, force = false } = options;
|
|
8
|
+
const targetDir = path.join(process.cwd(), projectPath);
|
|
9
|
+
|
|
10
|
+
console.log(chalk.bold.cyan('\nš§¹ G360 Clean\n'));
|
|
11
|
+
console.log(chalk.gray(`Path: ${targetDir}\n`));
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(targetDir)) {
|
|
14
|
+
console.error(chalk.red(`ā Path not found: ${targetDir}`));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const g360Dir = path.join(targetDir, 'g360');
|
|
19
|
+
if (!fs.existsSync(g360Dir)) {
|
|
20
|
+
console.log(chalk.yellow('No g360 assets found to clean.'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const assetsToClean = getAssetsToClean(g360Dir);
|
|
25
|
+
|
|
26
|
+
console.log(chalk.yellow('Assets to be removed:'));
|
|
27
|
+
assetsToClean.forEach(asset => console.log(chalk.gray(` - ${asset}`)));
|
|
28
|
+
console.log();
|
|
29
|
+
|
|
30
|
+
if (dryRun) {
|
|
31
|
+
console.log(chalk.yellow('š DRY RUN - No files will be removed\n'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!force) {
|
|
36
|
+
console.log(chalk.gray('Run with --force to confirm deletion.'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await fs.remove(g360Dir);
|
|
42
|
+
await manifest.remove(targetDir);
|
|
43
|
+
console.log(chalk.green('\nā
G360 assets cleaned successfully!\n'));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getAssetsToClean(dir, baseDir = dir) {
|
|
50
|
+
const items = [];
|
|
51
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
52
|
+
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const fullPath = path.join(dir, entry.name);
|
|
55
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
56
|
+
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
items.push(...getAssetsToClean(fullPath, baseDir));
|
|
59
|
+
} else {
|
|
60
|
+
items.push(relativePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return items;
|
|
65
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export async function health(options) {
|
|
9
|
+
const { verbose = false } = options;
|
|
10
|
+
|
|
11
|
+
console.log(chalk.bold.cyan('\nš„ G360 Health Check\n'));
|
|
12
|
+
|
|
13
|
+
const checks = [];
|
|
14
|
+
|
|
15
|
+
checks.push({
|
|
16
|
+
name: 'Node.js',
|
|
17
|
+
status: process.version ? 'ok' : 'error',
|
|
18
|
+
message: process.version
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const npmPath = process.env.PATH?.includes('npm') || true;
|
|
22
|
+
checks.push({
|
|
23
|
+
name: 'npm',
|
|
24
|
+
status: npmPath ? 'ok' : 'error',
|
|
25
|
+
message: npmPath ? 'Available' : 'Not found'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const assetsPath = path.join(__dirname, '../assets');
|
|
29
|
+
const hasAssets = fs.existsSync(assetsPath);
|
|
30
|
+
checks.push({
|
|
31
|
+
name: 'G360 Assets',
|
|
32
|
+
status: hasAssets ? 'ok' : 'warn',
|
|
33
|
+
message: hasAssets ? 'Installed' : 'Run g360 update'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const manifest = path.join(process.cwd(), 'g360-manifest.json');
|
|
37
|
+
const hasManifest = fs.existsSync(manifest);
|
|
38
|
+
checks.push({
|
|
39
|
+
name: 'Project Manifest',
|
|
40
|
+
status: hasManifest ? 'ok' : 'warn',
|
|
41
|
+
message: hasManifest ? 'Found' : 'Not in a G360 project'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
console.log(chalk.yellow('System Status:'));
|
|
45
|
+
checks.forEach(check => {
|
|
46
|
+
const icon = check.status === 'ok' ? chalk.green('ā') :
|
|
47
|
+
check.status === 'warn' ? chalk.yellow('!') : chalk.red('ā');
|
|
48
|
+
console.log(` ${icon} ${check.name}: ${chalk.gray(check.message)}`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const allOk = checks.every(c => c.status !== 'error');
|
|
52
|
+
console.log();
|
|
53
|
+
|
|
54
|
+
if (allOk) {
|
|
55
|
+
console.log(chalk.green('ā
System is healthy!\n'));
|
|
56
|
+
} else {
|
|
57
|
+
console.log(chalk.red('ā Some checks failed.\n'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (verbose) {
|
|
61
|
+
console.log(chalk.gray('\nAdditional Info:'));
|
|
62
|
+
console.log(` CWD: ${process.cwd()}`);
|
|
63
|
+
console.log(` Platform: ${process.platform}`);
|
|
64
|
+
console.log();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { manifest } from '../lib/manifest.js';
|
|
6
|
+
import { progress } from '../lib/progress.js';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
export async function init(name, options) {
|
|
11
|
+
const { template = 'web-pwa', dir = '.', dryRun = false, force = false } = options;
|
|
12
|
+
const targetDir = path.join(process.cwd(), dir, name);
|
|
13
|
+
|
|
14
|
+
console.log(chalk.bold.cyan('\nš G360 Project Initialization\n'));
|
|
15
|
+
console.log(`Project: ${chalk.yellow(name)}`);
|
|
16
|
+
console.log(`Template: ${chalk.blue(template)}`);
|
|
17
|
+
console.log(`Target: ${chalk.gray(targetDir)}\n`);
|
|
18
|
+
|
|
19
|
+
const templatesPath = path.join(__dirname, '../assets/templates');
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(templatesPath)) {
|
|
22
|
+
console.error(chalk.red('ā Templates not found. Run: g360 update'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const templateDir = path.join(templatesPath, template);
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(templateDir)) {
|
|
29
|
+
console.error(chalk.red(`ā Template "${template}" not found.`));
|
|
30
|
+
console.log(chalk.gray('\nAvailable templates:'));
|
|
31
|
+
const templates = fs.readdirSync(templatesPath);
|
|
32
|
+
templates.forEach(t => console.log(chalk.gray(` - ${t}`)));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (fs.existsSync(targetDir) && !force) {
|
|
37
|
+
console.error(chalk.red(`ā Directory "${name}" already exists. Use --force to overwrite.`));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (dryRun) {
|
|
42
|
+
console.log(chalk.yellow('š DRY RUN - No files will be created\n'));
|
|
43
|
+
console.log(chalk.gray(`Would create: ${targetDir}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const progressBar = progress('Creating project...');
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await fs.copy(templateDir, targetDir);
|
|
51
|
+
await manifest.init(targetDir, { name, template, version: '1.0.0' });
|
|
52
|
+
progressBar.stop();
|
|
53
|
+
|
|
54
|
+
console.log(chalk.green('\nā
Project created successfully!\n'));
|
|
55
|
+
console.log(chalk.gray('Next steps:'));
|
|
56
|
+
console.log(` ${chalk.cyan('cd')} ${name}`);
|
|
57
|
+
console.log(` ${chalk.cyan('g360 bring')}`);
|
|
58
|
+
console.log(` ${chalk.cyan('g360 present')}\n`);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
progressBar.stop();
|
|
61
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export async function list(type, options) {
|
|
9
|
+
const { json = false } = options;
|
|
10
|
+
const assetsPath = path.join(__dirname, '../assets');
|
|
11
|
+
|
|
12
|
+
console.log(chalk.bold.cyan('\nš G360 Assets\n'));
|
|
13
|
+
|
|
14
|
+
const assets = {
|
|
15
|
+
templates: [],
|
|
16
|
+
components: [],
|
|
17
|
+
skills: []
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const templatesPath = path.join(assetsPath, 'templates');
|
|
21
|
+
const componentsPath = path.join(assetsPath, 'components');
|
|
22
|
+
const skillsPath = path.join(assetsPath, 'skills');
|
|
23
|
+
const enginePath = path.join(assetsPath, 'engine');
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(templatesPath)) {
|
|
26
|
+
assets.templates = fs.readdirSync(templatesPath);
|
|
27
|
+
}
|
|
28
|
+
if (fs.existsSync(componentsPath)) {
|
|
29
|
+
assets.components = fs.readdirSync(componentsPath).filter(f => f.endsWith('.jsx') || f.endsWith('.js'));
|
|
30
|
+
}
|
|
31
|
+
if (fs.existsSync(skillsPath)) {
|
|
32
|
+
assets.skills = fs.readdirSync(skillsPath).filter(f => f.endsWith('.js') || f.endsWith('.mjs'));
|
|
33
|
+
}
|
|
34
|
+
if (fs.existsSync(enginePath)) {
|
|
35
|
+
assets.engine = fs.readdirSync(enginePath).filter(f => f.startsWith('g360-skill-'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (json) {
|
|
39
|
+
console.log(JSON.stringify(assets, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!type || type === 'all' || type === 'templates') {
|
|
44
|
+
console.log(chalk.bold.yellow('\nš Templates:'));
|
|
45
|
+
if (assets.templates.length) {
|
|
46
|
+
assets.templates.forEach(t => console.log(chalk.gray(` - ${t}`)));
|
|
47
|
+
} else {
|
|
48
|
+
console.log(chalk.gray(' No templates found'));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!type || type === 'all' || type === 'components') {
|
|
53
|
+
console.log(chalk.bold.yellow('\nš§© Components:'));
|
|
54
|
+
if (assets.components.length) {
|
|
55
|
+
assets.components.forEach(c => console.log(chalk.gray(` - ${c}`)));
|
|
56
|
+
} else {
|
|
57
|
+
console.log(chalk.gray(' No components found'));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!type || type === 'all' || type === 'skills') {
|
|
62
|
+
console.log(chalk.bold.yellow('\nā” Skills:'));
|
|
63
|
+
if (assets.engine.length) {
|
|
64
|
+
assets.engine.forEach(s => console.log(chalk.gray(` - ${s}`)));
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.gray(' No skills found'));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export async function present(projectPath, options) {
|
|
6
|
+
const { depth = 3 } = options;
|
|
7
|
+
const targetDir = path.join(process.cwd(), projectPath);
|
|
8
|
+
|
|
9
|
+
console.log(chalk.bold.cyan('\nšļø G360 Project Structure\n'));
|
|
10
|
+
console.log(chalk.gray(`Path: ${targetDir}\n`));
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(targetDir)) {
|
|
13
|
+
console.error(chalk.red(`ā Path not found: ${targetDir}`));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const manifestPath = path.join(targetDir, 'g360-manifest.json');
|
|
18
|
+
if (fs.existsSync(manifestPath)) {
|
|
19
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
20
|
+
console.log(chalk.yellow('š Project Info:'));
|
|
21
|
+
console.log(chalk.gray(` Name: ${manifest.name || 'Unknown'}`));
|
|
22
|
+
console.log(chalk.gray(` Template: ${manifest.template || 'Unknown'}`));
|
|
23
|
+
console.log(chalk.gray(` Version: ${manifest.version || 'Unknown'}\n`));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(chalk.yellow('š Structure:'));
|
|
27
|
+
printTree(targetDir, '', parseInt(depth), true);
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function printTree(dir, prefix, depth, isRoot) {
|
|
32
|
+
if (depth < 0) return;
|
|
33
|
+
|
|
34
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
35
|
+
const dirs = items.filter(i => i.isDirectory() && !i.name.startsWith('.') && i.name !== 'node_modules');
|
|
36
|
+
const files = items.filter(i => i.isFile() && !i.name.startsWith('.'));
|
|
37
|
+
|
|
38
|
+
dirs.forEach((dirItem, index) => {
|
|
39
|
+
const isLast = index === dirs.length - 1 && files.length === 0;
|
|
40
|
+
const connector = isLast ? 'āāā ' : 'āāā ';
|
|
41
|
+
console.log(chalk.cyan(`${prefix}${connector}${dirItem.name}/`));
|
|
42
|
+
|
|
43
|
+
const newPrefix = prefix + (isLast ? ' ' : 'ā ');
|
|
44
|
+
printTree(path.join(dir, dirItem.name), newPrefix, depth - 1, false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
files.forEach((fileItem, index) => {
|
|
48
|
+
const isLast = index === files.length - 1;
|
|
49
|
+
const connector = isLast ? 'āāā ' : 'āāā ';
|
|
50
|
+
const ext = path.extname(fileItem.name);
|
|
51
|
+
const color = ['.json', '.js', '.jsx', '.ts', '.tsx'].includes(ext) ? chalk.white : chalk.gray;
|
|
52
|
+
console.log(color(`${prefix}${connector}${fileItem.name}`));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
|
|
4
|
+
export async function update(options) {
|
|
5
|
+
const { check = false } = options;
|
|
6
|
+
|
|
7
|
+
console.log(chalk.bold.cyan('\nā¬ļø G360 Update\n'));
|
|
8
|
+
|
|
9
|
+
if (check) {
|
|
10
|
+
try {
|
|
11
|
+
const currentVersion = '1.0.0';
|
|
12
|
+
console.log(chalk.gray(`Current version: ${currentVersion}`));
|
|
13
|
+
console.log(chalk.gray('Check npm for latest version...'));
|
|
14
|
+
console.log(chalk.yellow('\nUse: npm install -g g360-cli@latest to update'));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(chalk.red(`Error checking version: ${error.message}`));
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
console.log(chalk.yellow('Installing latest g360-cli...\n'));
|
|
23
|
+
execSync('npm install -g g360-cli', { stdio: 'inherit' });
|
|
24
|
+
console.log(chalk.green('\nā
g360-cli updated successfully!\n'));
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error(chalk.red('\nā Update failed. Try:'));
|
|
27
|
+
console.log(chalk.gray(' npm install -g g360-cli@latest\n'));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
export const assets = {
|
|
8
|
+
path: path.join(__dirname, '../assets'),
|
|
9
|
+
|
|
10
|
+
exists(assetPath) {
|
|
11
|
+
return fs.existsSync(path.join(this.path, assetPath));
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async copy(assetPath, destPath, options = {}) {
|
|
15
|
+
const { overwrite = false } = options;
|
|
16
|
+
const src = path.join(this.path, assetPath);
|
|
17
|
+
const dest = path.join(destPath, path.basename(assetPath));
|
|
18
|
+
|
|
19
|
+
if (!this.exists(assetPath)) {
|
|
20
|
+
throw new Error(`Asset not found: ${assetPath}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (fs.existsSync(dest) && !overwrite) {
|
|
24
|
+
throw new Error(`Destination already exists: ${dest}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await fs.copy(src, dest, { overwrite });
|
|
28
|
+
return dest;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
list(category) {
|
|
32
|
+
const categoryPath = path.join(this.path, category);
|
|
33
|
+
if (!fs.existsSync(categoryPath)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
return fs.readdirSync(categoryPath);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export const auditor = {
|
|
6
|
+
async audit(projectDir, options = {}) {
|
|
7
|
+
const { verbose = false } = options;
|
|
8
|
+
const results = {
|
|
9
|
+
passed: 0,
|
|
10
|
+
failed: 0,
|
|
11
|
+
warnings: 0,
|
|
12
|
+
total: 0,
|
|
13
|
+
issues: []
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const checks = [
|
|
17
|
+
{ name: 'manifest', check: () => this.checkManifest(projectDir) },
|
|
18
|
+
{ name: 'structure', check: () => this.checkStructure(projectDir) },
|
|
19
|
+
{ name: 'config', check: () => this.checkConfig(projectDir) }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
for (const { name, check } of checks) {
|
|
23
|
+
const result = await check();
|
|
24
|
+
results.total++;
|
|
25
|
+
|
|
26
|
+
if (result.status === 'pass') {
|
|
27
|
+
results.passed++;
|
|
28
|
+
} else if (result.status === 'fail') {
|
|
29
|
+
results.failed++;
|
|
30
|
+
results.issues.push({ ...result.issue, severity: 'error' });
|
|
31
|
+
} else if (result.status === 'warn') {
|
|
32
|
+
results.warnings++;
|
|
33
|
+
results.issues.push({ ...result.issue, severity: 'warning' });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (verbose && result.details) {
|
|
37
|
+
console.log(chalk.gray(` ${name}: ${result.details}`));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return results;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
checkManifest(projectDir) {
|
|
45
|
+
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
46
|
+
if (fs.existsSync(manifestPath)) {
|
|
47
|
+
return { status: 'pass', details: 'Manifest found' };
|
|
48
|
+
}
|
|
49
|
+
return { status: 'warn', issue: { file: 'g360-manifest.json', message: 'No manifest found' } };
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
checkStructure(projectDir) {
|
|
53
|
+
const g360Dir = path.join(projectDir, 'g360');
|
|
54
|
+
if (fs.existsSync(g360Dir)) {
|
|
55
|
+
return { status: 'pass', details: 'G360 assets directory found' };
|
|
56
|
+
}
|
|
57
|
+
return { status: 'warn', issue: { file: 'g360/', message: 'No G360 assets found' } };
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
checkConfig(projectDir) {
|
|
61
|
+
const configPath = path.join(projectDir, 'g360', 'config');
|
|
62
|
+
if (fs.existsSync(configPath)) {
|
|
63
|
+
return { status: 'pass', details: 'Config directory found' };
|
|
64
|
+
}
|
|
65
|
+
return { status: 'pass' };
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
export const checksum = {
|
|
5
|
+
async calculate(filePath) {
|
|
6
|
+
const content = await fs.readFile(filePath);
|
|
7
|
+
return crypto.createHash('md5').update(content).digest('hex');
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
async verify(filePath, expectedHash) {
|
|
11
|
+
const actualHash = await this.calculate(filePath);
|
|
12
|
+
return actualHash === expectedHash;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
async generateManifest(dir, files = []) {
|
|
16
|
+
const manifest = {};
|
|
17
|
+
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const filePath = `${dir}/${file}`;
|
|
20
|
+
if (fs.existsSync(filePath)) {
|
|
21
|
+
manifest[file] = await this.calculate(filePath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return manifest;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const config = {
|
|
2
|
+
defaults: {
|
|
3
|
+
template: 'web-pwa',
|
|
4
|
+
assets: ['components', 'skills', 'engine'],
|
|
5
|
+
theme: 'cool-light'
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
projectTypes: {
|
|
9
|
+
'web-pwa': { framework: 'vanilla', features: ['pwa', 'offline'] },
|
|
10
|
+
'web-svelte': { framework: 'svelte', features: ['routing', 'stores'] },
|
|
11
|
+
'python-cli': { framework: 'python', features: ['cli', 'argparse'] },
|
|
12
|
+
'vba-excel': { framework: 'vba', features: ['excel', 'macros'] }
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
themes: {
|
|
16
|
+
'cool-light': { primary: '#3B82F6', secondary: '#10B981', accent: '#8B5CF6' },
|
|
17
|
+
'cool-dark': { primary: '#60A5FA', secondary: '#34D399', accent: '#A78BFA' },
|
|
18
|
+
'warm-light': { primary: '#F59E0B', secondary: '#EF4444', accent: '#8B5CF6' },
|
|
19
|
+
'warm-dark': { primary: '#FBBF24', secondary: '#F87171', accent: '#A78BFA' },
|
|
20
|
+
'neutral-light': { primary: '#6B7280', secondary: '#374151', accent: '#111827' },
|
|
21
|
+
'neutral-dark': { primary: '#9CA3AF', secondary: '#D1D5DB', accent: '#F9FAFB' }
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export const manifest = {
|
|
5
|
+
async init(projectDir, data) {
|
|
6
|
+
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
7
|
+
const manifest = {
|
|
8
|
+
name: data.name,
|
|
9
|
+
template: data.template,
|
|
10
|
+
version: data.version,
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
assets: []
|
|
13
|
+
};
|
|
14
|
+
await fs.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
15
|
+
return manifest;
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async load(projectDir) {
|
|
19
|
+
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
20
|
+
if (fs.existsSync(manifestPath)) {
|
|
21
|
+
return fs.readJson(manifestPath);
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
async addAsset(projectDir, asset) {
|
|
27
|
+
const data = await this.load(projectDir);
|
|
28
|
+
if (data) {
|
|
29
|
+
data.assets.push({
|
|
30
|
+
...asset,
|
|
31
|
+
addedAt: new Date().toISOString()
|
|
32
|
+
});
|
|
33
|
+
await fs.writeJson(path.join(projectDir, 'g360-manifest.json'), data, { spaces: 2 });
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async remove(projectDir) {
|
|
38
|
+
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
39
|
+
if (fs.existsSync(manifestPath)) {
|
|
40
|
+
await fs.remove(manifestPath);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export const offline = {
|
|
5
|
+
cache: new Map(),
|
|
6
|
+
cacheDir: '.g360-cache',
|
|
7
|
+
|
|
8
|
+
async isAvailable() {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
async getCached(asset) {
|
|
13
|
+
return this.cache.get(asset);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async setCache(asset, data) {
|
|
17
|
+
this.cache.set(asset, data);
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async loadFromCache(asset) {
|
|
21
|
+
const cachePath = path.join(this.cacheDir, `${asset}.json`);
|
|
22
|
+
if (fs.existsSync(cachePath)) {
|
|
23
|
+
return fs.readJson(cachePath);
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async saveToCache(asset, data) {
|
|
29
|
+
const cachePath = path.join(this.cacheDir, `${asset}.json`);
|
|
30
|
+
await fs.ensureDir(this.cacheDir);
|
|
31
|
+
await fs.writeJson(cachePath, data);
|
|
32
|
+
}
|
|
33
|
+
};
|