ai-conventions-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/dist/download.js +78 -0
- package/dist/generate.js +133 -0
- package/dist/index.js +35 -0
- package/dist/prompt.js +56 -0
- package/package.json +35 -0
package/dist/download.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
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.downloadTemplates = downloadTemplates;
|
|
40
|
+
const axios_1 = __importDefault(require("axios"));
|
|
41
|
+
const tar = __importStar(require("tar"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const ora_1 = __importDefault(require("ora"));
|
|
46
|
+
const TARBALL_URL = 'https://github.com/sso0o/ai-conventions/archive/refs/heads/master.tar.gz';
|
|
47
|
+
async function downloadTemplates() {
|
|
48
|
+
const spinner = (0, ora_1.default)('템플릿 다운로드 중...').start();
|
|
49
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-conventions-'));
|
|
50
|
+
const tarballPath = path.join(tmpDir, 'templates.tar.gz');
|
|
51
|
+
const extractDir = path.join(tmpDir, 'extracted');
|
|
52
|
+
try {
|
|
53
|
+
const response = await axios_1.default.get(TARBALL_URL, {
|
|
54
|
+
responseType: 'stream',
|
|
55
|
+
timeout: 30000,
|
|
56
|
+
});
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
const writer = fs.createWriteStream(tarballPath);
|
|
59
|
+
response.data.pipe(writer);
|
|
60
|
+
writer.on('finish', resolve);
|
|
61
|
+
writer.on('error', reject);
|
|
62
|
+
});
|
|
63
|
+
spinner.text = '압축 해제 중...';
|
|
64
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
65
|
+
await tar.extract({
|
|
66
|
+
file: tarballPath,
|
|
67
|
+
cwd: extractDir,
|
|
68
|
+
strip: 1,
|
|
69
|
+
});
|
|
70
|
+
fs.unlinkSync(tarballPath);
|
|
71
|
+
spinner.succeed('템플릿 다운로드 완료');
|
|
72
|
+
return extractDir;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
spinner.fail('템플릿 다운로드 실패');
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
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.generateFiles = generateFiles;
|
|
40
|
+
const fse = __importStar(require("fs-extra"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
async function generateFiles(answers, templateDir, outputDir) {
|
|
44
|
+
const docsDir = path.join(outputDir, 'docs');
|
|
45
|
+
const copiedFiles = [];
|
|
46
|
+
fse.ensureDirSync(docsDir);
|
|
47
|
+
copyCommon(templateDir, docsDir, copiedFiles);
|
|
48
|
+
if (answers.frontend !== 'none') {
|
|
49
|
+
copyFrontend(answers, templateDir, docsDir, copiedFiles);
|
|
50
|
+
}
|
|
51
|
+
if (answers.backend !== 'none') {
|
|
52
|
+
copyBackend(answers, templateDir, docsDir, copiedFiles);
|
|
53
|
+
}
|
|
54
|
+
const conventionContent = buildConventionContent(copiedFiles);
|
|
55
|
+
fse.writeFileSync(path.join(outputDir, 'CLAUDE.md'), conventionContent, 'utf-8');
|
|
56
|
+
fse.writeFileSync(path.join(outputDir, 'AGENTS.md'), conventionContent, 'utf-8');
|
|
57
|
+
console.log(chalk_1.default.green('\n생성된 파일:'));
|
|
58
|
+
copiedFiles.forEach((f) => console.log(chalk_1.default.gray(` - ${f}`)));
|
|
59
|
+
console.log(chalk_1.default.gray(' - CLAUDE.md'));
|
|
60
|
+
console.log(chalk_1.default.gray(' - AGENTS.md'));
|
|
61
|
+
}
|
|
62
|
+
function copyCommon(templateDir, docsDir, copiedFiles) {
|
|
63
|
+
const src = path.join(templateDir, 'templates', 'common');
|
|
64
|
+
if (!fse.pathExistsSync(src))
|
|
65
|
+
return;
|
|
66
|
+
for (const file of fse.readdirSync(src)) {
|
|
67
|
+
const srcPath = path.join(src, file);
|
|
68
|
+
if (fse.statSync(srcPath).isFile()) {
|
|
69
|
+
fse.copySync(srcPath, path.join(docsDir, file));
|
|
70
|
+
copiedFiles.push(`docs/${file}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function copyFrontend(answers, templateDir, docsDir, copiedFiles) {
|
|
75
|
+
const stackSrc = path.join(templateDir, 'templates', 'frontend', answers.frontend);
|
|
76
|
+
const dst = path.join(docsDir, 'frontend');
|
|
77
|
+
fse.ensureDirSync(dst);
|
|
78
|
+
const archPath = path.join(stackSrc, 'architecture', answers.frontendArchitecture, 'folder-structure.md');
|
|
79
|
+
const fallbackPath = path.join(stackSrc, 'architecture', 'layered', 'folder-structure.md');
|
|
80
|
+
const resolvedArch = fse.pathExistsSync(archPath) ? archPath : fallbackPath;
|
|
81
|
+
if (fse.pathExistsSync(resolvedArch)) {
|
|
82
|
+
fse.copySync(resolvedArch, path.join(dst, 'folder-structure.md'));
|
|
83
|
+
copiedFiles.push('docs/frontend/folder-structure.md');
|
|
84
|
+
}
|
|
85
|
+
// All .md files directly inside the stack dir (skip architecture/ subdir)
|
|
86
|
+
for (const file of fse.readdirSync(stackSrc)) {
|
|
87
|
+
const srcPath = path.join(stackSrc, file);
|
|
88
|
+
if (fse.statSync(srcPath).isFile() && file.endsWith('.md')) {
|
|
89
|
+
fse.copySync(srcPath, path.join(dst, file));
|
|
90
|
+
copiedFiles.push(`docs/frontend/${file}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function copyBackend(answers, templateDir, docsDir, copiedFiles) {
|
|
95
|
+
const stackSrc = path.join(templateDir, 'templates', 'backend', answers.backend);
|
|
96
|
+
const dst = path.join(docsDir, 'backend');
|
|
97
|
+
fse.ensureDirSync(dst);
|
|
98
|
+
const archPath = path.join(stackSrc, 'architecture', answers.backendArchitecture, 'folder-structure.md');
|
|
99
|
+
if (fse.pathExistsSync(archPath)) {
|
|
100
|
+
fse.copySync(archPath, path.join(dst, 'folder-structure.md'));
|
|
101
|
+
copiedFiles.push('docs/backend/folder-structure.md');
|
|
102
|
+
}
|
|
103
|
+
// All .md files directly inside the stack dir (skip architecture/ subdir)
|
|
104
|
+
for (const file of fse.readdirSync(stackSrc)) {
|
|
105
|
+
const srcPath = path.join(stackSrc, file);
|
|
106
|
+
if (fse.statSync(srcPath).isFile() && file.endsWith('.md')) {
|
|
107
|
+
fse.copySync(srcPath, path.join(dst, file));
|
|
108
|
+
copiedFiles.push(`docs/backend/${file}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function buildConventionContent(files) {
|
|
113
|
+
const commonFiles = files.filter((f) => !f.startsWith('docs/frontend/') && !f.startsWith('docs/backend/'));
|
|
114
|
+
const frontendFiles = files.filter((f) => f.startsWith('docs/frontend/'));
|
|
115
|
+
const backendFiles = files.filter((f) => f.startsWith('docs/backend/'));
|
|
116
|
+
const lines = ['# Convention Rules', ''];
|
|
117
|
+
if (commonFiles.length > 0) {
|
|
118
|
+
lines.push('## Common');
|
|
119
|
+
commonFiles.forEach((f) => lines.push(`- @${f}`));
|
|
120
|
+
lines.push('');
|
|
121
|
+
}
|
|
122
|
+
if (frontendFiles.length > 0) {
|
|
123
|
+
lines.push('## Frontend');
|
|
124
|
+
frontendFiles.forEach((f) => lines.push(`- @${f}`));
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
if (backendFiles.length > 0) {
|
|
128
|
+
lines.push('## Backend');
|
|
129
|
+
backendFiles.forEach((f) => lines.push(`- @${f}`));
|
|
130
|
+
lines.push('');
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
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 chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompt_1 = require("./prompt");
|
|
10
|
+
const download_1 = require("./download");
|
|
11
|
+
const generate_1 = require("./generate");
|
|
12
|
+
const program = new commander_1.Command();
|
|
13
|
+
program
|
|
14
|
+
.name('ai-conventions')
|
|
15
|
+
.description('Generate convention files from ai-conventions templates')
|
|
16
|
+
.version('1.0.0');
|
|
17
|
+
program
|
|
18
|
+
.command('init')
|
|
19
|
+
.description('Initialize convention files in current project')
|
|
20
|
+
.action(async () => {
|
|
21
|
+
console.log(chalk_1.default.bold.blue('\nAI Conventions CLI\n'));
|
|
22
|
+
try {
|
|
23
|
+
const answers = await (0, prompt_1.prompt)();
|
|
24
|
+
const templateDir = await (0, download_1.downloadTemplates)();
|
|
25
|
+
await (0, generate_1.generateFiles)(answers, templateDir, process.cwd());
|
|
26
|
+
console.log(chalk_1.default.bold.green('\nConvention files generated successfully!\n'));
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
console.error(chalk_1.default.red(`\nError: ${error.message}\n`));
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
program.parse(process.argv);
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.prompt = prompt;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
async function prompt() {
|
|
9
|
+
const raw = await inquirer_1.default.prompt([
|
|
10
|
+
{
|
|
11
|
+
type: 'list',
|
|
12
|
+
name: 'frontend',
|
|
13
|
+
message: '프론트엔드 스택을 선택하세요:',
|
|
14
|
+
choices: [
|
|
15
|
+
{ name: 'Vite', value: 'vite' },
|
|
16
|
+
{ name: 'Next.js', value: 'nextjs' },
|
|
17
|
+
{ name: '없음', value: 'none' },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: 'list',
|
|
22
|
+
name: 'frontendArchitecture',
|
|
23
|
+
message: '프론트엔드 아키텍처를 선택하세요:',
|
|
24
|
+
choices: [
|
|
25
|
+
{ name: 'Layered Architecture', value: 'layered' },
|
|
26
|
+
{ name: 'Feature-Slice Design', value: 'feature-slice' },
|
|
27
|
+
],
|
|
28
|
+
when: (answers) => answers.frontend !== 'none',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'list',
|
|
32
|
+
name: 'backend',
|
|
33
|
+
message: '백엔드 스택을 선택하세요:',
|
|
34
|
+
choices: [
|
|
35
|
+
{ name: 'Spring Boot', value: 'spring-boot' },
|
|
36
|
+
{ name: 'NestJS', value: 'nestjs' },
|
|
37
|
+
{ name: '없음', value: 'none' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'list',
|
|
42
|
+
name: 'backendArchitecture',
|
|
43
|
+
message: '백엔드 아키텍처를 선택하세요:',
|
|
44
|
+
choices: [
|
|
45
|
+
{ name: 'Layered Architecture', value: 'layered' },
|
|
46
|
+
{ name: 'Clean Architecture', value: 'clean' },
|
|
47
|
+
],
|
|
48
|
+
when: (answers) => answers.backend !== 'none',
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
return {
|
|
52
|
+
...raw,
|
|
53
|
+
frontendArchitecture: raw.frontendArchitecture ?? 'layered',
|
|
54
|
+
backendArchitecture: raw.backendArchitecture ?? 'layered',
|
|
55
|
+
};
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-conventions-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI to generate convention files from ai-conventions templates",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-conventions": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"axios": "^1.6.0",
|
|
20
|
+
"chalk": "^4.1.2",
|
|
21
|
+
"commander": "^11.1.0",
|
|
22
|
+
"fs-extra": "^11.2.0",
|
|
23
|
+
"inquirer": "^8.2.6",
|
|
24
|
+
"ora": "^5.4.1",
|
|
25
|
+
"tar": "^7.5.15"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/fs-extra": "^11.0.4",
|
|
29
|
+
"@types/inquirer": "^8.2.10",
|
|
30
|
+
"@types/node": "^20.10.0",
|
|
31
|
+
"@types/tar": "^6.1.11",
|
|
32
|
+
"ts-node": "^10.9.2",
|
|
33
|
+
"typescript": "^5.3.2"
|
|
34
|
+
}
|
|
35
|
+
}
|