@malamute/ai-rules 1.0.0 → 1.3.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 (145) hide show
  1. package/README.md +272 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/CLAUDE.md +52 -149
  4. package/configs/_shared/rules/conventions/documentation.md +324 -0
  5. package/configs/_shared/rules/conventions/git.md +265 -0
  6. package/configs/_shared/rules/conventions/npm.md +80 -0
  7. package/configs/_shared/{.claude/rules → rules/conventions}/performance.md +1 -1
  8. package/configs/_shared/rules/conventions/principles.md +334 -0
  9. package/configs/_shared/rules/devops/ci-cd.md +262 -0
  10. package/configs/_shared/rules/devops/docker.md +275 -0
  11. package/configs/_shared/rules/devops/nx.md +194 -0
  12. package/configs/_shared/rules/domain/backend/api-design.md +203 -0
  13. package/configs/_shared/rules/lang/csharp/async.md +220 -0
  14. package/configs/_shared/rules/lang/csharp/csharp.md +314 -0
  15. package/configs/_shared/rules/lang/csharp/linq.md +210 -0
  16. package/configs/_shared/rules/lang/python/async.md +337 -0
  17. package/configs/_shared/rules/lang/python/celery.md +476 -0
  18. package/configs/_shared/rules/lang/python/config.md +339 -0
  19. package/configs/{python/.claude/rules → _shared/rules/lang/python}/database/sqlalchemy.md +6 -1
  20. package/configs/_shared/rules/lang/python/deployment.md +523 -0
  21. package/configs/_shared/rules/lang/python/error-handling.md +330 -0
  22. package/configs/_shared/rules/lang/python/migrations.md +421 -0
  23. package/configs/_shared/rules/lang/python/python.md +172 -0
  24. package/configs/_shared/rules/lang/python/repository.md +383 -0
  25. package/configs/{python/.claude/rules → _shared/rules/lang/python}/testing.md +2 -69
  26. package/configs/_shared/rules/lang/typescript/async.md +447 -0
  27. package/configs/_shared/rules/lang/typescript/generics.md +356 -0
  28. package/configs/_shared/rules/lang/typescript/typescript.md +212 -0
  29. package/configs/_shared/rules/quality/error-handling.md +48 -0
  30. package/configs/_shared/rules/quality/logging.md +45 -0
  31. package/configs/_shared/rules/quality/observability.md +240 -0
  32. package/configs/_shared/rules/quality/testing-patterns.md +65 -0
  33. package/configs/_shared/rules/security/secrets-management.md +222 -0
  34. package/configs/_shared/skills/analysis/explore/SKILL.md +257 -0
  35. package/configs/_shared/skills/analysis/security-audit/SKILL.md +184 -0
  36. package/configs/_shared/skills/dev/api-endpoint/SKILL.md +126 -0
  37. package/configs/_shared/{.claude/commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  38. package/configs/_shared/{.claude/commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  39. package/configs/_shared/{.claude/commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  40. package/configs/_shared/skills/infra/deploy/SKILL.md +139 -0
  41. package/configs/_shared/skills/infra/docker/SKILL.md +95 -0
  42. package/configs/_shared/skills/infra/migration/SKILL.md +158 -0
  43. package/configs/_shared/skills/nx/nx-affected/SKILL.md +72 -0
  44. package/configs/_shared/skills/nx/nx-lib/SKILL.md +375 -0
  45. package/configs/angular/CLAUDE.md +24 -216
  46. package/configs/angular/{.claude/rules → rules/core}/components.md +69 -15
  47. package/configs/angular/rules/core/resource.md +285 -0
  48. package/configs/angular/rules/core/signals.md +323 -0
  49. package/configs/angular/rules/http.md +338 -0
  50. package/configs/angular/rules/routing.md +291 -0
  51. package/configs/angular/rules/ssr.md +312 -0
  52. package/configs/angular/rules/state/signal-store.md +408 -0
  53. package/configs/angular/{.claude/rules → rules/state}/state.md +2 -2
  54. package/configs/angular/{.claude/rules → rules}/testing.md +7 -7
  55. package/configs/angular/rules/ui/aria.md +422 -0
  56. package/configs/angular/rules/ui/forms.md +424 -0
  57. package/configs/angular/rules/ui/pipes-directives.md +335 -0
  58. package/configs/angular/{.claude/settings.json → settings.json} +3 -0
  59. package/configs/dotnet/CLAUDE.md +53 -286
  60. package/configs/dotnet/rules/background-services.md +552 -0
  61. package/configs/dotnet/rules/configuration.md +426 -0
  62. package/configs/dotnet/rules/ddd.md +447 -0
  63. package/configs/dotnet/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/rules/mediatr.md +320 -0
  65. package/configs/dotnet/rules/middleware.md +489 -0
  66. package/configs/dotnet/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/rules/validation.md +388 -0
  68. package/configs/dotnet/settings.json +29 -0
  69. package/configs/fastapi/CLAUDE.md +144 -0
  70. package/configs/fastapi/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/rules/dependencies.md +170 -0
  72. package/configs/{python/.claude → fastapi}/rules/fastapi.md +61 -1
  73. package/configs/fastapi/rules/lifespan.md +274 -0
  74. package/configs/fastapi/rules/middleware.md +229 -0
  75. package/configs/fastapi/rules/pydantic.md +433 -0
  76. package/configs/fastapi/rules/responses.md +251 -0
  77. package/configs/fastapi/rules/routers.md +202 -0
  78. package/configs/fastapi/rules/security.md +222 -0
  79. package/configs/fastapi/rules/testing.md +251 -0
  80. package/configs/fastapi/rules/websockets.md +298 -0
  81. package/configs/fastapi/settings.json +35 -0
  82. package/configs/flask/CLAUDE.md +166 -0
  83. package/configs/flask/rules/blueprints.md +208 -0
  84. package/configs/flask/rules/cli.md +285 -0
  85. package/configs/flask/rules/configuration.md +281 -0
  86. package/configs/flask/rules/context.md +238 -0
  87. package/configs/flask/rules/error-handlers.md +278 -0
  88. package/configs/flask/rules/extensions.md +278 -0
  89. package/configs/flask/rules/flask.md +171 -0
  90. package/configs/flask/rules/marshmallow.md +206 -0
  91. package/configs/flask/rules/security.md +267 -0
  92. package/configs/flask/rules/testing.md +284 -0
  93. package/configs/flask/settings.json +35 -0
  94. package/configs/nestjs/CLAUDE.md +57 -215
  95. package/configs/nestjs/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/rules/filters.md +376 -0
  97. package/configs/nestjs/rules/interceptors.md +317 -0
  98. package/configs/nestjs/rules/middleware.md +321 -0
  99. package/configs/nestjs/{.claude/rules → rules}/modules.md +26 -0
  100. package/configs/nestjs/rules/pipes.md +351 -0
  101. package/configs/nestjs/rules/websockets.md +451 -0
  102. package/configs/nestjs/settings.json +31 -0
  103. package/configs/nextjs/CLAUDE.md +69 -331
  104. package/configs/nextjs/rules/api-routes.md +358 -0
  105. package/configs/nextjs/rules/authentication.md +355 -0
  106. package/configs/nextjs/{.claude/rules → rules}/components.md +52 -0
  107. package/configs/nextjs/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/rules/database.md +400 -0
  109. package/configs/nextjs/rules/middleware.md +303 -0
  110. package/configs/nextjs/rules/routing.md +324 -0
  111. package/configs/nextjs/rules/seo.md +350 -0
  112. package/configs/nextjs/rules/server-actions.md +353 -0
  113. package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +6 -6
  114. package/configs/nextjs/{.claude/settings.json → settings.json} +7 -0
  115. package/package.json +24 -9
  116. package/src/cli.js +218 -0
  117. package/src/config.js +63 -0
  118. package/src/index.js +4 -0
  119. package/src/installer.js +414 -0
  120. package/src/merge.js +109 -0
  121. package/src/tech-config.json +45 -0
  122. package/src/utils.js +88 -0
  123. package/configs/dotnet/.claude/settings.json +0 -9
  124. package/configs/nestjs/.claude/settings.json +0 -15
  125. package/configs/python/.claude/rules/flask.md +0 -332
  126. package/configs/python/.claude/settings.json +0 -18
  127. package/configs/python/CLAUDE.md +0 -273
  128. package/src/install.js +0 -315
  129. /package/configs/_shared/{.claude/rules → rules/domain/frontend}/accessibility.md +0 -0
  130. /package/configs/_shared/{.claude/rules → rules/security}/security.md +0 -0
  131. /package/configs/_shared/{.claude/skills → skills/dev}/debug/SKILL.md +0 -0
  132. /package/configs/_shared/{.claude/skills → skills/dev}/learning/SKILL.md +0 -0
  133. /package/configs/_shared/{.claude/skills → skills/dev}/spec/SKILL.md +0 -0
  134. /package/configs/_shared/{.claude/skills → skills/git}/review/SKILL.md +0 -0
  135. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  136. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  137. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  138. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  139. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  140. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  141. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  142. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  143. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  144. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  145. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -82,7 +82,7 @@ export const useUserStore = create<UserState>((set) => ({
82
82
 
83
83
  ```typescript
84
84
  // stores/app-store.ts
85
- import { create } from 'zustand';
85
+ import { create, StateCreator } from 'zustand';
86
86
 
87
87
  // User slice
88
88
  interface UserSlice {
@@ -90,7 +90,7 @@ interface UserSlice {
90
90
  setUser: (user: User | null) => void;
91
91
  }
92
92
 
93
- const createUserSlice = (set: any): UserSlice => ({
93
+ const createUserSlice: StateCreator<AppStore, [], [], UserSlice> = (set) => ({
94
94
  user: null,
95
95
  setUser: (user) => set({ user }),
96
96
  });
@@ -103,13 +103,13 @@ interface CartSlice {
103
103
  clearCart: () => void;
104
104
  }
105
105
 
106
- const createCartSlice = (set: any): CartSlice => ({
106
+ const createCartSlice: StateCreator<AppStore, [], [], CartSlice> = (set) => ({
107
107
  items: [],
108
- addItem: (item) => set((state: any) => ({
108
+ addItem: (item) => set((state) => ({
109
109
  items: [...state.items, item]
110
110
  })),
111
- removeItem: (id) => set((state: any) => ({
112
- items: state.items.filter((item: CartItem) => item.id !== id)
111
+ removeItem: (id) => set((state) => ({
112
+ items: state.items.filter((item) => item.id !== id)
113
113
  })),
114
114
  clearCart: () => set({ items: [] }),
115
115
  });
@@ -14,16 +14,23 @@
14
14
  "Bash(npm install *)",
15
15
  "Bash(npm ci)",
16
16
  "Bash(npx nx *)",
17
+ "Bash(npx prisma *)",
17
18
  "Read",
18
19
  "Edit",
19
20
  "Write"
20
21
  ],
21
22
  "deny": [
23
+ "Bash(git push *)",
24
+ "Bash(git push)",
22
25
  "Bash(rm -rf *)",
23
26
  "Bash(nx reset)",
24
27
  "Read(.env)",
25
28
  "Read(.env.*)",
26
29
  "Read(**/secrets/**)"
27
30
  ]
31
+ },
32
+ "env": {
33
+ "NODE_ENV": "development",
34
+ "NX_DAEMON": "true"
28
35
  }
29
36
  }
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@malamute/ai-rules",
3
- "version": "1.0.0",
3
+ "version": "1.3.0",
4
4
  "description": "Claude Code configuration boilerplates for Angular, Next.js, NestJS, .NET, Python and more",
5
- "main": "src/install.js",
6
- "bin": {
7
- "ai-rules": "./bin/cli.js"
8
- },
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": "./bin/cli.js",
9
8
  "scripts": {
10
- "test": "node bin/cli.js --help"
9
+ "test": "vitest run",
10
+ "test:watch": "vitest",
11
+ "test:coverage": "vitest run --coverage",
12
+ "lint": "eslint src bin",
13
+ "lint:rules": "node scripts/lint-rules.js",
14
+ "validate": "npm test && npm run lint:rules"
11
15
  },
12
16
  "keywords": [
13
17
  "claude",
@@ -24,12 +28,16 @@
24
28
  "python",
25
29
  "fastapi"
26
30
  ],
27
- "author": "",
31
+ "author": "Mehdi Chaabi",
28
32
  "license": "MIT",
29
33
  "repository": {
30
34
  "type": "git",
31
- "url": ""
35
+ "url": "git+https://github.com/SpaceMalamute/ai-rules.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/SpaceMalamute/ai-rules/issues"
32
39
  },
40
+ "homepage": "https://github.com/SpaceMalamute/ai-rules#readme",
33
41
  "files": [
34
42
  "bin",
35
43
  "src",
@@ -37,5 +45,12 @@
37
45
  ],
38
46
  "engines": {
39
47
  "node": ">=18.0.0"
40
- }
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.39.2",
51
+ "eslint": "^9.39.2",
52
+ "husky": "^9.1.7",
53
+ "vitest": "^2.0.0"
54
+ },
55
+ "packageManager": "yarn@4.12.0"
41
56
  }
package/src/cli.js ADDED
@@ -0,0 +1,218 @@
1
+ import readline from 'readline';
2
+ import { colors, log } from './utils.js';
3
+ import { VERSION, AVAILABLE_TECHS } from './config.js';
4
+ import { init, update, status, listTechnologies } from './installer.js';
5
+
6
+ function printUsage() {
7
+ console.log(`
8
+ ${colors.bold('AI Rules')} v${VERSION} - Claude Code configuration boilerplates
9
+
10
+ ${colors.bold('Usage:')}
11
+ ai-rules init [tech] [tech2] [options]
12
+ ai-rules update [options]
13
+ ai-rules status
14
+ ai-rules list
15
+
16
+ ${colors.bold('Commands:')}
17
+ init Install configuration (interactive if no tech specified)
18
+ update Update installed configs to latest version
19
+ status Show current installation status
20
+ list List available technologies
21
+
22
+ ${colors.bold('Technologies:')}
23
+ angular Angular 21 + Nx + NgRx
24
+ nextjs Next.js 15 + React 19
25
+ nestjs NestJS + Prisma/TypeORM
26
+ dotnet .NET 9 + EF Core
27
+ fastapi FastAPI + SQLAlchemy + Pydantic
28
+ flask Flask + SQLAlchemy + Marshmallow
29
+
30
+ ${colors.bold('Options:')}
31
+ --minimal Only install CLAUDE.md, settings.json, and tech rules (no shared skills/rules)
32
+ --target <dir> Target directory (default: current directory)
33
+ --dry-run Preview changes without writing files
34
+ --force Overwrite files without backup (update command)
35
+
36
+ ${colors.bold('Examples:')}
37
+ ai-rules init # Interactive mode
38
+ ai-rules init angular # Full install (skills + rules)
39
+ ai-rules init angular --minimal # Minimal install
40
+ ai-rules init nextjs --dry-run
41
+ ai-rules update
42
+ ai-rules update --force
43
+ ai-rules status
44
+ `);
45
+ }
46
+
47
+ async function prompt(question) {
48
+ const rl = readline.createInterface({
49
+ input: process.stdin,
50
+ output: process.stdout,
51
+ });
52
+
53
+ return new Promise((resolve) => {
54
+ rl.question(question, (answer) => {
55
+ rl.close();
56
+ resolve(answer.trim());
57
+ });
58
+ });
59
+ }
60
+
61
+ async function multiSelect(message, choices) {
62
+ console.log(`\n${colors.bold(message)}`);
63
+ console.log(colors.dim('(enter numbers separated by spaces, or "all")'));
64
+ console.log('');
65
+
66
+ choices.forEach((choice, i) => {
67
+ console.log(` ${colors.cyan(i + 1)}. ${choice.name} ${colors.dim(`- ${choice.description}`)}`);
68
+ });
69
+
70
+ console.log('');
71
+ const answer = await prompt('Your selection: ');
72
+
73
+ if (answer.toLowerCase() === 'all') {
74
+ return choices.map((c) => c.value);
75
+ }
76
+
77
+ const indices = answer
78
+ .split(/[\s,]+/)
79
+ .map((s) => parseInt(s, 10) - 1)
80
+ .filter((i) => i >= 0 && i < choices.length);
81
+
82
+ return indices.map((i) => choices[i].value);
83
+ }
84
+
85
+ async function interactiveInit() {
86
+ console.log(`\n${colors.bold('AI Rules')} - Interactive Setup\n`);
87
+
88
+ const techChoices = [
89
+ { name: 'Angular', value: 'angular', description: 'Angular 21 + Nx + NgRx + Signals' },
90
+ { name: 'Next.js', value: 'nextjs', description: 'Next.js 15 + React 19 + App Router' },
91
+ { name: 'NestJS', value: 'nestjs', description: 'NestJS 11 + Prisma/TypeORM + Passport' },
92
+ { name: '.NET', value: 'dotnet', description: '.NET 9 + ASP.NET Core + EF Core' },
93
+ { name: 'FastAPI', value: 'fastapi', description: 'FastAPI + SQLAlchemy 2.0 + Pydantic v2' },
94
+ { name: 'Flask', value: 'flask', description: 'Flask + SQLAlchemy 2.0 + Marshmallow' },
95
+ ];
96
+
97
+ const techs = await multiSelect('Select technologies:', techChoices);
98
+
99
+ if (techs.length === 0) {
100
+ log.error('No technology selected');
101
+ process.exit(1);
102
+ }
103
+
104
+ const extraChoices = [
105
+ { name: 'Skills', value: 'skills', description: '/learning, /review, /spec, /debug, etc.' },
106
+ { name: 'Shared Rules', value: 'rules', description: 'security, performance, accessibility' },
107
+ ];
108
+
109
+ const extras = await multiSelect('Include extras:', extraChoices);
110
+
111
+ const targetDir = await prompt(`Target directory ${colors.dim('(. for current)')}: `) || '.';
112
+
113
+ const options = {
114
+ target: targetDir === '.' ? null : targetDir,
115
+ withSkills: extras.includes('skills'),
116
+ withRules: extras.includes('rules'),
117
+ all: false,
118
+ dryRun: false,
119
+ force: false,
120
+ };
121
+
122
+ console.log('');
123
+ return { techs, options };
124
+ }
125
+
126
+ export async function run(args) {
127
+ if (args.includes('--help') || args.includes('-h')) {
128
+ printUsage();
129
+ return;
130
+ }
131
+
132
+ if (args.length === 0) {
133
+ printUsage();
134
+ return;
135
+ }
136
+
137
+ const command = args[0];
138
+
139
+ if (command === 'list') {
140
+ listTechnologies();
141
+ return;
142
+ }
143
+
144
+ if (command === 'status') {
145
+ const targetIndex = args.indexOf('--target');
146
+ const targetDir = targetIndex !== -1 ? args[targetIndex + 1] : process.cwd();
147
+ status(targetDir);
148
+ return;
149
+ }
150
+
151
+ if (command === 'update') {
152
+ const options = {
153
+ target: null,
154
+ dryRun: args.includes('--dry-run'),
155
+ force: args.includes('--force'),
156
+ };
157
+
158
+ const targetIndex = args.indexOf('--target');
159
+ if (targetIndex !== -1) {
160
+ options.target = args[targetIndex + 1];
161
+ }
162
+
163
+ await update(options);
164
+ return;
165
+ }
166
+
167
+ if (command === 'init') {
168
+ const minimal = args.includes('--minimal');
169
+ const options = {
170
+ target: null,
171
+ withSkills: !minimal,
172
+ withRules: !minimal,
173
+ dryRun: args.includes('--dry-run'),
174
+ force: args.includes('--force'),
175
+ };
176
+
177
+ const techs = [];
178
+
179
+ for (let i = 1; i < args.length; i++) {
180
+ const arg = args[i];
181
+
182
+ if (arg === '--minimal') {
183
+ // Already handled above
184
+ } else if (arg === '--target') {
185
+ options.target = args[++i];
186
+ } else if (arg === '--dry-run' || arg === '--force') {
187
+ // Already handled
188
+ } else if (!arg.startsWith('-')) {
189
+ if (AVAILABLE_TECHS.includes(arg)) {
190
+ techs.push(arg);
191
+ } else {
192
+ log.error(`Unknown technology: ${arg}`);
193
+ console.log(`Available: ${AVAILABLE_TECHS.join(', ')}`);
194
+ process.exit(1);
195
+ }
196
+ }
197
+ }
198
+
199
+ // Interactive mode if no techs specified
200
+ if (techs.length === 0) {
201
+ try {
202
+ const result = await interactiveInit();
203
+ init(result.techs, { ...options, ...result.options });
204
+ } catch (_e) {
205
+ console.log('\nAborted.');
206
+ process.exit(0);
207
+ }
208
+ return;
209
+ }
210
+
211
+ init(techs, options);
212
+ return;
213
+ }
214
+
215
+ log.error(`Unknown command: ${command}`);
216
+ printUsage();
217
+ process.exit(1);
218
+ }
package/src/config.js ADDED
@@ -0,0 +1,63 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { createRequire } from 'module';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const require = createRequire(import.meta.url);
10
+
11
+ export const CONFIGS_DIR = path.join(__dirname, '..', 'configs');
12
+ export const AVAILABLE_TECHS = ['angular', 'nextjs', 'nestjs', 'dotnet', 'fastapi', 'flask'];
13
+ export const VERSION = require('../package.json').version;
14
+
15
+ export const TECH_CONFIG = JSON.parse(
16
+ fs.readFileSync(path.join(__dirname, 'tech-config.json'), 'utf8')
17
+ );
18
+
19
+ /**
20
+ * Get rule directories to include based on selected technologies.
21
+ * Returns paths relative to the rules directory (e.g., "lang/typescript", "domain/frontend").
22
+ */
23
+ export function getRulePathsToInclude(techs) {
24
+ const paths = new Set();
25
+ const { ruleMapping, alwaysInclude } = TECH_CONFIG;
26
+
27
+ // Always include common rules
28
+ alwaysInclude.forEach((dir) => paths.add(dir));
29
+
30
+ // Add language and domain-specific rules based on selected techs
31
+ for (const tech of techs) {
32
+ const config = TECH_CONFIG.technologies[tech];
33
+ if (!config) continue;
34
+
35
+ // Add language-specific rules (e.g., "lang/typescript")
36
+ if (config.language && ruleMapping.language[config.language]) {
37
+ paths.add(ruleMapping.language[config.language]);
38
+ }
39
+
40
+ // Add domain-specific rules (e.g., "domain/frontend")
41
+ if (config.type && ruleMapping.type[config.type]) {
42
+ paths.add(ruleMapping.type[config.type]);
43
+ }
44
+ }
45
+
46
+ return paths;
47
+ }
48
+
49
+ /**
50
+ * Check if a rule path should be included for the given technologies.
51
+ * @param {string} rulePath - Path relative to rules dir (e.g., "lang/python/async.md")
52
+ * @param {Set<string>} includedPaths - Set of paths to include
53
+ * @returns {boolean}
54
+ */
55
+ export function shouldIncludeRule(rulePath, includedPaths) {
56
+ // Check if the path starts with any of the included paths
57
+ for (const includedPath of includedPaths) {
58
+ if (rulePath === includedPath || rulePath.startsWith(includedPath + '/')) {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ }
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { run } from './cli.js';
2
+ export { init, update, status } from './installer.js';
3
+ export { readManifest } from './merge.js';
4
+ export { VERSION } from './config.js';