@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,311 +0,0 @@
|
|
|
1
|
-
import { GuidelineMapping } from '../embedded-data.js';
|
|
2
|
-
import { Language } from '../models/project.js';
|
|
3
|
-
import { InstructionLevel, ArchitectureType, DatasourceType } from '../models/profile.js';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
-
import {
|
|
7
|
-
DataSource,
|
|
8
|
-
HybridDataSource,
|
|
9
|
-
FileSystemDataSource,
|
|
10
|
-
CustomMappingsDataSource,
|
|
11
|
-
EmbeddedDataSource,
|
|
12
|
-
GuidelineData
|
|
13
|
-
} from './data-source.js';
|
|
14
|
-
import { CONFIG } from '../config.js';
|
|
15
|
-
|
|
16
|
-
export type { GuidelineMapping };
|
|
17
|
-
|
|
18
|
-
export class GuidelineLoader {
|
|
19
|
-
private mappings: Record<string, GuidelineMapping>;
|
|
20
|
-
private guidelines: Record<string, string>;
|
|
21
|
-
private version: string;
|
|
22
|
-
|
|
23
|
-
static async create(dataSource?: DataSource): Promise<GuidelineLoader> {
|
|
24
|
-
const source = dataSource || GuidelineLoader.createDefaultDataSource();
|
|
25
|
-
const data = await source.load();
|
|
26
|
-
return new GuidelineLoader(data);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private static createDefaultDataSource(): DataSource {
|
|
30
|
-
const homeDir = homedir();
|
|
31
|
-
const userDataPath = join(homeDir, CONFIG.CACHE_DIR_NAME, CONFIG.DATA_DIR);
|
|
32
|
-
const officialCachePath = join(homeDir, CONFIG.CACHE_DIR_NAME, CONFIG.CACHE_DIR);
|
|
33
|
-
|
|
34
|
-
return new HybridDataSource([
|
|
35
|
-
new CustomMappingsDataSource(userDataPath), // Highest priority
|
|
36
|
-
new FileSystemDataSource(officialCachePath), // Official updates
|
|
37
|
-
new EmbeddedDataSource() // Fallback
|
|
38
|
-
]);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private constructor(data: GuidelineData) {
|
|
42
|
-
this.mappings = data.mappings;
|
|
43
|
-
this.guidelines = data.guidelines;
|
|
44
|
-
this.version = data.version || 'unknown';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getVersion(): string {
|
|
48
|
-
return this.version;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getAllGuidelines(): Array<{ id: string; mapping: GuidelineMapping }> {
|
|
52
|
-
return Object.entries(this.mappings).map(([id, mapping]) => ({
|
|
53
|
-
id,
|
|
54
|
-
mapping
|
|
55
|
-
}));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
getGuidelinesForProfile(
|
|
59
|
-
language: Language,
|
|
60
|
-
level: InstructionLevel,
|
|
61
|
-
architecture: ArchitectureType,
|
|
62
|
-
datasource?: DatasourceType
|
|
63
|
-
): string[] {
|
|
64
|
-
const matchingGuidelines: string[] = [];
|
|
65
|
-
|
|
66
|
-
for (const [guidelineId, mapping] of Object.entries(this.mappings)) {
|
|
67
|
-
// Language filter
|
|
68
|
-
if (mapping.languages && !mapping.languages.includes(language)) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Level filter
|
|
73
|
-
if (mapping.levels && !mapping.levels.includes(level)) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Architecture filter - SKIP for "full" level (include all architectures)
|
|
78
|
-
if (level !== 'full' && mapping.architectures && !mapping.architectures.includes(architecture)) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Datasource filter - skip if guideline requires specific datasource that doesn't match
|
|
83
|
-
if (datasource && mapping.datasources && !mapping.datasources.includes(datasource)) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Skip database guidelines entirely if datasource is 'none'
|
|
88
|
-
if (datasource === 'none' && mapping.category === 'Database') {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
matchingGuidelines.push(guidelineId);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return matchingGuidelines;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
loadGuideline(guidelineId: string): string {
|
|
99
|
-
const mapping = this.mappings[guidelineId];
|
|
100
|
-
if (!mapping) {
|
|
101
|
-
throw new Error(`Guideline not found: ${guidelineId}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const content = this.guidelines[mapping.path];
|
|
105
|
-
if (!content) {
|
|
106
|
-
throw new Error(`Guideline content not found: ${mapping.path}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return content;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
getMapping(guidelineId: string): GuidelineMapping | undefined {
|
|
113
|
-
return this.mappings[guidelineId];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
assembleProfile(
|
|
117
|
-
language: Language,
|
|
118
|
-
level: InstructionLevel,
|
|
119
|
-
architecture: ArchitectureType,
|
|
120
|
-
datasource?: DatasourceType
|
|
121
|
-
): string {
|
|
122
|
-
const guidelineIds = this.getGuidelinesForProfile(language, level, architecture, datasource);
|
|
123
|
-
|
|
124
|
-
if (guidelineIds.length === 0) {
|
|
125
|
-
throw new Error(`No guidelines found for profile: ${language}-${level}-${architecture}`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const guidelineContents = guidelineIds.map(id => this.loadGuideline(id));
|
|
129
|
-
return guidelineContents.join('\n\n---\n\n');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getGuidelinesByCategory(language?: Language, level?: InstructionLevel, architecture?: ArchitectureType, datasource?: DatasourceType): Record<string, string[]> {
|
|
133
|
-
const byCategory: Record<string, string[]> = {};
|
|
134
|
-
|
|
135
|
-
for (const [guidelineId, mapping] of Object.entries(this.mappings)) {
|
|
136
|
-
if (language && mapping.languages && !mapping.languages.includes(language)) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (level && mapping.levels && !mapping.levels.includes(level)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Architecture filter - SKIP for "full" level (include all architectures)
|
|
145
|
-
if (level !== 'full' && architecture && mapping.architectures && !mapping.architectures.includes(architecture)) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (datasource && mapping.datasources && !mapping.datasources.includes(datasource)) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (datasource === 'none' && mapping.category === 'Database') {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const category = mapping.category || 'General';
|
|
158
|
-
if (!byCategory[category]) {
|
|
159
|
-
byCategory[category] = [];
|
|
160
|
-
}
|
|
161
|
-
byCategory[category].push(guidelineId);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return byCategory;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
getCategoryTree(language?: Language, level?: InstructionLevel, architecture?: ArchitectureType, datasource?: DatasourceType): CategoryNode[] {
|
|
168
|
-
const byCategory = this.getGuidelinesByCategory(language, level, architecture, datasource);
|
|
169
|
-
|
|
170
|
-
return Object.entries(byCategory)
|
|
171
|
-
.map(([name, guidelineIds]) => ({
|
|
172
|
-
name,
|
|
173
|
-
count: guidelineIds.length,
|
|
174
|
-
guidelines: guidelineIds.map(id => ({
|
|
175
|
-
id,
|
|
176
|
-
name: this.mappings[id].path.split('/').pop()?.replace('.md', '') || id,
|
|
177
|
-
path: this.mappings[id].path
|
|
178
|
-
}))
|
|
179
|
-
}))
|
|
180
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
getMetrics(guidelineIds: string[]): ProfileMetrics {
|
|
184
|
-
const categories: Record<string, number> = {};
|
|
185
|
-
|
|
186
|
-
for (const id of guidelineIds) {
|
|
187
|
-
const mapping = this.mappings[id];
|
|
188
|
-
if (mapping) {
|
|
189
|
-
const category = mapping.category || 'General';
|
|
190
|
-
categories[category] = (categories[category] || 0) + 1;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
guidelineCount: guidelineIds.length,
|
|
196
|
-
hooksCount: this.estimateHooks(guidelineIds),
|
|
197
|
-
subAgentsCount: this.estimateSubAgents(guidelineIds),
|
|
198
|
-
estimatedSize: this.estimateSize(guidelineIds),
|
|
199
|
-
categories: Object.entries(categories).map(([name, count]) => ({ name, count }))
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private estimateHooks(guidelineIds: string[]): number {
|
|
204
|
-
const hasFormatting = guidelineIds.some(id => id.includes('style') || id.includes('typescript') || id.includes('python'));
|
|
205
|
-
const hasSecurity = guidelineIds.some(id => id.includes('security'));
|
|
206
|
-
const hasTesting = guidelineIds.some(id => id.includes('testing'));
|
|
207
|
-
|
|
208
|
-
let count = 0;
|
|
209
|
-
if (hasFormatting) count += 1;
|
|
210
|
-
if (hasSecurity) count += 2;
|
|
211
|
-
if (hasTesting) count += 1;
|
|
212
|
-
return count;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private estimateSubAgents(guidelineIds: string[]): number {
|
|
216
|
-
const hasArchitecture = guidelineIds.some(id => id.includes('architecture'));
|
|
217
|
-
const hasSecurity = guidelineIds.some(id => id.includes('security'));
|
|
218
|
-
const hasStyle = guidelineIds.some(id => id.includes('style'));
|
|
219
|
-
|
|
220
|
-
let count = 1;
|
|
221
|
-
if (hasArchitecture) count += 1;
|
|
222
|
-
if (hasSecurity) count += 1;
|
|
223
|
-
if (hasStyle && guidelineIds.length > 10) count += 1;
|
|
224
|
-
return count;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
private estimateSize(guidelineIds: string[]): string {
|
|
228
|
-
const avgSizePerGuideline = 2.5;
|
|
229
|
-
const totalKB = guidelineIds.length * avgSizePerGuideline;
|
|
230
|
-
|
|
231
|
-
if (totalKB < 1) return '<1 KB';
|
|
232
|
-
if (totalKB < 10) return `~${Math.round(totalKB)} KB`;
|
|
233
|
-
return `~${Math.round(totalKB / 10) * 10} KB`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
getStats(): {
|
|
237
|
-
totalGuidelines: number;
|
|
238
|
-
byLanguage: Record<string, number>;
|
|
239
|
-
byLevel: Record<string, number>;
|
|
240
|
-
byArchitecture: Record<string, number>;
|
|
241
|
-
byCategory: Record<string, number>;
|
|
242
|
-
byTag: Record<string, number>;
|
|
243
|
-
} {
|
|
244
|
-
const stats = {
|
|
245
|
-
totalGuidelines: Object.keys(this.mappings).length,
|
|
246
|
-
byLanguage: {} as Record<string, number>,
|
|
247
|
-
byLevel: {} as Record<string, number>,
|
|
248
|
-
byArchitecture: {} as Record<string, number>,
|
|
249
|
-
byCategory: {} as Record<string, number>,
|
|
250
|
-
byTag: {} as Record<string, number>
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
for (const mapping of Object.values(this.mappings)) {
|
|
254
|
-
if (mapping.languages) {
|
|
255
|
-
for (const lang of mapping.languages) {
|
|
256
|
-
stats.byLanguage[lang] = (stats.byLanguage[lang] || 0) + 1;
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
stats.byLanguage['all'] = (stats.byLanguage['all'] || 0) + 1;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (mapping.levels) {
|
|
263
|
-
for (const lvl of mapping.levels) {
|
|
264
|
-
stats.byLevel[lvl] = (stats.byLevel[lvl] || 0) + 1;
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
stats.byLevel['all'] = (stats.byLevel['all'] || 0) + 1;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (mapping.architectures) {
|
|
271
|
-
for (const arch of mapping.architectures) {
|
|
272
|
-
stats.byArchitecture[arch] = (stats.byArchitecture[arch] || 0) + 1;
|
|
273
|
-
}
|
|
274
|
-
} else {
|
|
275
|
-
stats.byArchitecture['all'] = (stats.byArchitecture['all'] || 0) + 1;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const category = mapping.category || 'General';
|
|
279
|
-
stats.byCategory[category] = (stats.byCategory[category] || 0) + 1;
|
|
280
|
-
|
|
281
|
-
if (mapping.tags) {
|
|
282
|
-
for (const tag of mapping.tags) {
|
|
283
|
-
stats.byTag[tag] = (stats.byTag[tag] || 0) + 1;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return stats;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export interface CategoryNode {
|
|
293
|
-
name: string;
|
|
294
|
-
count: number;
|
|
295
|
-
guidelines: {
|
|
296
|
-
id: string;
|
|
297
|
-
name: string;
|
|
298
|
-
path: string;
|
|
299
|
-
}[];
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export interface ProfileMetrics {
|
|
303
|
-
guidelineCount: number;
|
|
304
|
-
hooksCount: number;
|
|
305
|
-
subAgentsCount: number;
|
|
306
|
-
estimatedSize: string;
|
|
307
|
-
categories: {
|
|
308
|
-
name: string;
|
|
309
|
-
count: number;
|
|
310
|
-
}[];
|
|
311
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
export interface HookEntry {
|
|
2
|
-
matcher?: string;
|
|
3
|
-
hooks?: HookEntry[];
|
|
4
|
-
type?: string;
|
|
5
|
-
command?: string;
|
|
6
|
-
prompt?: string;
|
|
7
|
-
timeout?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type HookEventMap = Record<string, HookEntry[]>;
|
|
11
|
-
|
|
12
|
-
export interface HookConfig {
|
|
13
|
-
name: string;
|
|
14
|
-
description: string;
|
|
15
|
-
hooks: HookEventMap;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const EMBEDDED_HOOKS: Record<string, HookConfig> = {
|
|
19
|
-
formatting: {
|
|
20
|
-
name: 'Auto-format on file write',
|
|
21
|
-
description: 'Automatically format code files after writing',
|
|
22
|
-
hooks: {
|
|
23
|
-
PostToolUse: [
|
|
24
|
-
{
|
|
25
|
-
matcher: 'Write(src/**/*.ts)',
|
|
26
|
-
hooks: [
|
|
27
|
-
{
|
|
28
|
-
type: 'command',
|
|
29
|
-
command: 'npx prettier --write "${CLAUDE_FILE}" 2>/dev/null || true'
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
matcher: 'Write(src/**/*.tsx)',
|
|
35
|
-
hooks: [
|
|
36
|
-
{
|
|
37
|
-
type: 'command',
|
|
38
|
-
command: 'npx prettier --write "${CLAUDE_FILE}" 2>/dev/null || true'
|
|
39
|
-
}
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
]
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
security: {
|
|
46
|
-
name: 'Block sensitive file access',
|
|
47
|
-
description: 'Prevent reading or modifying sensitive files',
|
|
48
|
-
hooks: {
|
|
49
|
-
PreToolUse: [
|
|
50
|
-
{
|
|
51
|
-
matcher: 'Read(.env*)',
|
|
52
|
-
hooks: [
|
|
53
|
-
{
|
|
54
|
-
type: 'command',
|
|
55
|
-
command: "echo 'Blocked: Sensitive file access not allowed' && exit 2"
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
matcher: 'Read(secrets/**)',
|
|
61
|
-
hooks: [
|
|
62
|
-
{
|
|
63
|
-
type: 'command',
|
|
64
|
-
command: "echo 'Blocked: Secrets directory is protected' && exit 2"
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
matcher: 'Write(.env*)',
|
|
70
|
-
hooks: [
|
|
71
|
-
{
|
|
72
|
-
type: 'command',
|
|
73
|
-
command: "echo 'Blocked: Cannot modify environment files' && exit 2"
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
testing: {
|
|
81
|
-
name: 'Verify tests before completion',
|
|
82
|
-
description: 'Ensure tests pass before task completion',
|
|
83
|
-
hooks: {
|
|
84
|
-
Stop: [
|
|
85
|
-
{
|
|
86
|
-
hooks: [
|
|
87
|
-
{
|
|
88
|
-
type: 'prompt',
|
|
89
|
-
prompt:
|
|
90
|
-
'Before completing this task, verify that:\n1. All tests pass\n2. No new failing tests were introduced\n3. Test coverage meets requirements\n\nIs the task truly complete with passing tests?',
|
|
91
|
-
timeout: 15
|
|
92
|
-
}
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export class HookGenerator {
|
|
101
|
-
async generateHooks(guidelineIds: string[]): Promise<HookEventMap> {
|
|
102
|
-
const mergedHooks: HookEventMap = {};
|
|
103
|
-
|
|
104
|
-
const hasFormatting = guidelineIds.some(
|
|
105
|
-
(id) => id.includes('style') || id.includes('typescript') || id.includes('python')
|
|
106
|
-
);
|
|
107
|
-
const hasSecurity = guidelineIds.some((id) => id.includes('security'));
|
|
108
|
-
const hasTesting = guidelineIds.some((id) => id.includes('testing'));
|
|
109
|
-
|
|
110
|
-
if (hasFormatting) {
|
|
111
|
-
this.mergeHooks(mergedHooks, EMBEDDED_HOOKS.formatting.hooks);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (hasSecurity) {
|
|
115
|
-
this.mergeHooks(mergedHooks, EMBEDDED_HOOKS.security.hooks);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (hasTesting) {
|
|
119
|
-
this.mergeHooks(mergedHooks, EMBEDDED_HOOKS.testing.hooks);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return mergedHooks;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private mergeHooks(target: HookEventMap, source: HookEventMap): void {
|
|
126
|
-
for (const [event, hooks] of Object.entries(source)) {
|
|
127
|
-
if (!target[event]) {
|
|
128
|
-
target[event] = [];
|
|
129
|
-
}
|
|
130
|
-
target[event].push(...hooks);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
generateClaudeCodeSettings(
|
|
135
|
-
hooks: HookEventMap,
|
|
136
|
-
projectPath: string,
|
|
137
|
-
level: 'basic' | 'standard' | 'expert' | 'full' = 'standard'
|
|
138
|
-
): string {
|
|
139
|
-
// Base permissions for all levels
|
|
140
|
-
const baseAllow = [
|
|
141
|
-
'Bash(npm run:*)',
|
|
142
|
-
'Bash(yarn:*)',
|
|
143
|
-
'Bash(pnpm:*)',
|
|
144
|
-
'Bash(bun:*)',
|
|
145
|
-
'Bash(git:*)',
|
|
146
|
-
'Bash(npx:*)',
|
|
147
|
-
'Read(src/**)',
|
|
148
|
-
'Read(package.json)',
|
|
149
|
-
'Read(tsconfig.json)',
|
|
150
|
-
'Read(CLAUDE.md)',
|
|
151
|
-
'Read(.claude/**)',
|
|
152
|
-
'Write(src/**)',
|
|
153
|
-
'Write(.claude/**)'
|
|
154
|
-
];
|
|
155
|
-
|
|
156
|
-
// Network permissions only for expert/full levels (least-privilege)
|
|
157
|
-
const allowInternet = level === 'expert' || level === 'full';
|
|
158
|
-
const allowWebSearch = level === 'expert' || level === 'full';
|
|
159
|
-
|
|
160
|
-
const allow = allowWebSearch ? [...baseAllow, 'WebSearch'] : baseAllow;
|
|
161
|
-
|
|
162
|
-
const settings = {
|
|
163
|
-
alwaysThinkingEnabled: true,
|
|
164
|
-
hooks,
|
|
165
|
-
permissions: {
|
|
166
|
-
allow,
|
|
167
|
-
deny: ['Read(.env*)', 'Read(secrets/**)', 'Bash(rm:*)', 'Bash(sudo:*)'],
|
|
168
|
-
ask: ['Write(package.json)', 'Write(.gitignore)', 'Write(.env.example)']
|
|
169
|
-
},
|
|
170
|
-
sandbox: {
|
|
171
|
-
allowInternetAccess: allowInternet,
|
|
172
|
-
workingDirectory: projectPath
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
return JSON.stringify(settings, null, 2);
|
|
177
|
-
}
|
|
178
|
-
}
|