@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.
Files changed (160) hide show
  1. package/{.claude/guidelines → .agent/rules}/api-design.md +5 -1
  2. package/{.claude/guidelines → .agent/rules}/architecture.md +5 -1
  3. package/{.claude/guidelines → .agent/rules}/best-practices.md +5 -1
  4. package/{.claude/guidelines → .agent/rules}/code-style.md +5 -1
  5. package/{.claude/guidelines → .agent/rules}/design-patterns.md +5 -1
  6. package/{.claude/guidelines → .agent/rules}/devops.md +5 -1
  7. package/{.claude/guidelines → .agent/rules}/error-handling.md +5 -1
  8. package/.agent/rules/instructions.md +28 -0
  9. package/{.claude/guidelines → .agent/rules}/language.md +5 -1
  10. package/{.claude/guidelines → .agent/rules}/performance.md +5 -1
  11. package/{.claude/guidelines → .agent/rules}/security.md +5 -1
  12. package/{.claude/guidelines → .agent/rules}/testing.md +5 -1
  13. package/.agent/workflows/add-documentation.md +10 -0
  14. package/.agent/workflows/generate-integration-tests.md +10 -0
  15. package/.agent/workflows/generate-unit-tests.md +11 -0
  16. package/.agent/workflows/performance-audit.md +11 -0
  17. package/.agent/workflows/refactor-extract-module.md +12 -0
  18. package/.agent/workflows/security-audit.md +12 -0
  19. package/.gemini/instructions.md +4843 -0
  20. package/.vs/ProjectSettings.json +2 -2
  21. package/.vs/VSWorkspaceState.json +15 -15
  22. package/.vs/aicgen.slnx/v18/DocumentLayout.json +53 -53
  23. package/AGENTS.md +9 -11
  24. package/assets/icon.svg +33 -33
  25. package/bun.lock +734 -26
  26. package/{CLAUDE.md → claude.md} +2 -2
  27. package/config.example.yml +129 -0
  28. package/config.yml +38 -0
  29. package/data/architecture/microservices/api-gateway.md +56 -56
  30. package/data/devops/observability.md +73 -73
  31. package/data/guideline-mappings.yml +128 -0
  32. package/data/language/dart/async.md +289 -0
  33. package/data/language/dart/basics.md +280 -0
  34. package/data/language/dart/error-handling.md +355 -0
  35. package/data/language/dart/index.md +10 -0
  36. package/data/language/dart/testing.md +352 -0
  37. package/data/language/swift/basics.md +477 -0
  38. package/data/language/swift/concurrency.md +654 -0
  39. package/data/language/swift/error-handling.md +679 -0
  40. package/data/language/swift/swiftui-mvvm.md +795 -0
  41. package/data/language/swift/testing.md +708 -0
  42. package/data/version.json +10 -8
  43. package/dist/index.js +50153 -28959
  44. package/jest.config.js +46 -0
  45. package/package.json +14 -3
  46. package/.claude/agents/architecture-reviewer.md +0 -88
  47. package/.claude/agents/guideline-checker.md +0 -73
  48. package/.claude/agents/security-auditor.md +0 -108
  49. package/.claude/settings.json +0 -98
  50. package/.claude/settings.local.json +0 -8
  51. package/.eslintrc.json +0 -28
  52. package/.github/workflows/release.yml +0 -180
  53. package/.github/workflows/test.yml +0 -81
  54. package/CONTRIBUTING.md +0 -821
  55. package/dist/commands/init.d.ts +0 -8
  56. package/dist/commands/init.d.ts.map +0 -1
  57. package/dist/commands/init.js +0 -46
  58. package/dist/commands/init.js.map +0 -1
  59. package/dist/config/profiles.d.ts +0 -4
  60. package/dist/config/profiles.d.ts.map +0 -1
  61. package/dist/config/profiles.js +0 -30
  62. package/dist/config/profiles.js.map +0 -1
  63. package/dist/config/settings.d.ts +0 -7
  64. package/dist/config/settings.d.ts.map +0 -1
  65. package/dist/config/settings.js +0 -7
  66. package/dist/config/settings.js.map +0 -1
  67. package/dist/index.d.ts +0 -3
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js.map +0 -1
  70. package/dist/models/guideline.d.ts +0 -15
  71. package/dist/models/guideline.d.ts.map +0 -1
  72. package/dist/models/guideline.js +0 -2
  73. package/dist/models/guideline.js.map +0 -1
  74. package/dist/models/preference.d.ts +0 -9
  75. package/dist/models/preference.d.ts.map +0 -1
  76. package/dist/models/preference.js +0 -2
  77. package/dist/models/preference.js.map +0 -1
  78. package/dist/models/profile.d.ts +0 -9
  79. package/dist/models/profile.d.ts.map +0 -1
  80. package/dist/models/profile.js +0 -2
  81. package/dist/models/profile.js.map +0 -1
  82. package/dist/models/project.d.ts +0 -13
  83. package/dist/models/project.d.ts.map +0 -1
  84. package/dist/models/project.js +0 -2
  85. package/dist/models/project.js.map +0 -1
  86. package/dist/services/ai/anthropic.d.ts +0 -7
  87. package/dist/services/ai/anthropic.d.ts.map +0 -1
  88. package/dist/services/ai/anthropic.js +0 -39
  89. package/dist/services/ai/anthropic.js.map +0 -1
  90. package/dist/services/generator.d.ts +0 -2
  91. package/dist/services/generator.d.ts.map +0 -1
  92. package/dist/services/generator.js +0 -4
  93. package/dist/services/generator.js.map +0 -1
  94. package/dist/services/learner.d.ts +0 -2
  95. package/dist/services/learner.d.ts.map +0 -1
  96. package/dist/services/learner.js +0 -4
  97. package/dist/services/learner.js.map +0 -1
  98. package/dist/services/scanner.d.ts +0 -3
  99. package/dist/services/scanner.d.ts.map +0 -1
  100. package/dist/services/scanner.js +0 -54
  101. package/dist/services/scanner.js.map +0 -1
  102. package/dist/utils/errors.d.ts +0 -15
  103. package/dist/utils/errors.d.ts.map +0 -1
  104. package/dist/utils/errors.js +0 -27
  105. package/dist/utils/errors.js.map +0 -1
  106. package/dist/utils/file.d.ts +0 -7
  107. package/dist/utils/file.d.ts.map +0 -1
  108. package/dist/utils/file.js +0 -32
  109. package/dist/utils/file.js.map +0 -1
  110. package/dist/utils/logger.d.ts +0 -6
  111. package/dist/utils/logger.d.ts.map +0 -1
  112. package/dist/utils/logger.js +0 -17
  113. package/dist/utils/logger.js.map +0 -1
  114. package/dist/utils/path.d.ts +0 -6
  115. package/dist/utils/path.d.ts.map +0 -1
  116. package/dist/utils/path.js +0 -14
  117. package/dist/utils/path.js.map +0 -1
  118. package/docs/planning/memory-lane.md +0 -83
  119. package/packaging/linux/aicgen.spec +0 -23
  120. package/packaging/linux/control +0 -9
  121. package/packaging/macos/scripts/postinstall +0 -12
  122. package/packaging/windows/setup.nsi +0 -92
  123. package/scripts/add-categories.ts +0 -87
  124. package/scripts/build-binary.ts +0 -46
  125. package/scripts/embed-data.ts +0 -105
  126. package/scripts/generate-version.ts +0 -150
  127. package/scripts/test-decompress.ts +0 -27
  128. package/scripts/test-extract.ts +0 -31
  129. package/src/__tests__/services/assistant-file-writer.test.ts +0 -400
  130. package/src/__tests__/services/guideline-loader.test.ts +0 -281
  131. package/src/__tests__/services/tarball-extraction.test.ts +0 -125
  132. package/src/commands/add-guideline.ts +0 -296
  133. package/src/commands/clear.ts +0 -61
  134. package/src/commands/guideline-selector.ts +0 -123
  135. package/src/commands/init.ts +0 -645
  136. package/src/commands/quick-add.ts +0 -586
  137. package/src/commands/remove-guideline.ts +0 -152
  138. package/src/commands/stats.ts +0 -49
  139. package/src/commands/update.ts +0 -240
  140. package/src/config.ts +0 -82
  141. package/src/embedded-data.ts +0 -1492
  142. package/src/index.ts +0 -67
  143. package/src/models/profile.ts +0 -24
  144. package/src/models/project.ts +0 -43
  145. package/src/services/assistant-file-writer.ts +0 -612
  146. package/src/services/config-generator.ts +0 -150
  147. package/src/services/config-manager.ts +0 -70
  148. package/src/services/data-source.ts +0 -248
  149. package/src/services/first-run-init.ts +0 -148
  150. package/src/services/guideline-loader.ts +0 -311
  151. package/src/services/hook-generator.ts +0 -178
  152. package/src/services/subagent-generator.ts +0 -310
  153. package/src/utils/banner.ts +0 -66
  154. package/src/utils/errors.ts +0 -27
  155. package/src/utils/file.ts +0 -67
  156. package/src/utils/formatting.ts +0 -172
  157. package/src/utils/logger.ts +0 -89
  158. package/src/utils/path.ts +0 -17
  159. package/src/utils/wizard-state.ts +0 -132
  160. 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
- }