@aicgen/aicgen 1.0.0-beta.1 → 1.0.0-beta.2
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/.vs/ProjectSettings.json +2 -2
- package/.vs/VSWorkspaceState.json +15 -15
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +53 -53
- package/assets/icon.svg +33 -33
- package/bun.lock +0 -43
- package/data/architecture/microservices/api-gateway.md +56 -56
- package/data/devops/observability.md +73 -73
- package/dist/index.js +9299 -9299
- package/package.json +2 -2
- 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/guidelines/api-design.md +0 -645
- package/.claude/guidelines/architecture.md +0 -2503
- package/.claude/guidelines/best-practices.md +0 -618
- package/.claude/guidelines/code-style.md +0 -304
- package/.claude/guidelines/design-patterns.md +0 -573
- package/.claude/guidelines/devops.md +0 -226
- package/.claude/guidelines/error-handling.md +0 -413
- package/.claude/guidelines/language.md +0 -782
- package/.claude/guidelines/performance.md +0 -706
- package/.claude/guidelines/security.md +0 -583
- package/.claude/guidelines/testing.md +0 -568
- 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
- /package/{CLAUDE.md → claude.md} +0 -0
|
@@ -1,586 +0,0 @@
|
|
|
1
|
-
import { select, checkbox, confirm } from '@inquirer/prompts';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
4
|
-
import { readFile, writeFile, appendFile, mkdir } from 'fs/promises';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
-
import ora from 'ora';
|
|
7
|
-
import { AIAssistant, Language, ProjectType } from '../models/project.js';
|
|
8
|
-
import { GuidelineLoader } from '../services/guideline-loader.js';
|
|
9
|
-
import { showBanner } from '../utils/banner.js';
|
|
10
|
-
import { AssistantFileWriter } from '../services/assistant-file-writer.js';
|
|
11
|
-
import { ProfileSelection, ArchitectureType, InstructionLevel } from '../models/profile.js';
|
|
12
|
-
import { ConfigGenerator } from '../services/config-generator.js';
|
|
13
|
-
|
|
14
|
-
interface CheckboxChoice {
|
|
15
|
-
name: string;
|
|
16
|
-
value: string;
|
|
17
|
-
checked?: boolean;
|
|
18
|
-
disabled?: boolean | string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export async function quickAddCommand() {
|
|
23
|
-
showBanner();
|
|
24
|
-
console.log(chalk.cyan('🚀 Quick Add Guidelines\n'));
|
|
25
|
-
|
|
26
|
-
const projectPath = process.cwd();
|
|
27
|
-
const spinner = ora('Detecting project configuration...').start();
|
|
28
|
-
|
|
29
|
-
// Detect which assistant is being used
|
|
30
|
-
const assistant = await detectAssistant(projectPath);
|
|
31
|
-
|
|
32
|
-
if (!assistant) {
|
|
33
|
-
spinner.fail('No AI configuration found');
|
|
34
|
-
console.log(chalk.yellow('\n⚠️ No existing AI configuration detected.'));
|
|
35
|
-
console.log(chalk.gray(' Run `aicgen init` first to create a configuration.\n'));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
spinner.succeed(`Detected: ${assistant}`);
|
|
40
|
-
|
|
41
|
-
// Load existing configuration
|
|
42
|
-
const existingConfig = await loadExistingConfig(projectPath, assistant);
|
|
43
|
-
|
|
44
|
-
console.log(chalk.cyan('\n📋 Current Configuration:'));
|
|
45
|
-
console.log(` Assistant: ${existingConfig.assistant}`);
|
|
46
|
-
console.log(` Language: ${existingConfig.language}`);
|
|
47
|
-
console.log(` Level: ${existingConfig.level}`);
|
|
48
|
-
console.log(` Architecture: ${existingConfig.architecture || 'Not specified'}`);
|
|
49
|
-
|
|
50
|
-
// Load guideline library
|
|
51
|
-
const loader = await GuidelineLoader.create();
|
|
52
|
-
|
|
53
|
-
let continueLoop = true;
|
|
54
|
-
while (continueLoop) {
|
|
55
|
-
// Organize by category
|
|
56
|
-
const categoryTree = loader.getCategoryTree(
|
|
57
|
-
existingConfig.language,
|
|
58
|
-
existingConfig.level,
|
|
59
|
-
existingConfig.architecture || 'modular-monolith',
|
|
60
|
-
existingConfig.datasource || 'sql'
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Build choices
|
|
64
|
-
const choices: CheckboxChoice[] = [];
|
|
65
|
-
const guidelineMap = new Map<string, string>();
|
|
66
|
-
|
|
67
|
-
for (const category of categoryTree) {
|
|
68
|
-
choices.push({
|
|
69
|
-
name: chalk.bold.cyan(`${category.name} (${category.count})`),
|
|
70
|
-
value: `__CATEGORY_${category.name}__`,
|
|
71
|
-
disabled: true
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
for (const guideline of category.guidelines) {
|
|
75
|
-
choices.push({
|
|
76
|
-
name: ` ${guideline.name}`,
|
|
77
|
-
value: guideline.id,
|
|
78
|
-
checked: false
|
|
79
|
-
});
|
|
80
|
-
guidelineMap.set(guideline.id, guideline.name);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Let user select guidelines
|
|
85
|
-
const selectedIds = await checkbox({
|
|
86
|
-
message: 'Select guidelines to add (Space to toggle, Enter to confirm, or press Enter with no selection to cancel):',
|
|
87
|
-
choices,
|
|
88
|
-
pageSize: 25,
|
|
89
|
-
loop: false
|
|
90
|
-
}) as string[];
|
|
91
|
-
|
|
92
|
-
if (selectedIds.length === 0) {
|
|
93
|
-
const action = await select({
|
|
94
|
-
message: 'No guidelines selected. What would you like to do?',
|
|
95
|
-
choices: [
|
|
96
|
-
{ value: 'retry', name: 'Try again', description: 'Go back to guideline selection' },
|
|
97
|
-
{ value: 'cancel', name: 'Cancel', description: 'Exit without changes' }
|
|
98
|
-
]
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (action === 'cancel') {
|
|
102
|
-
console.log(chalk.gray('\nCancelled.'));
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
console.clear();
|
|
107
|
-
showBanner();
|
|
108
|
-
console.log(chalk.cyan('🚀 Quick Add Guidelines\n'));
|
|
109
|
-
console.log(chalk.cyan('📋 Current Configuration:'));
|
|
110
|
-
console.log(` Assistant: ${existingConfig.assistant}`);
|
|
111
|
-
console.log(` Language: ${existingConfig.language}`);
|
|
112
|
-
console.log(` Level: ${existingConfig.level}`);
|
|
113
|
-
console.log(` Architecture: ${existingConfig.architecture || 'Not specified'}\n`);
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Filter out category separators
|
|
118
|
-
const validIds = selectedIds.filter(id => !id.startsWith('__CATEGORY_'));
|
|
119
|
-
|
|
120
|
-
console.log(chalk.cyan(`\n✓ Selected ${validIds.length} guideline(s)`));
|
|
121
|
-
|
|
122
|
-
// Show what will be added
|
|
123
|
-
console.log(chalk.cyan('\n📚 Adding:'));
|
|
124
|
-
validIds.slice(0, 10).forEach(id => {
|
|
125
|
-
console.log(` • ${guidelineMap.get(id) || id}`);
|
|
126
|
-
});
|
|
127
|
-
if (validIds.length > 10) {
|
|
128
|
-
console.log(chalk.gray(` ... and ${validIds.length - 10} more`));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Inner loop for "how to add" and confirmation
|
|
132
|
-
let addModeLoop = true;
|
|
133
|
-
while (addModeLoop) {
|
|
134
|
-
// Ask how to add
|
|
135
|
-
const addChoices = [
|
|
136
|
-
{ value: 'append', name: 'Append to existing config', description: 'Add to current configuration' },
|
|
137
|
-
{ value: 'replace', name: 'Replace entire config', description: 'Regenerate with selected + existing guidelines' },
|
|
138
|
-
{ value: 'back', name: '← Back to selection', description: 'Change selected guidelines' },
|
|
139
|
-
{ value: 'cancel', name: 'Cancel', description: 'Exit without changes' }
|
|
140
|
-
];
|
|
141
|
-
|
|
142
|
-
const addMode = await select({
|
|
143
|
-
message: 'How would you like to add these guidelines?',
|
|
144
|
-
choices: addChoices
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (addMode === 'back') {
|
|
148
|
-
console.clear();
|
|
149
|
-
showBanner();
|
|
150
|
-
console.log(chalk.cyan('🚀 Quick Add Guidelines\n'));
|
|
151
|
-
console.log(chalk.cyan('📋 Current Configuration:'));
|
|
152
|
-
console.log(` Assistant: ${existingConfig.assistant}`);
|
|
153
|
-
console.log(` Language: ${existingConfig.language}`);
|
|
154
|
-
console.log(` Level: ${existingConfig.level}`);
|
|
155
|
-
console.log(` Architecture: ${existingConfig.architecture || 'Not specified'}\n`);
|
|
156
|
-
addModeLoop = false; // Exit inner loop, continue outer loop
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (addMode === 'cancel') {
|
|
161
|
-
console.log(chalk.gray('\nCancelled.'));
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Warn before destructive replace
|
|
166
|
-
if (addMode === 'replace') {
|
|
167
|
-
console.log(chalk.yellow('\n⚠️ Warning: Replace mode will regenerate your entire configuration.'));
|
|
168
|
-
console.log(chalk.gray(' This will overwrite any manual edits to settings, hooks, or guidelines.'));
|
|
169
|
-
|
|
170
|
-
const confirmReplace = await confirm({
|
|
171
|
-
message: 'Are you sure you want to replace the entire configuration?',
|
|
172
|
-
default: false
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
if (!confirmReplace) {
|
|
176
|
-
console.log(chalk.gray('\nGoing back to mode selection...\n'));
|
|
177
|
-
continue; // Stay in inner loop - go back to "how to add" question
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Execute the operation
|
|
182
|
-
spinner.start('Adding guidelines...');
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
if (addMode === 'append') {
|
|
186
|
-
await appendGuidelines(projectPath, assistant, validIds, loader);
|
|
187
|
-
} else {
|
|
188
|
-
await regenerateConfig(projectPath, existingConfig, validIds, loader);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
spinner.succeed('Guidelines added successfully!');
|
|
192
|
-
console.log(chalk.green(`\n✅ Added ${validIds.length} guideline(s) to ${assistant} configuration`));
|
|
193
|
-
continueLoop = false;
|
|
194
|
-
addModeLoop = false;
|
|
195
|
-
} catch (error) {
|
|
196
|
-
spinner.fail('Failed to add guidelines');
|
|
197
|
-
console.error(chalk.red(`\n❌ Error: ${error}`));
|
|
198
|
-
continueLoop = false;
|
|
199
|
-
addModeLoop = false;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async function detectAssistant(projectPath: string): Promise<AIAssistant | null> {
|
|
206
|
-
const configs = [
|
|
207
|
-
{ path: 'CLAUDE.md', assistant: 'claude-code' as AIAssistant },
|
|
208
|
-
{ path: '.claude', assistant: 'claude-code' as AIAssistant },
|
|
209
|
-
{ path: '.github/copilot-instructions.md', assistant: 'copilot' as AIAssistant },
|
|
210
|
-
{ path: '.gemini', assistant: 'gemini' as AIAssistant },
|
|
211
|
-
{ path: '.agent', assistant: 'antigravity' as AIAssistant },
|
|
212
|
-
{ path: '.codex', assistant: 'codex' as AIAssistant }
|
|
213
|
-
];
|
|
214
|
-
|
|
215
|
-
for (const config of configs) {
|
|
216
|
-
if (existsSync(join(projectPath, config.path))) {
|
|
217
|
-
return config.assistant;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async function loadExistingConfig(projectPath: string, assistant: AIAssistant): Promise<ProfileSelection> {
|
|
225
|
-
// Default fallback - get project name from path
|
|
226
|
-
const pathParts = projectPath.split(/[/\\]/);
|
|
227
|
-
const defaultName = pathParts[pathParts.length - 1] || 'project';
|
|
228
|
-
|
|
229
|
-
const config: ProfileSelection = {
|
|
230
|
-
assistant,
|
|
231
|
-
language: 'typescript',
|
|
232
|
-
level: 'standard',
|
|
233
|
-
architecture: 'modular-monolith',
|
|
234
|
-
projectType: 'web',
|
|
235
|
-
projectName: defaultName,
|
|
236
|
-
datasource: 'sql'
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// 1. Detect language and name from project files using ConfigGenerator logic
|
|
240
|
-
try {
|
|
241
|
-
const generator = await ConfigGenerator.create();
|
|
242
|
-
const detected = await generator.detectProject(projectPath);
|
|
243
|
-
if (detected.language !== 'unknown') {
|
|
244
|
-
config.language = detected.language;
|
|
245
|
-
}
|
|
246
|
-
config.projectName = detected.name;
|
|
247
|
-
} catch {
|
|
248
|
-
// Ignore errors here
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// 2. Parsed from instruction files if possible
|
|
252
|
-
try {
|
|
253
|
-
let content = '';
|
|
254
|
-
let filePath = '';
|
|
255
|
-
|
|
256
|
-
switch (assistant) {
|
|
257
|
-
case 'claude-code': {
|
|
258
|
-
// Check root first, then .claude/ for backward compatibility
|
|
259
|
-
const rootPath = join(projectPath, 'CLAUDE.md');
|
|
260
|
-
const legacyPath = join(projectPath, '.claude', 'CLAUDE.md');
|
|
261
|
-
filePath = existsSync(rootPath) ? rootPath : legacyPath;
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
case 'copilot':
|
|
265
|
-
filePath = join(projectPath, '.github', 'copilot-instructions.md');
|
|
266
|
-
break;
|
|
267
|
-
case 'gemini':
|
|
268
|
-
filePath = join(projectPath, '.gemini', 'instructions.md');
|
|
269
|
-
break;
|
|
270
|
-
case 'antigravity':
|
|
271
|
-
filePath = join(projectPath, '.agent', 'rules', 'instructions.md');
|
|
272
|
-
break;
|
|
273
|
-
case 'codex':
|
|
274
|
-
filePath = join(projectPath, '.codex', 'instructions.md');
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (filePath && existsSync(filePath)) {
|
|
279
|
-
content = await readFile(filePath, 'utf-8');
|
|
280
|
-
|
|
281
|
-
// Extract Language
|
|
282
|
-
const langMatch = content.match(/Language:\*\*\s*([\w-#]+)/i) || content.match(/Language:\s*([\w-#]+)/i);
|
|
283
|
-
if (langMatch && langMatch[1]) {
|
|
284
|
-
config.language = langMatch[1].toLowerCase().trim() as Language;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Extract Architecture
|
|
288
|
-
const archMatch = content.match(/Architecture:\*\*\s*([\w-]+)/i) || content.match(/Architecture:\s*([\w-]+)/i);
|
|
289
|
-
if (archMatch && archMatch[1]) {
|
|
290
|
-
config.architecture = archMatch[1].toLowerCase().trim() as ArchitectureType;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Extract Type
|
|
294
|
-
const typeMatch = content.match(/Type:\*\*\s*([\w-]+)/i) || content.match(/Type:\s*([\w-]+)/i);
|
|
295
|
-
if (typeMatch && typeMatch[1]) {
|
|
296
|
-
const typeStr = typeMatch[1].toLowerCase().trim();
|
|
297
|
-
// Simple mapping verification
|
|
298
|
-
if (['web', 'api', 'cli', 'library', 'desktop', 'mobile', 'other'].includes(typeStr)) {
|
|
299
|
-
config.projectType = typeStr as ProjectType;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Extract Level
|
|
304
|
-
const levelMatch = content.match(/Level:\*\*\s*([\w-]+)/i) || content.match(/Level:\s*([\w-]+)/i);
|
|
305
|
-
if (levelMatch && levelMatch[1]) {
|
|
306
|
-
config.level = levelMatch[1].toLowerCase().trim() as InstructionLevel;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
} catch (err) {
|
|
310
|
-
console.log(chalk.yellow(`\n⚠️ Could not parse existing config, using defaults: ${err}`));
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return config;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async function appendGuidelines(
|
|
317
|
-
projectPath: string,
|
|
318
|
-
assistant: AIAssistant,
|
|
319
|
-
guidelineIds: string[],
|
|
320
|
-
loader: GuidelineLoader
|
|
321
|
-
): Promise<void> {
|
|
322
|
-
const guidelines = guidelineIds.map(id => loader.loadGuideline(id)).filter(Boolean);
|
|
323
|
-
|
|
324
|
-
switch (assistant) {
|
|
325
|
-
case 'claude-code':
|
|
326
|
-
await appendToClaudeCode(projectPath, guidelines);
|
|
327
|
-
break;
|
|
328
|
-
case 'copilot':
|
|
329
|
-
await appendToCopilot(projectPath, guidelines);
|
|
330
|
-
break;
|
|
331
|
-
case 'gemini':
|
|
332
|
-
await appendToGemini(projectPath, guidelines);
|
|
333
|
-
break;
|
|
334
|
-
case 'antigravity':
|
|
335
|
-
await appendToAntigravity(projectPath, guidelines);
|
|
336
|
-
break;
|
|
337
|
-
case 'codex':
|
|
338
|
-
await appendToCodex(projectPath, guidelines);
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async function appendToClaudeCode(projectPath: string, guidelines: string[]): Promise<void> {
|
|
344
|
-
// Check root first, then .claude/ for backward compatibility
|
|
345
|
-
const rootPath = join(projectPath, 'CLAUDE.md');
|
|
346
|
-
const legacyPath = join(projectPath, '.claude', 'CLAUDE.md');
|
|
347
|
-
const instructionsPath = existsSync(rootPath) ? rootPath : legacyPath;
|
|
348
|
-
|
|
349
|
-
if (!existsSync(instructionsPath)) {
|
|
350
|
-
throw new Error('CLAUDE.md not found');
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Create guidelines directory
|
|
354
|
-
const guidelinesDir = join(projectPath, '.claude', 'guidelines');
|
|
355
|
-
await mkdir(guidelinesDir, { recursive: true });
|
|
356
|
-
|
|
357
|
-
// Create or append to additional.md file
|
|
358
|
-
const additionalFile = join(guidelinesDir, 'additional.md');
|
|
359
|
-
const additionalContent = `# Additional Guidelines\n\n${guidelines.join('\n\n---\n\n')}`;
|
|
360
|
-
|
|
361
|
-
if (existsSync(additionalFile)) {
|
|
362
|
-
// Append to existing file
|
|
363
|
-
await appendFile(additionalFile, `\n\n---\n\n${guidelines.join('\n\n---\n\n')}`, 'utf-8');
|
|
364
|
-
} else {
|
|
365
|
-
// Create new file
|
|
366
|
-
await writeFile(additionalFile, additionalContent, 'utf-8');
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Update CLAUDE.md with reference if not already present
|
|
370
|
-
const content = await readFile(instructionsPath, 'utf-8');
|
|
371
|
-
|
|
372
|
-
if (content.includes('@.claude/guidelines/additional.md')) {
|
|
373
|
-
// Reference already exists, file is updated, we're done
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Add reference to guidelines section
|
|
378
|
-
const guidelinesMatch = content.match(/(## Guidelines[\s\S]*?)(?=\n## |$)/);
|
|
379
|
-
if (guidelinesMatch) {
|
|
380
|
-
const guidelinesSection = guidelinesMatch[1];
|
|
381
|
-
const newGuidelinesSection = guidelinesSection.trimEnd() + '\n- **Additional**: @.claude/guidelines/additional.md';
|
|
382
|
-
const newContent = content.replace(guidelinesSection, newGuidelinesSection);
|
|
383
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
384
|
-
} else {
|
|
385
|
-
// Fallback: append to end of file
|
|
386
|
-
const newContent = content.trimEnd() + '\n\n- **Additional**: @.claude/guidelines/additional.md\n';
|
|
387
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
async function appendToCopilot(projectPath: string, guidelines: string[]): Promise<void> {
|
|
392
|
-
const instructionsPath = join(projectPath, '.github', 'copilot-instructions.md');
|
|
393
|
-
|
|
394
|
-
if (!existsSync(instructionsPath)) {
|
|
395
|
-
throw new Error('copilot-instructions.md not found');
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Create instructions directory
|
|
399
|
-
const instructionsDir = join(projectPath, '.github', 'instructions');
|
|
400
|
-
await mkdir(instructionsDir, { recursive: true });
|
|
401
|
-
|
|
402
|
-
// Create additional instructions file with frontmatter
|
|
403
|
-
const additionalFile = join(instructionsDir, 'additional.instructions.md');
|
|
404
|
-
const additionalContent = `---
|
|
405
|
-
applyTo: "**/*"
|
|
406
|
-
description: "Additional guidelines"
|
|
407
|
-
---
|
|
408
|
-
|
|
409
|
-
# Additional Guidelines
|
|
410
|
-
|
|
411
|
-
${guidelines.join('\n\n---\n\n')}`;
|
|
412
|
-
|
|
413
|
-
if (existsSync(additionalFile)) {
|
|
414
|
-
// Append to existing file (skip frontmatter)
|
|
415
|
-
await appendFile(additionalFile, `\n\n---\n\n${guidelines.join('\n\n---\n\n')}`, 'utf-8');
|
|
416
|
-
} else {
|
|
417
|
-
// Create new file
|
|
418
|
-
await writeFile(additionalFile, additionalContent, 'utf-8');
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Update copilot-instructions.md with reference if not already present
|
|
422
|
-
const content = await readFile(instructionsPath, 'utf-8');
|
|
423
|
-
|
|
424
|
-
if (content.includes('@.github/instructions/additional.instructions.md')) {
|
|
425
|
-
// Reference already exists
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Add reference to guidelines section
|
|
430
|
-
const guidelinesMatch = content.match(/(## Guidelines[\s\S]*?)(?=\n## |$)/);
|
|
431
|
-
if (guidelinesMatch) {
|
|
432
|
-
const guidelinesSection = guidelinesMatch[1];
|
|
433
|
-
const newGuidelinesSection = guidelinesSection.trimEnd() + '\n- Additional: @.github/instructions/additional.instructions.md';
|
|
434
|
-
const newContent = content.replace(guidelinesSection, newGuidelinesSection);
|
|
435
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
436
|
-
} else {
|
|
437
|
-
// Fallback: append to end
|
|
438
|
-
const newContent = content.trimEnd() + '\n\n- Additional: @.github/instructions/additional.instructions.md\n';
|
|
439
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async function appendToGemini(projectPath: string, guidelines: string[]): Promise<void> {
|
|
444
|
-
const instructionsPath = join(projectPath, '.gemini', 'instructions.md');
|
|
445
|
-
|
|
446
|
-
if (!existsSync(instructionsPath)) {
|
|
447
|
-
throw new Error('instructions.md not found');
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Create guidelines directory
|
|
451
|
-
const geminiDir = join(projectPath, '.gemini');
|
|
452
|
-
await mkdir(geminiDir, { recursive: true });
|
|
453
|
-
|
|
454
|
-
// Create additional guidelines file
|
|
455
|
-
const additionalFile = join(geminiDir, 'additional-guidelines.md');
|
|
456
|
-
const additionalContent = `# Additional Guidelines\n\n${guidelines.join('\n\n---\n\n')}`;
|
|
457
|
-
|
|
458
|
-
if (existsSync(additionalFile)) {
|
|
459
|
-
// Append to existing file
|
|
460
|
-
await appendFile(additionalFile, `\n\n---\n\n${guidelines.join('\n\n---\n\n')}`, 'utf-8');
|
|
461
|
-
} else {
|
|
462
|
-
// Create new file
|
|
463
|
-
await writeFile(additionalFile, additionalContent, 'utf-8');
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Update instructions.md with import reference if not already present
|
|
467
|
-
const content = await readFile(instructionsPath, 'utf-8');
|
|
468
|
-
|
|
469
|
-
if (content.includes('additional-guidelines.md')) {
|
|
470
|
-
// Reference already exists
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Add reference before the closing section
|
|
475
|
-
const newContent = content.trimEnd() + `\n\n## Additional Guidelines\n\nSee: additional-guidelines.md\n`;
|
|
476
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async function appendToAntigravity(
|
|
480
|
-
projectPath: string,
|
|
481
|
-
guidelines: string[]
|
|
482
|
-
): Promise<void> {
|
|
483
|
-
// Create rules directory
|
|
484
|
-
const rulesDir = join(projectPath, '.agent', 'rules');
|
|
485
|
-
await mkdir(rulesDir, { recursive: true });
|
|
486
|
-
|
|
487
|
-
// Create additional rules file
|
|
488
|
-
const additionalRulesPath = join(rulesDir, 'additional.md');
|
|
489
|
-
const additionalContent = `# Additional Rules\n\n${guidelines.join('\n\n---\n\n')}\n\n---\n*Generated by aicgen*\n`;
|
|
490
|
-
|
|
491
|
-
if (existsSync(additionalRulesPath)) {
|
|
492
|
-
// Append to existing file
|
|
493
|
-
await appendFile(additionalRulesPath, `\n\n---\n\n${guidelines.join('\n\n---\n\n')}`, 'utf-8');
|
|
494
|
-
} else {
|
|
495
|
-
// Create new file
|
|
496
|
-
await writeFile(additionalRulesPath, additionalContent, 'utf-8');
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Update instructions.md with reference if not already present
|
|
500
|
-
const instructionsPath = join(rulesDir, 'instructions.md');
|
|
501
|
-
if (existsSync(instructionsPath)) {
|
|
502
|
-
const content = await readFile(instructionsPath, 'utf-8');
|
|
503
|
-
|
|
504
|
-
if (content.includes('@.agent/rules/additional.md')) {
|
|
505
|
-
// Reference already exists
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// Add reference to rule index section
|
|
510
|
-
const ruleIndexMatch = content.match(/(## Rule Index[\s\S]*?)(?=\n## |$)/);
|
|
511
|
-
if (ruleIndexMatch) {
|
|
512
|
-
const ruleIndexSection = ruleIndexMatch[1];
|
|
513
|
-
const newRuleIndexSection = ruleIndexSection.trimEnd() + '\n- **Additional**: @.agent/rules/additional.md';
|
|
514
|
-
const newContent = content.replace(ruleIndexSection, newRuleIndexSection);
|
|
515
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
516
|
-
} else {
|
|
517
|
-
// Fallback: append to end
|
|
518
|
-
const newContent = content.trimEnd() + '\n\n- **Additional**: @.agent/rules/additional.md\n';
|
|
519
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
async function appendToCodex(projectPath: string, guidelines: string[]): Promise<void> {
|
|
525
|
-
const instructionsPath = join(projectPath, '.codex', 'instructions.md');
|
|
526
|
-
|
|
527
|
-
if (!existsSync(instructionsPath)) {
|
|
528
|
-
throw new Error('instructions.md not found');
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Create codex directory
|
|
532
|
-
const codexDir = join(projectPath, '.codex');
|
|
533
|
-
await mkdir(codexDir, { recursive: true });
|
|
534
|
-
|
|
535
|
-
// Create additional guidelines file
|
|
536
|
-
const additionalFile = join(codexDir, 'additional-guidelines.md');
|
|
537
|
-
const additionalContent = `# Additional Guidelines\n\n${guidelines.join('\n\n---\n\n')}`;
|
|
538
|
-
|
|
539
|
-
if (existsSync(additionalFile)) {
|
|
540
|
-
// Append to existing file
|
|
541
|
-
await appendFile(additionalFile, `\n\n---\n\n${guidelines.join('\n\n---\n\n')}`, 'utf-8');
|
|
542
|
-
} else {
|
|
543
|
-
// Create new file
|
|
544
|
-
await writeFile(additionalFile, additionalContent, 'utf-8');
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Update instructions.md with import reference if not already present
|
|
548
|
-
const content = await readFile(instructionsPath, 'utf-8');
|
|
549
|
-
|
|
550
|
-
if (content.includes('additional-guidelines.md')) {
|
|
551
|
-
// Reference already exists
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Add reference before the closing section
|
|
556
|
-
const newContent = content.trimEnd() + `\n\n## Additional Guidelines\n\nSee: additional-guidelines.md\n`;
|
|
557
|
-
await writeFile(instructionsPath, newContent, 'utf-8');
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
async function regenerateConfig(
|
|
561
|
-
projectPath: string,
|
|
562
|
-
existingConfig: ProfileSelection,
|
|
563
|
-
additionalIds: string[],
|
|
564
|
-
loader: GuidelineLoader
|
|
565
|
-
): Promise<void> {
|
|
566
|
-
// Get existing guidelines + new ones
|
|
567
|
-
const existingIds = loader.getGuidelinesForProfile(
|
|
568
|
-
existingConfig.language,
|
|
569
|
-
existingConfig.level,
|
|
570
|
-
existingConfig.architecture,
|
|
571
|
-
existingConfig.datasource
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
const allIds = [...new Set([...existingIds, ...additionalIds])];
|
|
575
|
-
|
|
576
|
-
// Regenerate configuration with all guidelines
|
|
577
|
-
const fileWriter = await AssistantFileWriter.create();
|
|
578
|
-
const files = await fileWriter.generateFiles(
|
|
579
|
-
existingConfig.assistant,
|
|
580
|
-
allIds,
|
|
581
|
-
existingConfig,
|
|
582
|
-
projectPath
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
await fileWriter.writeFiles(files);
|
|
586
|
-
}
|