ai-codebase-registry 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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @ai-meta
3
+ * @id module:ai-agent-infra:gen-side-effects
4
+ * @domain ai-agent-infra
5
+ * @type module
6
+ * @side-effects none
7
+ * @stability high
8
+ * @complexity moderate
9
+ * @token-budget 100
10
+ * @purpose Generates side-effects.json — side-effects graph from @side-effects annotations
11
+ *
12
+ * @filechangelog
13
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Extracted from generate-registry.mjs
14
+ */
15
+
16
+ import { generateSemanticId } from '../core/semantic-id.mjs';
17
+
18
+ /**
19
+ * Generate side-effects graph from manifest metadata.
20
+ *
21
+ * @param {object} manifest - File manifest with sideEffects field
22
+ * @returns {object} Side-effects registry
23
+ */
24
+ export function generateSideEffects(manifest) {
25
+ const effects = {};
26
+ const mutations = [];
27
+ const missingSideEffects = [];
28
+ let documentedCount = 0;
29
+
30
+ for (const [relPath, meta] of Object.entries(manifest)) {
31
+ const semanticId = meta.id || generateSemanticId(relPath, meta);
32
+
33
+ if (meta.sideEffects !== undefined) {
34
+ documentedCount++;
35
+ const sideEffectsList = meta.sideEffects === 'none'
36
+ ? []
37
+ : meta.sideEffects.split(',').map(s => s.trim()).filter(Boolean);
38
+
39
+ effects[semanticId] = {
40
+ id: semanticId,
41
+ file: relPath,
42
+ sideEffects: sideEffectsList,
43
+ downstreamOf: [],
44
+ };
45
+
46
+ for (const effect of sideEffectsList) {
47
+ if (effect.startsWith('writes:') || effect.startsWith('triggers:') || effect.startsWith('invalidates:')) {
48
+ mutations.push({ source: semanticId, effect, file: relPath });
49
+ }
50
+ }
51
+ } else {
52
+ missingSideEffects.push(relPath);
53
+ }
54
+ }
55
+
56
+ return {
57
+ version: '1.0',
58
+ generated: new Date().toISOString(),
59
+ documentedCount,
60
+ missingCount: missingSideEffects.length,
61
+ effects,
62
+ mutations,
63
+ missing_side_effects: missingSideEffects,
64
+ };
65
+ }
package/lib/index.mjs ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @ai-meta
3
+ * @id module:ai-agent-infra:index
4
+ * @domain ai-agent-infra
5
+ * @type index
6
+ * @side-effects none
7
+ * @stability high
8
+ * @complexity trivial
9
+ * @token-budget 50
10
+ * @purpose Main entry point for ai-codebase-registry package
11
+ *
12
+ * @filechangelog
13
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created package entry point
14
+ */
15
+
16
+ export { Registry } from './core/registry.mjs';
17
+ export { loadConfig } from './core/config-loader.mjs';
18
+ export { parseAiMeta } from './core/ai-meta-parser.mjs';
19
+ export { generateSemanticId } from './core/semantic-id.mjs';
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @ai-meta
3
+ * @id module:ai-agent-infra:imports-typescript
4
+ * @domain ai-agent-infra
5
+ * @type module
6
+ * @side-effects none
7
+ * @stability high
8
+ * @complexity moderate
9
+ * @token-budget 200
10
+ * @purpose Parses TypeScript/ESM import and export specifiers from source files; resolves aliases to project paths
11
+ *
12
+ * @filechangelog
13
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Extracted from generate-registry.mjs, parameterized aliases
14
+ */
15
+
16
+ import { join, dirname } from 'path';
17
+
18
+ /**
19
+ * Extract import/export specifiers from TypeScript/ESM file content.
20
+ * Returns an array of raw module specifiers.
21
+ *
22
+ * @param {string} content - File content
23
+ * @returns {string[]} Array of module specifiers
24
+ */
25
+ export function parseImports(content) {
26
+ const specifiers = new Set();
27
+
28
+ // Normalize multi-line imports
29
+ const normalized = content.replace(/import\s*\{[^}]*\}/gs, (match) =>
30
+ match.replace(/\n/g, ' ')
31
+ );
32
+
33
+ // Static imports: import ... from '...'
34
+ const staticRe = /(?:import|export)\s+(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
35
+ let match;
36
+ while ((match = staticRe.exec(normalized)) !== null) {
37
+ specifiers.add(match[1]);
38
+ }
39
+
40
+ // Side-effect imports: import '...'
41
+ const sideEffectRe = /import\s+['"]([^'"]+)['"]/g;
42
+ while ((match = sideEffectRe.exec(normalized)) !== null) {
43
+ specifiers.add(match[1]);
44
+ }
45
+
46
+ // Dynamic imports: import('...')
47
+ const dynamicRe = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
48
+ while ((match = dynamicRe.exec(content)) !== null) {
49
+ specifiers.add(match[1]);
50
+ }
51
+
52
+ // Re-exports
53
+ const reExportRe = /export\s+(?:\*|\{[^}]*\})\s+from\s+['"]([^'"]+)['"]/g;
54
+ while ((match = reExportRe.exec(content)) !== null) {
55
+ specifiers.add(match[1]);
56
+ }
57
+
58
+ return [...specifiers];
59
+ }
60
+
61
+ /**
62
+ * Create a module specifier resolver with project-specific aliases.
63
+ *
64
+ * @param {object} aliasMap - Map of alias prefixes to target paths
65
+ * @param {Set<string>} allFilePaths - Set of all known file paths for resolution
66
+ * @returns {(specifier: string, fromRelPath: string) => string|null}
67
+ */
68
+ export function createResolver(aliasMap, allFilePaths) {
69
+ const aliasEntries = Object.entries(aliasMap);
70
+
71
+ return function resolveSpecifier(specifier, fromRelPath) {
72
+ const isAliased = aliasEntries.some(([alias]) => specifier.startsWith(alias));
73
+ if (!specifier.startsWith('.') && !isAliased) {
74
+ return null;
75
+ }
76
+
77
+ let basePath;
78
+
79
+ for (const [alias, target] of aliasEntries) {
80
+ if (specifier.startsWith(alias)) {
81
+ basePath = join(target, specifier.slice(alias.length));
82
+ break;
83
+ }
84
+ }
85
+
86
+ if (!basePath && specifier.startsWith('.')) {
87
+ basePath = join(dirname(fromRelPath), specifier);
88
+ }
89
+
90
+ if (!basePath) return null;
91
+ basePath = basePath.replace(/\\/g, '/');
92
+
93
+ const candidates = [
94
+ basePath, basePath + '.ts', basePath + '.tsx',
95
+ basePath + '/index.ts', basePath + '/index.tsx',
96
+ ];
97
+
98
+ for (const c of candidates) {
99
+ if (allFilePaths.has(c)) return c;
100
+ }
101
+
102
+ return null;
103
+ };
104
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @ai-meta
3
+ * @id index:ai-agent-infra:parsers-index
4
+ * @domain ai-agent-infra
5
+ * @type index
6
+ * @side-effects none
7
+ * @stability high
8
+ * @complexity trivial
9
+ * @token-budget 20
10
+ * @purpose Barrel export for all parsers
11
+ *
12
+ * @filechangelog
13
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created parsers barrel export
14
+ */
15
+
16
+ export { parseImports, createResolver } from './imports-typescript.mjs';
@@ -0,0 +1,254 @@
1
+ /**
2
+ * @ai-meta
3
+ * @id module:ai-agent-infra:query-engine
4
+ * @domain ai-agent-infra
5
+ * @type module
6
+ * @side-effects none
7
+ * @stability medium
8
+ * @complexity complex
9
+ * @token-budget 300
10
+ * @purpose Query engine for registry data — supports domain/type/impact/coverage/orphaned-routes queries
11
+ *
12
+ * @filechangelog
13
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created query engine with all query modes
14
+ */
15
+
16
+ import { Registry } from '../core/registry.mjs';
17
+
18
+ // ANSI colors
19
+ const C = {
20
+ reset: '\x1b[0m',
21
+ bold: '\x1b[1m',
22
+ dim: '\x1b[2m',
23
+ cyan: '\x1b[36m',
24
+ yellow: '\x1b[33m',
25
+ green: '\x1b[32m',
26
+ red: '\x1b[31m',
27
+ magenta: '\x1b[35m',
28
+ };
29
+
30
+ /**
31
+ * Execute a query against the registry and return formatted results.
32
+ *
33
+ * @param {Registry} registry - Loaded registry instance
34
+ * @param {object} query - Query parameters
35
+ * @param {{ json?: boolean }} options
36
+ * @returns {{ output: string, data: any }}
37
+ */
38
+ export function executeQuery(registry, query, options = {}) {
39
+ if (query.help) return formatHelp();
40
+ if (query.coverage) return formatCoverage(registry, options);
41
+ if (query.orphanedRoutes) return formatOrphanedRoutes(registry, options);
42
+ if (query.id) return formatIdLookup(registry, query.id, options);
43
+ if (query.consumers) return formatConsumers(registry, query.consumers, options);
44
+ if (query.deps) return formatDeps(registry, query.deps, options);
45
+ if (query.impact) return formatImpact(registry, query.impact, options);
46
+ if (query.sideEffects) return formatSideEffects(registry, query.sideEffects, options);
47
+ if (query.complexity) return formatComplexity(registry, query.complexity, options);
48
+ if (query.domain) return formatDomainQuery(registry, query.domain, query.type, options);
49
+ return formatHelp();
50
+ }
51
+
52
+ function formatHelp() {
53
+ const output = `
54
+ ${C.bold}ai-codebase-registry query${C.reset}
55
+
56
+ ${C.bold}Usage:${C.reset}
57
+ ai-registry query [options]
58
+ node scripts/query-registry.mjs [options]
59
+
60
+ ${C.bold}Options:${C.reset}
61
+ --domain <name> Filter files by domain
62
+ --type <type> Filter files by type (combine with --domain)
63
+ --id <semantic-id> Lookup file by semantic ID
64
+ --consumers <filepath> Show who imports this file
65
+ --deps <filepath> Show what this file imports
66
+ --impact <filepath> Full impact analysis for changing a file
67
+ --side-effects <pattern> Find files with matching side-effects
68
+ --complexity <level> Find files at/above complexity (trivial|moderate|complex)
69
+ --orphaned-routes Find routes with no nav entry point
70
+ --coverage Show @ai-meta coverage statistics
71
+ --json Output as JSON (for piping)
72
+ --help Show this help
73
+ `;
74
+ return { output, data: null };
75
+ }
76
+
77
+ function formatDomainQuery(registry, domain, type, options) {
78
+ const results = registry.query({ domain, type });
79
+ if (options.json) return { output: JSON.stringify(results, null, 2), data: results };
80
+
81
+ const header = type
82
+ ? `Registry Query: domain=${domain}, type=${type}`
83
+ : `Registry Query: domain=${domain}`;
84
+
85
+ let output = `\n${C.bold}${header}${C.reset}\n${'='.repeat(header.length)}\n`;
86
+ for (const r of results) {
87
+ const purpose = r.purpose ? ` ${C.dim}"${r.purpose}"${C.reset}` : '';
88
+ output += ` ${C.cyan}${r.path}${C.reset} ${C.yellow}${r.type}${C.reset} ${r.size} lines${purpose}\n`;
89
+ }
90
+ output += `\n${C.green}Found ${results.length} files${C.reset}\n`;
91
+ return { output, data: results };
92
+ }
93
+
94
+ function formatIdLookup(registry, id, options) {
95
+ const result = registry.resolveWithMeta(id);
96
+ if (!result) {
97
+ const output = `${C.red}No file found for semantic ID: ${id}${C.reset}\n`;
98
+ return { output, data: null };
99
+ }
100
+ if (options.json) return { output: JSON.stringify(result, null, 2), data: result };
101
+
102
+ let output = `\n${C.bold}Semantic ID: ${id}${C.reset}\n${'='.repeat(40)}\n`;
103
+ output += ` ${C.cyan}Path:${C.reset} ${result.path}\n`;
104
+ output += ` ${C.cyan}Domain:${C.reset} ${result.domain}\n`;
105
+ output += ` ${C.cyan}Type:${C.reset} ${result.type}\n`;
106
+ output += ` ${C.cyan}Size:${C.reset} ${result.size} lines\n`;
107
+ if (result.purpose) output += ` ${C.cyan}Purpose:${C.reset} ${result.purpose}\n`;
108
+ if (result.sideEffects) output += ` ${C.cyan}Effects:${C.reset} ${result.sideEffects}\n`;
109
+ return { output, data: result };
110
+ }
111
+
112
+ function formatConsumers(registry, filePath, options) {
113
+ const consumers = registry.consumers(filePath);
114
+ if (options.json) return { output: JSON.stringify(consumers, null, 2), data: consumers };
115
+
116
+ let output = `\n${C.bold}Consumers of: ${filePath}${C.reset}\n${'='.repeat(50)}\n`;
117
+ if (consumers.length === 0) {
118
+ output += ` ${C.dim}No consumers found (not imported by any file)${C.reset}\n`;
119
+ } else {
120
+ for (const c of consumers) output += ` ${C.cyan}${c}${C.reset}\n`;
121
+ }
122
+ output += `\n${C.green}${consumers.length} direct consumers${C.reset}\n`;
123
+ return { output, data: consumers };
124
+ }
125
+
126
+ function formatDeps(registry, filePath, options) {
127
+ const deps = registry.dependencies(filePath);
128
+ if (options.json) return { output: JSON.stringify(deps, null, 2), data: deps };
129
+
130
+ let output = `\n${C.bold}Dependencies of: ${filePath}${C.reset}\n${'='.repeat(50)}\n`;
131
+ if (deps.length === 0) {
132
+ output += ` ${C.dim}No internal dependencies${C.reset}\n`;
133
+ } else {
134
+ for (const d of deps) output += ` ${C.cyan}${d}${C.reset}\n`;
135
+ }
136
+ output += `\n${C.green}${deps.length} direct dependencies${C.reset}\n`;
137
+ return { output, data: deps };
138
+ }
139
+
140
+ function formatImpact(registry, filePath, options) {
141
+ const impact = registry.impact(filePath);
142
+ if (options.json) return { output: JSON.stringify(impact, null, 2), data: impact };
143
+
144
+ let output = `\n${C.bold}Impact Analysis: ${filePath}${C.reset}\n${'='.repeat(60)}\n`;
145
+
146
+ output += `\n${C.bold}Direct consumers (${impact.directConsumers.length}):${C.reset}\n`;
147
+ for (const c of impact.directConsumers) output += ` ${C.cyan}${c}${C.reset}\n`;
148
+
149
+ if (impact.transitiveConsumers.length > 0) {
150
+ output += `\n${C.bold}Transitive consumers (${impact.transitiveConsumers.length}):${C.reset}\n`;
151
+ for (const t of impact.transitiveConsumers) output += ` ${C.dim}${t}${C.reset}\n`;
152
+ }
153
+
154
+ if (impact.changeImpactRules.length > 0) {
155
+ output += `\n${C.bold}Change Impact Rules:${C.reset}\n`;
156
+ for (const rule of impact.changeImpactRules) {
157
+ output += ` ${C.yellow}! ${rule.then || rule.description || JSON.stringify(rule)}${C.reset}\n`;
158
+ }
159
+ }
160
+
161
+ if (impact.sideEffects.length > 0) {
162
+ output += `\n${C.bold}Side Effects:${C.reset}\n`;
163
+ for (const e of impact.sideEffects) output += ` ${C.magenta}${e}${C.reset}\n`;
164
+ }
165
+
166
+ const total = impact.directConsumers.length + impact.transitiveConsumers.length;
167
+ output += `\n${C.green}Total affected: ${total} files${C.reset}\n`;
168
+ return { output, data: impact };
169
+ }
170
+
171
+ function formatSideEffects(registry, pattern, options) {
172
+ const results = [];
173
+ for (const [id, entry] of Object.entries(registry.sideEffects)) {
174
+ if (entry.sideEffects.some(e => e.includes(pattern))) {
175
+ results.push({ id, file: entry.file, sideEffects: entry.sideEffects });
176
+ }
177
+ }
178
+ if (options.json) return { output: JSON.stringify(results, null, 2), data: results };
179
+
180
+ let output = `\n${C.bold}Side-effects matching: "${pattern}"${C.reset}\n${'='.repeat(50)}\n`;
181
+ for (const r of results) {
182
+ output += ` ${C.cyan}${r.file}${C.reset} ${C.dim}(${r.id})${C.reset}\n`;
183
+ for (const e of r.sideEffects) output += ` ${C.magenta}${e}${C.reset}\n`;
184
+ }
185
+ output += `\n${C.green}Found ${results.length} files${C.reset}\n`;
186
+ return { output, data: results };
187
+ }
188
+
189
+ function formatComplexity(registry, level, options) {
190
+ const LEVELS = { trivial: 1, moderate: 2, complex: 3 };
191
+ const threshold = LEVELS[level] || 2;
192
+ const results = [];
193
+
194
+ for (const [path, meta] of Object.entries(registry.manifest)) {
195
+ // Infer complexity from size if not explicitly set
196
+ const size = meta.size || 0;
197
+ let fileComplexity;
198
+ if (size < 50) fileComplexity = 1;
199
+ else if (size < 150) fileComplexity = 2;
200
+ else fileComplexity = 3;
201
+
202
+ if (fileComplexity >= threshold) {
203
+ results.push({ path, size, domain: meta.domain, type: meta.type });
204
+ }
205
+ }
206
+
207
+ results.sort((a, b) => b.size - a.size);
208
+ if (options.json) return { output: JSON.stringify(results, null, 2), data: results };
209
+
210
+ let output = `\n${C.bold}Files with complexity >= ${level}${C.reset}\n${'='.repeat(50)}\n`;
211
+ for (const r of results.slice(0, 50)) {
212
+ output += ` ${C.cyan}${r.path}${C.reset} ${C.yellow}${r.size} lines${C.reset} ${r.domain}/${r.type}\n`;
213
+ }
214
+ if (results.length > 50) output += ` ${C.dim}... and ${results.length - 50} more${C.reset}\n`;
215
+ output += `\n${C.green}Found ${results.length} files${C.reset}\n`;
216
+ return { output, data: results };
217
+ }
218
+
219
+ function formatOrphanedRoutes(registry, options) {
220
+ const orphaned = registry.orphanedRoutes();
221
+ if (options.json) return { output: JSON.stringify(orphaned, null, 2), data: orphaned };
222
+
223
+ let output = `\n${C.bold}Orphaned Routes (no nav entry point)${C.reset}\n${'='.repeat(50)}\n`;
224
+ if (orphaned.length === 0) {
225
+ output += ` ${C.green}All routes have entry points!${C.reset}\n`;
226
+ } else {
227
+ for (const r of orphaned) {
228
+ output += ` ${C.red}${r.path}${C.reset} -> ${C.cyan}${r.component}${C.reset}\n`;
229
+ if (r.file) output += ` ${C.dim}${r.file}${C.reset}\n`;
230
+ }
231
+ }
232
+ output += `\n${C.yellow}${orphaned.length} orphaned routes${C.reset}\n`;
233
+ return { output, data: orphaned };
234
+ }
235
+
236
+ function formatCoverage(registry, options) {
237
+ const coverage = registry.coverage();
238
+ if (options.json) return { output: JSON.stringify(coverage, null, 2), data: coverage };
239
+
240
+ let output = `\n${C.bold}Registry Coverage${C.reset}\n${'='.repeat(40)}\n`;
241
+ output += ` ${C.cyan}Total files:${C.reset} ${coverage.totalFiles}\n`;
242
+ output += ` ${C.cyan}With @id:${C.reset} ${coverage.withId.count} (${coverage.withId.pct}%)\n`;
243
+ output += ` ${C.cyan}With @side-effects:${C.reset} ${coverage.withSideEffects.count} (${coverage.withSideEffects.pct}%)\n`;
244
+ output += ` ${C.cyan}With @purpose:${C.reset} ${coverage.withPurpose.count} (${coverage.withPurpose.pct}%)\n`;
245
+
246
+ if (coverage.missingId.length > 0 && coverage.missingId.length <= 20) {
247
+ output += `\n${C.yellow}Missing @id (${coverage.missingId.length}):${C.reset}\n`;
248
+ for (const p of coverage.missingId) output += ` ${C.dim}${p}${C.reset}\n`;
249
+ } else if (coverage.missingId.length > 20) {
250
+ output += `\n${C.yellow}Missing @id: ${coverage.missingId.length} files (use --json for full list)${C.reset}\n`;
251
+ }
252
+
253
+ return { output, data: coverage };
254
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "ai-codebase-registry Config",
4
+ "type": "object",
5
+ "required": ["name", "scanDirs"],
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "Project name"
10
+ },
11
+ "description": {
12
+ "type": "string",
13
+ "description": "Project description"
14
+ },
15
+ "scanDirs": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "object",
19
+ "required": ["dir"],
20
+ "properties": {
21
+ "dir": { "type": "string" },
22
+ "prefix": { "type": "string" }
23
+ }
24
+ },
25
+ "minItems": 1,
26
+ "description": "Directories to scan for source files"
27
+ },
28
+ "extensions": {
29
+ "type": "array",
30
+ "items": { "type": "string" },
31
+ "default": [".ts", ".tsx"],
32
+ "description": "File extensions to include"
33
+ },
34
+ "skipDirs": {
35
+ "type": "array",
36
+ "items": { "type": "string" },
37
+ "description": "Directory names to skip during scanning"
38
+ },
39
+ "aliases": {
40
+ "type": "object",
41
+ "additionalProperties": { "type": "string" },
42
+ "description": "Import alias map (e.g., { '@/': 'src/' })"
43
+ },
44
+ "router": {
45
+ "type": "string",
46
+ "enum": ["react-router", "nextjs", "expo-router", "express", "none"],
47
+ "default": "none",
48
+ "description": "Router framework for route extraction"
49
+ },
50
+ "routerEntryFile": {
51
+ "type": ["string", "null"],
52
+ "description": "Path to router entry file (e.g., App.tsx)"
53
+ },
54
+ "routeBuilderFile": {
55
+ "type": ["string", "null"],
56
+ "description": "Path to route builder file (e.g., routes.ts)"
57
+ },
58
+ "dataStore": {
59
+ "type": "string",
60
+ "enum": ["firestore", "postgres", "mongodb", "none"],
61
+ "default": "none",
62
+ "description": "Data store for relationships template"
63
+ },
64
+ "registryDir": {
65
+ "type": "string",
66
+ "default": "_registry",
67
+ "description": "Output directory for registry files"
68
+ },
69
+ "agent": {
70
+ "type": "string",
71
+ "default": "claude-code",
72
+ "description": "Target AI agent"
73
+ },
74
+ "claudemd": {
75
+ "type": "boolean",
76
+ "default": true,
77
+ "description": "Whether to generate/update CLAUDE.md sections"
78
+ },
79
+ "rulesFile": {
80
+ "type": ["string", "null"],
81
+ "description": "Path to project-specific scoring rules"
82
+ },
83
+ "patternsFile": {
84
+ "type": ["string", "null"],
85
+ "description": "Path to canonical patterns file"
86
+ }
87
+ }
88
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "ai-codebase-registry",
3
+ "version": "1.0.0",
4
+ "description": "Registry-driven AI-agent infrastructure for codebases. Auto-generates machine-readable metadata, dependency graphs, side-effects tracking, and query APIs for AI coding agents. Supports TypeScript, Swift, Kotlin, and Python.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./lib/index.mjs",
8
+ "./core/file-discovery": "./lib/core/file-discovery.mjs",
9
+ "./core/ai-meta-parser": "./lib/core/ai-meta-parser.mjs",
10
+ "./core/domain-detector": "./lib/core/domain-detector.mjs",
11
+ "./core/type-detector": "./lib/core/type-detector.mjs",
12
+ "./core/semantic-id": "./lib/core/semantic-id.mjs",
13
+ "./parsers": "./lib/parsers/index.mjs",
14
+ "./parsers/imports-typescript": "./lib/parsers/imports-typescript.mjs",
15
+ "./generators": "./lib/generators/index.mjs",
16
+ "./query": "./lib/query/engine.mjs"
17
+ },
18
+ "bin": {
19
+ "ai-registry": "./bin/cli.mjs",
20
+ "ai-codebase-registry": "./bin/cli.mjs"
21
+ },
22
+ "files": [
23
+ "bin/",
24
+ "lib/",
25
+ "templates/",
26
+ "schemas/",
27
+ "README.md"
28
+ ],
29
+ "scripts": {
30
+ "test": "node --test tests/",
31
+ "lint": "echo 'no lint configured'"
32
+ },
33
+ "keywords": [
34
+ "ai",
35
+ "agent",
36
+ "codebase",
37
+ "registry",
38
+ "metadata",
39
+ "claude",
40
+ "ai-meta",
41
+ "dependency-graph",
42
+ "side-effects",
43
+ "swift",
44
+ "multi-language"
45
+ ],
46
+ "license": "MIT",
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/ming0627/bellyfun"
56
+ }
57
+ }
@@ -0,0 +1,27 @@
1
+ /** @type {import('ai-codebase-registry').Config} */
2
+ export default {
3
+ name: 'my-swift-project',
4
+ description: 'What this project does',
5
+
6
+ scanDirs: [
7
+ { dir: 'Sources', prefix: 'Sources' },
8
+ { dir: 'Tests', prefix: 'Tests' },
9
+ ],
10
+ extensions: ['.swift'],
11
+ skipDirs: ['build', '.build', 'Pods', 'DerivedData', 'xcuserdata'],
12
+
13
+ language: 'swift',
14
+ fieldMap: {
15
+ mutations: 'side-effects',
16
+ dependencies: 'depends-on',
17
+ },
18
+ generators: ['manifest', 'semantic-ids', 'side-effects', 'features'],
19
+
20
+ aliases: {},
21
+ router: 'none',
22
+ dataStore: 'none', // 'firestore' | 'coredata' | 'realm' | 'none'
23
+
24
+ registryDir: '_registry',
25
+ agent: 'claude-code',
26
+ claudemd: true,
27
+ };
@@ -0,0 +1,25 @@
1
+ /** @type {import('ai-codebase-registry').Config} */
2
+ export default {
3
+ name: 'my-project',
4
+ description: 'What this project does',
5
+
6
+ scanDirs: [
7
+ { dir: 'src', prefix: 'src' },
8
+ ],
9
+ extensions: ['.ts', '.tsx'],
10
+ skipDirs: ['node_modules', 'dist', '.next', 'coverage', '__tests__', '__mocks__'],
11
+
12
+ aliases: {
13
+ '@/': 'src/',
14
+ },
15
+
16
+ router: 'none', // 'react-router' | 'nextjs' | 'expo-router' | 'express' | 'none'
17
+ routerEntryFile: null,
18
+ routeBuilderFile: null,
19
+
20
+ dataStore: 'none', // 'firestore' | 'postgres' | 'mongodb' | 'none'
21
+
22
+ registryDir: '_registry',
23
+ agent: 'claude-code',
24
+ claudemd: true,
25
+ };