@aicgen/aicgen 1.0.0-beta.1 → 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/{.claude/guidelines → .agent/rules}/api-design.md +5 -1
- package/{.claude/guidelines → .agent/rules}/architecture.md +5 -1
- package/{.claude/guidelines → .agent/rules}/best-practices.md +5 -1
- package/{.claude/guidelines → .agent/rules}/code-style.md +5 -1
- package/{.claude/guidelines → .agent/rules}/design-patterns.md +5 -1
- package/{.claude/guidelines → .agent/rules}/devops.md +5 -1
- package/{.claude/guidelines → .agent/rules}/error-handling.md +5 -1
- package/.agent/rules/instructions.md +28 -0
- package/{.claude/guidelines → .agent/rules}/language.md +5 -1
- package/{.claude/guidelines → .agent/rules}/performance.md +5 -1
- package/{.claude/guidelines → .agent/rules}/security.md +5 -1
- package/{.claude/guidelines → .agent/rules}/testing.md +5 -1
- package/.agent/workflows/add-documentation.md +10 -0
- package/.agent/workflows/generate-integration-tests.md +10 -0
- package/.agent/workflows/generate-unit-tests.md +11 -0
- package/.agent/workflows/performance-audit.md +11 -0
- package/.agent/workflows/refactor-extract-module.md +12 -0
- package/.agent/workflows/security-audit.md +12 -0
- package/.gemini/instructions.md +4843 -0
- package/.vs/ProjectSettings.json +2 -2
- package/.vs/VSWorkspaceState.json +15 -15
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +53 -53
- package/AGENTS.md +9 -11
- package/assets/icon.svg +33 -33
- package/bun.lock +734 -26
- package/{CLAUDE.md → claude.md} +2 -2
- package/config.example.yml +129 -0
- package/config.yml +38 -0
- package/data/architecture/microservices/api-gateway.md +56 -56
- package/data/devops/observability.md +73 -73
- package/data/guideline-mappings.yml +128 -0
- package/data/language/dart/async.md +289 -0
- package/data/language/dart/basics.md +280 -0
- package/data/language/dart/error-handling.md +355 -0
- package/data/language/dart/index.md +10 -0
- package/data/language/dart/testing.md +352 -0
- package/data/language/swift/basics.md +477 -0
- package/data/language/swift/concurrency.md +654 -0
- package/data/language/swift/error-handling.md +679 -0
- package/data/language/swift/swiftui-mvvm.md +795 -0
- package/data/language/swift/testing.md +708 -0
- package/data/version.json +10 -8
- package/dist/index.js +50153 -28959
- package/jest.config.js +46 -0
- package/package.json +14 -3
- package/.claude/agents/architecture-reviewer.md +0 -88
- package/.claude/agents/guideline-checker.md +0 -73
- package/.claude/agents/security-auditor.md +0 -108
- package/.claude/settings.json +0 -98
- package/.claude/settings.local.json +0 -8
- package/.eslintrc.json +0 -28
- package/.github/workflows/release.yml +0 -180
- package/.github/workflows/test.yml +0 -81
- package/CONTRIBUTING.md +0 -821
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -46
- package/dist/commands/init.js.map +0 -1
- package/dist/config/profiles.d.ts +0 -4
- package/dist/config/profiles.d.ts.map +0 -1
- package/dist/config/profiles.js +0 -30
- package/dist/config/profiles.js.map +0 -1
- package/dist/config/settings.d.ts +0 -7
- package/dist/config/settings.d.ts.map +0 -1
- package/dist/config/settings.js +0 -7
- package/dist/config/settings.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/models/guideline.d.ts +0 -15
- package/dist/models/guideline.d.ts.map +0 -1
- package/dist/models/guideline.js +0 -2
- package/dist/models/guideline.js.map +0 -1
- package/dist/models/preference.d.ts +0 -9
- package/dist/models/preference.d.ts.map +0 -1
- package/dist/models/preference.js +0 -2
- package/dist/models/preference.js.map +0 -1
- package/dist/models/profile.d.ts +0 -9
- package/dist/models/profile.d.ts.map +0 -1
- package/dist/models/profile.js +0 -2
- package/dist/models/profile.js.map +0 -1
- package/dist/models/project.d.ts +0 -13
- package/dist/models/project.d.ts.map +0 -1
- package/dist/models/project.js +0 -2
- package/dist/models/project.js.map +0 -1
- package/dist/services/ai/anthropic.d.ts +0 -7
- package/dist/services/ai/anthropic.d.ts.map +0 -1
- package/dist/services/ai/anthropic.js +0 -39
- package/dist/services/ai/anthropic.js.map +0 -1
- package/dist/services/generator.d.ts +0 -2
- package/dist/services/generator.d.ts.map +0 -1
- package/dist/services/generator.js +0 -4
- package/dist/services/generator.js.map +0 -1
- package/dist/services/learner.d.ts +0 -2
- package/dist/services/learner.d.ts.map +0 -1
- package/dist/services/learner.js +0 -4
- package/dist/services/learner.js.map +0 -1
- package/dist/services/scanner.d.ts +0 -3
- package/dist/services/scanner.d.ts.map +0 -1
- package/dist/services/scanner.js +0 -54
- package/dist/services/scanner.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -15
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -27
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/file.d.ts +0 -7
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js +0 -32
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -17
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/path.d.ts +0 -6
- package/dist/utils/path.d.ts.map +0 -1
- package/dist/utils/path.js +0 -14
- package/dist/utils/path.js.map +0 -1
- package/docs/planning/memory-lane.md +0 -83
- package/packaging/linux/aicgen.spec +0 -23
- package/packaging/linux/control +0 -9
- package/packaging/macos/scripts/postinstall +0 -12
- package/packaging/windows/setup.nsi +0 -92
- package/scripts/add-categories.ts +0 -87
- package/scripts/build-binary.ts +0 -46
- package/scripts/embed-data.ts +0 -105
- package/scripts/generate-version.ts +0 -150
- package/scripts/test-decompress.ts +0 -27
- package/scripts/test-extract.ts +0 -31
- package/src/__tests__/services/assistant-file-writer.test.ts +0 -400
- package/src/__tests__/services/guideline-loader.test.ts +0 -281
- package/src/__tests__/services/tarball-extraction.test.ts +0 -125
- package/src/commands/add-guideline.ts +0 -296
- package/src/commands/clear.ts +0 -61
- package/src/commands/guideline-selector.ts +0 -123
- package/src/commands/init.ts +0 -645
- package/src/commands/quick-add.ts +0 -586
- package/src/commands/remove-guideline.ts +0 -152
- package/src/commands/stats.ts +0 -49
- package/src/commands/update.ts +0 -240
- package/src/config.ts +0 -82
- package/src/embedded-data.ts +0 -1492
- package/src/index.ts +0 -67
- package/src/models/profile.ts +0 -24
- package/src/models/project.ts +0 -43
- package/src/services/assistant-file-writer.ts +0 -612
- package/src/services/config-generator.ts +0 -150
- package/src/services/config-manager.ts +0 -70
- package/src/services/data-source.ts +0 -248
- package/src/services/first-run-init.ts +0 -148
- package/src/services/guideline-loader.ts +0 -311
- package/src/services/hook-generator.ts +0 -178
- package/src/services/subagent-generator.ts +0 -310
- package/src/utils/banner.ts +0 -66
- package/src/utils/errors.ts +0 -27
- package/src/utils/file.ts +0 -67
- package/src/utils/formatting.ts +0 -172
- package/src/utils/logger.ts +0 -89
- package/src/utils/path.ts +0 -17
- package/src/utils/wizard-state.ts +0 -132
- package/tsconfig.json +0 -25
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { select, checkbox, confirm } from '@inquirer/prompts';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import { readFile, writeFile, rm } from 'fs/promises';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
7
|
-
import YAML from 'yaml';
|
|
8
|
-
import { CONFIG } from '../config.js';
|
|
9
|
-
import { showBanner } from '../utils/banner.js';
|
|
10
|
-
import { addBackOption, BACK_VALUE } from '../utils/wizard-state.js';
|
|
11
|
-
import { GuidelineMapping } from '../services/guideline-loader.js';
|
|
12
|
-
|
|
13
|
-
export async function removeGuidelineCommand() {
|
|
14
|
-
showBanner();
|
|
15
|
-
console.log(chalk.cyan('🗑️ Remove Custom Guidelines\n'));
|
|
16
|
-
|
|
17
|
-
const userDataPath = join(homedir(), CONFIG.CACHE_DIR_NAME, CONFIG.DATA_DIR);
|
|
18
|
-
const mappingsPath = join(userDataPath, 'custom-mappings.yml');
|
|
19
|
-
|
|
20
|
-
if (!existsSync(mappingsPath)) {
|
|
21
|
-
console.log(chalk.gray('No custom guidelines found.'));
|
|
22
|
-
console.log(chalk.gray('Use `aicgen add-guideline` to create custom guidelines.\n'));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Load custom mappings
|
|
27
|
-
const mappingsContent = await readFile(mappingsPath, 'utf-8');
|
|
28
|
-
const mappings = YAML.parse(mappingsContent) as Record<string, GuidelineMapping>;
|
|
29
|
-
|
|
30
|
-
const guidelineIds = Object.keys(mappings);
|
|
31
|
-
|
|
32
|
-
if (guidelineIds.length === 0) {
|
|
33
|
-
console.log(chalk.gray('No custom guidelines found.\n'));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log(chalk.cyan(`Found ${guidelineIds.length} custom guideline(s):\n`));
|
|
38
|
-
|
|
39
|
-
let continueLoop = true;
|
|
40
|
-
while (continueLoop) {
|
|
41
|
-
// Ask user what to do
|
|
42
|
-
const action = await select({
|
|
43
|
-
message: 'What would you like to do?',
|
|
44
|
-
choices: [
|
|
45
|
-
{ value: 'select', name: 'Select specific guidelines to remove', description: 'Choose which ones to delete' },
|
|
46
|
-
{ value: 'all', name: 'Remove ALL custom guidelines', description: 'Delete everything' },
|
|
47
|
-
{ value: 'cancel', name: 'Cancel', description: 'Exit without changes' }
|
|
48
|
-
]
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
if (action === 'cancel') {
|
|
52
|
-
console.log(chalk.gray('\nCancelled.'));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let toRemove: string[] = [];
|
|
57
|
-
|
|
58
|
-
if (action === 'all') {
|
|
59
|
-
const confirmAll = await confirm({
|
|
60
|
-
message: chalk.yellow(`Remove ALL ${guidelineIds.length} custom guidelines?`),
|
|
61
|
-
default: false
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (!confirmAll) {
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
toRemove = guidelineIds;
|
|
69
|
-
} else {
|
|
70
|
-
// Let user select which to remove
|
|
71
|
-
const choices = addBackOption(guidelineIds.map(id => {
|
|
72
|
-
const mapping = mappings[id];
|
|
73
|
-
return {
|
|
74
|
-
name: `${id} (${mapping.category || 'General'})`,
|
|
75
|
-
value: id,
|
|
76
|
-
checked: false
|
|
77
|
-
};
|
|
78
|
-
}), true);
|
|
79
|
-
|
|
80
|
-
const selected = await checkbox({
|
|
81
|
-
message: 'Select guidelines to remove (Space to toggle, Enter to confirm):',
|
|
82
|
-
choices,
|
|
83
|
-
loop: false
|
|
84
|
-
}) as string[];
|
|
85
|
-
|
|
86
|
-
// Check if Back was selected (it comes as a string in the array if selected, though checkbox UI usually handles it differently if we mix types, but here we treat it as value)
|
|
87
|
-
// Actually checkbox returns array of values. If BACK_VALUE is in there, we should go back.
|
|
88
|
-
if (selected.includes(BACK_VALUE)) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (selected.length === 0) {
|
|
93
|
-
console.log(chalk.gray('\nNo guidelines selected.'));
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
toRemove = selected;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Remove selected guidelines
|
|
101
|
-
let removedCount = 0;
|
|
102
|
-
const errors: string[] = [];
|
|
103
|
-
|
|
104
|
-
for (const id of toRemove) {
|
|
105
|
-
try {
|
|
106
|
-
const mapping = mappings[id];
|
|
107
|
-
|
|
108
|
-
// Delete the guideline file
|
|
109
|
-
const guidelinePath = join(userDataPath, 'guidelines', mapping.path);
|
|
110
|
-
if (existsSync(guidelinePath)) {
|
|
111
|
-
await rm(guidelinePath);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Remove from mappings
|
|
115
|
-
delete mappings[id];
|
|
116
|
-
removedCount++;
|
|
117
|
-
|
|
118
|
-
console.log(chalk.green(`✓ Removed: ${id}`));
|
|
119
|
-
} catch (error) {
|
|
120
|
-
errors.push(`Failed to remove ${id}: ${error}`);
|
|
121
|
-
console.log(chalk.red(`✗ Failed: ${id}`));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Update mappings file
|
|
126
|
-
if (removedCount > 0) {
|
|
127
|
-
if (Object.keys(mappings).length === 0) {
|
|
128
|
-
// No guidelines left, remove the file
|
|
129
|
-
await rm(mappingsPath);
|
|
130
|
-
console.log(chalk.gray('\n (No custom guidelines remaining, removed mappings file)'));
|
|
131
|
-
return; // Exit as nothing left
|
|
132
|
-
} else {
|
|
133
|
-
// Update mappings file
|
|
134
|
-
await writeFile(mappingsPath, YAML.stringify(mappings), 'utf-8');
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Summary
|
|
139
|
-
console.log(chalk.green(`\n✅ Removed ${removedCount} custom guideline(s)`));
|
|
140
|
-
|
|
141
|
-
if (errors.length > 0) {
|
|
142
|
-
console.log(chalk.red(`\n❌ Errors:`));
|
|
143
|
-
errors.forEach(err => console.log(chalk.red(` ${err}`)));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Refresh list for next loop iteration
|
|
147
|
-
const remainingIds = Object.keys(mappings);
|
|
148
|
-
if (remainingIds.length === 0) {
|
|
149
|
-
continueLoop = false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
package/src/commands/stats.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { GuidelineLoader } from '../services/guideline-loader.js';
|
|
3
|
-
|
|
4
|
-
export async function statsCommand() {
|
|
5
|
-
console.log(chalk.blue.bold('\n📊 Guideline Statistics\n'));
|
|
6
|
-
|
|
7
|
-
const loader = await GuidelineLoader.create();
|
|
8
|
-
const stats = loader.getStats();
|
|
9
|
-
|
|
10
|
-
console.log(chalk.cyan('Total Guidelines:'), chalk.white(stats.totalGuidelines));
|
|
11
|
-
|
|
12
|
-
console.log(chalk.cyan('\n📋 By Language:'));
|
|
13
|
-
Object.entries(stats.byLanguage)
|
|
14
|
-
.sort((a, b) => b[1] - a[1])
|
|
15
|
-
.forEach(([lang, count]) => {
|
|
16
|
-
console.log(` ${chalk.gray(lang.padEnd(15))} ${chalk.white(count)}`);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
console.log(chalk.cyan('\n🎯 By Level:'));
|
|
20
|
-
Object.entries(stats.byLevel)
|
|
21
|
-
.sort((a, b) => b[1] - a[1])
|
|
22
|
-
.forEach(([level, count]) => {
|
|
23
|
-
console.log(` ${chalk.gray(level.padEnd(15))} ${chalk.white(count)}`);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
console.log(chalk.cyan('\n🏗️ By Architecture:'));
|
|
27
|
-
Object.entries(stats.byArchitecture)
|
|
28
|
-
.sort((a, b) => b[1] - a[1])
|
|
29
|
-
.forEach(([arch, count]) => {
|
|
30
|
-
console.log(` ${chalk.gray(arch.padEnd(20))} ${chalk.white(count)}`);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
console.log(chalk.cyan('\n📚 By Category:'));
|
|
34
|
-
Object.entries(stats.byCategory)
|
|
35
|
-
.sort((a, b) => b[1] - a[1])
|
|
36
|
-
.forEach(([category, count]) => {
|
|
37
|
-
console.log(` ${chalk.gray(category.padEnd(20))} ${chalk.white(count)}`);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
console.log(chalk.cyan('\n🏷️ Top Tags:'));
|
|
41
|
-
Object.entries(stats.byTag)
|
|
42
|
-
.sort((a, b) => b[1] - a[1])
|
|
43
|
-
.slice(0, 15)
|
|
44
|
-
.forEach(([tag, count]) => {
|
|
45
|
-
console.log(` ${chalk.gray(tag.padEnd(25))} ${chalk.white(count)}`);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
console.log();
|
|
49
|
-
}
|
package/src/commands/update.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import ora from 'ora';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import { mkdir, writeFile, rm, readdir, cp } from 'fs/promises';
|
|
6
|
-
import { GuidelineLoader } from '../services/guideline-loader.js';
|
|
7
|
-
import { createSummaryBox } from '../utils/formatting.js';
|
|
8
|
-
import { CONFIG, GITHUB_RELEASES_URL } from '../config.js';
|
|
9
|
-
|
|
10
|
-
const DOWNLOAD_TIMEOUT_MS = 30000;
|
|
11
|
-
const MAX_TARBALL_SIZE_BYTES = 10 * 1024 * 1024;
|
|
12
|
-
|
|
13
|
-
interface GitHubRelease {
|
|
14
|
-
tag_name: string;
|
|
15
|
-
name: string;
|
|
16
|
-
body: string;
|
|
17
|
-
zipball_url: string;
|
|
18
|
-
tarball_url: string;
|
|
19
|
-
published_at: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface VersionInfo {
|
|
23
|
-
version: string;
|
|
24
|
-
downloadUrl: string;
|
|
25
|
-
changes: string[];
|
|
26
|
-
publishedAt: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function updateCommand(options: { force?: boolean } = {}) {
|
|
30
|
-
const spinner = ora('Checking for updates...').start();
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// 1. Get current version
|
|
34
|
-
const loader = await GuidelineLoader.create();
|
|
35
|
-
const currentVersion = loader.getVersion();
|
|
36
|
-
|
|
37
|
-
// 2. Fetch latest version from GitHub
|
|
38
|
-
const latestVersion = await fetchLatestVersion();
|
|
39
|
-
|
|
40
|
-
spinner.stop();
|
|
41
|
-
|
|
42
|
-
// Show current vs latest
|
|
43
|
-
console.log('\n' + createSummaryBox('📦 Update Check', [
|
|
44
|
-
{ label: 'Current', value: currentVersion },
|
|
45
|
-
{ label: 'Latest', value: latestVersion.version }
|
|
46
|
-
]));
|
|
47
|
-
|
|
48
|
-
// 3. Check if update needed
|
|
49
|
-
if (!options.force && !needsUpdate(currentVersion, latestVersion.version)) {
|
|
50
|
-
console.log(chalk.green('\n✓ Already up to date!'));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (options.force) {
|
|
55
|
-
console.log(chalk.yellow('\n⚠️ Force update requested'));
|
|
56
|
-
} else {
|
|
57
|
-
console.log(chalk.cyan('\n📋 What\'s new:'));
|
|
58
|
-
latestVersion.changes.slice(0, 10).forEach(change => {
|
|
59
|
-
console.log(` ${chalk.gray('•')} ${change}`);
|
|
60
|
-
});
|
|
61
|
-
if (latestVersion.changes.length > 10) {
|
|
62
|
-
console.log(chalk.gray(` ... and ${latestVersion.changes.length - 10} more changes`));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// 4. Download and install
|
|
67
|
-
spinner.start('Downloading guidelines...');
|
|
68
|
-
|
|
69
|
-
const cacheDir = join(homedir(), CONFIG.CACHE_DIR_NAME, CONFIG.CACHE_DIR);
|
|
70
|
-
|
|
71
|
-
// Clear existing cache
|
|
72
|
-
try {
|
|
73
|
-
await rm(cacheDir, { recursive: true, force: true });
|
|
74
|
-
} catch {
|
|
75
|
-
// Directory might not exist
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
await mkdir(cacheDir, { recursive: true });
|
|
79
|
-
|
|
80
|
-
// Download from GitHub
|
|
81
|
-
await downloadGuidelines(latestVersion.downloadUrl, cacheDir);
|
|
82
|
-
|
|
83
|
-
// Write version file
|
|
84
|
-
await writeFile(
|
|
85
|
-
join(cacheDir, 'version.json'),
|
|
86
|
-
JSON.stringify({ version: latestVersion.version, updatedAt: new Date().toISOString() }, null, 2),
|
|
87
|
-
'utf-8'
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
spinner.succeed(`Updated to v${latestVersion.version}`);
|
|
91
|
-
|
|
92
|
-
console.log(chalk.green('\n✅ Guidelines updated successfully!'));
|
|
93
|
-
console.log(chalk.gray(` Location: ${cacheDir}`));
|
|
94
|
-
console.log(chalk.cyan(`\n ℹ️ Your custom guidelines are safe (stored separately in ~/.aicgen/data/)`));
|
|
95
|
-
console.log(chalk.gray(`\n Run ${chalk.white('aicgen init')} to use the latest guidelines`));
|
|
96
|
-
|
|
97
|
-
} catch (error) {
|
|
98
|
-
spinner.fail('Update failed');
|
|
99
|
-
|
|
100
|
-
if ((error as Error).message.includes('rate limit')) {
|
|
101
|
-
console.error(chalk.red('\n❌ GitHub API rate limit exceeded'));
|
|
102
|
-
console.log(chalk.yellow(' Try again later or authenticate with GitHub'));
|
|
103
|
-
} else if ((error as Error).message.includes('ENOTFOUND') || (error as Error).message.includes('fetch')) {
|
|
104
|
-
console.error(chalk.red('\n❌ Network error - check your internet connection'));
|
|
105
|
-
} else {
|
|
106
|
-
console.error(chalk.red(`\n❌ ${(error as Error).message}`));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function fetchLatestVersion(): Promise<VersionInfo> {
|
|
114
|
-
const controller = new AbortController();
|
|
115
|
-
const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const response = await fetch(GITHUB_RELEASES_URL, {
|
|
119
|
-
headers: {
|
|
120
|
-
'Accept': 'application/vnd.github+json',
|
|
121
|
-
'User-Agent': CONFIG.USER_AGENT
|
|
122
|
-
},
|
|
123
|
-
signal: controller.signal
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
if (!response.ok) {
|
|
127
|
-
if (response.status === 404) {
|
|
128
|
-
throw new Error('Guidelines repository not found. The repository may not exist yet.');
|
|
129
|
-
} else if (response.status === 403) {
|
|
130
|
-
throw new Error('GitHub API rate limit exceeded');
|
|
131
|
-
}
|
|
132
|
-
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const data = await response.json() as GitHubRelease;
|
|
136
|
-
|
|
137
|
-
// Parse changelog from body
|
|
138
|
-
const changes = data.body
|
|
139
|
-
.split('\n')
|
|
140
|
-
.filter(line => line.trim().startsWith('-') || line.trim().startsWith('*'))
|
|
141
|
-
.map(line => line.replace(/^[-*]\s*/, '').trim())
|
|
142
|
-
.filter(line => line.length > 0);
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
version: data.tag_name.replace(/^v/, ''),
|
|
146
|
-
downloadUrl: data.tarball_url,
|
|
147
|
-
changes,
|
|
148
|
-
publishedAt: data.published_at
|
|
149
|
-
};
|
|
150
|
-
} finally {
|
|
151
|
-
clearTimeout(timeout);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function downloadGuidelines(url: string, targetDir: string): Promise<void> {
|
|
156
|
-
const controller = new AbortController();
|
|
157
|
-
const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
161
|
-
|
|
162
|
-
if (!response.ok) {
|
|
163
|
-
throw new Error(`Failed to download: ${response.status} ${response.statusText}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Check content-length if available
|
|
167
|
-
const contentLength = response.headers.get('content-length');
|
|
168
|
-
if (contentLength && parseInt(contentLength, 10) > MAX_TARBALL_SIZE_BYTES) {
|
|
169
|
-
throw new Error(`Tarball too large: ${contentLength} bytes (max ${MAX_TARBALL_SIZE_BYTES})`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const tarballBuffer = Buffer.from(await response.arrayBuffer());
|
|
173
|
-
|
|
174
|
-
// Verify size after download
|
|
175
|
-
if (tarballBuffer.length > MAX_TARBALL_SIZE_BYTES) {
|
|
176
|
-
throw new Error(`Downloaded tarball too large: ${tarballBuffer.length} bytes`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Extract tarball to temp directory
|
|
180
|
-
const tempDir = join(targetDir, '.temp-extract');
|
|
181
|
-
await mkdir(tempDir, { recursive: true });
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const tarballPath = join(tempDir, 'archive.tar.gz');
|
|
185
|
-
await writeFile(tarballPath, tarballBuffer);
|
|
186
|
-
|
|
187
|
-
const decompress = (await import('decompress')).default;
|
|
188
|
-
await decompress(tarballPath, tempDir);
|
|
189
|
-
|
|
190
|
-
const entries = await readdir(tempDir);
|
|
191
|
-
const expectedPrefix = `${CONFIG.GITHUB_REPO_OWNER}-${CONFIG.GITHUB_REPO_NAME}-`;
|
|
192
|
-
const rootDir = entries.find(entry => entry.startsWith(expectedPrefix));
|
|
193
|
-
|
|
194
|
-
if (!rootDir) {
|
|
195
|
-
throw new Error(`Could not find extracted repository directory (expected ${expectedPrefix}*)`);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const extractedPath = join(tempDir, rootDir);
|
|
199
|
-
const guidelinesTarget = join(targetDir, 'guidelines');
|
|
200
|
-
await mkdir(guidelinesTarget, { recursive: true });
|
|
201
|
-
|
|
202
|
-
const extractedEntries = await readdir(extractedPath, { withFileTypes: true });
|
|
203
|
-
for (const entry of extractedEntries) {
|
|
204
|
-
const sourcePath = join(extractedPath, entry.name);
|
|
205
|
-
const targetPath = join(guidelinesTarget, entry.name);
|
|
206
|
-
|
|
207
|
-
if (entry.name === 'guideline-mappings.yml') {
|
|
208
|
-
await cp(sourcePath, join(targetDir, entry.name));
|
|
209
|
-
} else if (entry.isDirectory() || entry.name.endsWith('.md')) {
|
|
210
|
-
await cp(sourcePath, targetPath, { recursive: true });
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
} finally {
|
|
214
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
215
|
-
}
|
|
216
|
-
} finally {
|
|
217
|
-
clearTimeout(timeout);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function needsUpdate(current: string, latest: string): boolean {
|
|
222
|
-
// If current is embedded or unknown, always update
|
|
223
|
-
if (current === 'embedded' || current === 'unknown' || current === 'custom') {
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Simple version comparison (assumes semver)
|
|
228
|
-
const currentParts = current.split('.').map(Number);
|
|
229
|
-
const latestParts = latest.split('.').map(Number);
|
|
230
|
-
|
|
231
|
-
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
232
|
-
const curr = currentParts[i] || 0;
|
|
233
|
-
const lat = latestParts[i] || 0;
|
|
234
|
-
|
|
235
|
-
if (lat > curr) return true;
|
|
236
|
-
if (lat < curr) return false;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return false;
|
|
240
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'fs';
|
|
2
|
-
import { join, dirname } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import YAML from 'yaml';
|
|
6
|
-
|
|
7
|
-
function getPackageVersion(): string {
|
|
8
|
-
try {
|
|
9
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
11
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
12
|
-
return packageJson.version || '1.0.0-beta.1';
|
|
13
|
-
} catch {
|
|
14
|
-
// In compiled binary, package.json is not available
|
|
15
|
-
// Version will be injected at build time via APP_VERSION env var
|
|
16
|
-
return process.env.APP_VERSION || '1.0.0-beta.1';
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getPackageName(): string {
|
|
21
|
-
try {
|
|
22
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
24
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
25
|
-
return packageJson.name || 'aicgen';
|
|
26
|
-
} catch {
|
|
27
|
-
return 'aicgen';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface UserConfig {
|
|
32
|
-
github?: {
|
|
33
|
-
owner?: string;
|
|
34
|
-
repo?: string;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function loadUserConfig(): UserConfig {
|
|
39
|
-
try {
|
|
40
|
-
const configPath = join(homedir(), '.aicgen', 'config.yml');
|
|
41
|
-
if (existsSync(configPath)) {
|
|
42
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
43
|
-
return YAML.parse(content) as UserConfig;
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// Config file doesn't exist or is invalid - use defaults
|
|
47
|
-
}
|
|
48
|
-
return {};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const userConfig = loadUserConfig();
|
|
52
|
-
|
|
53
|
-
export const CONFIG = {
|
|
54
|
-
APP_VERSION: getPackageVersion(),
|
|
55
|
-
|
|
56
|
-
APP_NAME: getPackageName(),
|
|
57
|
-
|
|
58
|
-
// Priority: ENV > User Config > Default
|
|
59
|
-
GITHUB_REPO_OWNER:
|
|
60
|
-
process.env.AICGEN_GITHUB_OWNER ||
|
|
61
|
-
userConfig.github?.owner ||
|
|
62
|
-
'aicgen',
|
|
63
|
-
|
|
64
|
-
GITHUB_REPO_NAME:
|
|
65
|
-
process.env.AICGEN_GITHUB_REPO ||
|
|
66
|
-
userConfig.github?.repo ||
|
|
67
|
-
'aicgen-data',
|
|
68
|
-
|
|
69
|
-
GITHUB_API_BASE: 'https://api.github.com',
|
|
70
|
-
|
|
71
|
-
USER_AGENT: 'aicgen-cli',
|
|
72
|
-
|
|
73
|
-
CACHE_DIR_NAME: '.aicgen',
|
|
74
|
-
|
|
75
|
-
DATA_DIR: 'data',
|
|
76
|
-
|
|
77
|
-
CACHE_DIR: 'cache/official',
|
|
78
|
-
} as const;
|
|
79
|
-
|
|
80
|
-
export const GITHUB_REPO_URL = `${CONFIG.GITHUB_API_BASE}/repos/${CONFIG.GITHUB_REPO_OWNER}/${CONFIG.GITHUB_REPO_NAME}`;
|
|
81
|
-
|
|
82
|
-
export const GITHUB_RELEASES_URL = `${GITHUB_REPO_URL}/releases/latest`;
|