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.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # ai-codebase-registry
2
+
3
+ Registry-driven AI-agent infrastructure for codebases. Auto-generates machine-readable metadata, dependency graphs, side-effects tracking, and query APIs for AI coding agents.
4
+
5
+ Supports **TypeScript**, **Swift**, **Kotlin**, and **Python** projects.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install --save-dev ai-codebase-registry
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Initialize config file
17
+ npx ai-registry init
18
+
19
+ # Generate registry from source
20
+ npx ai-registry generate
21
+
22
+ # Validate @ai-meta coverage
23
+ npx ai-registry validate
24
+ ```
25
+
26
+ ## CLI Commands
27
+
28
+ | Command | Description |
29
+ |---------|-------------|
30
+ | `ai-registry init` | Scaffold `ai-registry.config.mjs` for a new project |
31
+ | `ai-registry generate` | Rebuild all registry files from source |
32
+ | `ai-registry validate` | Check `@ai-meta` coverage and report gaps |
33
+ | `ai-registry query` | Query registry data (files, deps, impact, coverage) |
34
+ | `ai-registry add-headers` | Auto-add `@ai-meta` headers to files missing them |
35
+
36
+ ## Configuration
37
+
38
+ Create `ai-registry.config.mjs` in your project root:
39
+
40
+ ### TypeScript / JavaScript
41
+
42
+ ```js
43
+ export default {
44
+ name: 'my-project',
45
+ scanDirs: [{ dir: 'src', prefix: 'src' }],
46
+ extensions: ['.ts', '.tsx'],
47
+ skipDirs: ['node_modules', 'dist'],
48
+ aliases: { '@/': 'src/' },
49
+ registryDir: '_registry',
50
+ };
51
+ ```
52
+
53
+ ### Swift (iOS / macOS)
54
+
55
+ ```js
56
+ export default {
57
+ name: 'my-ios-app',
58
+ scanDirs: [
59
+ { dir: 'Sources', prefix: 'Sources' },
60
+ { dir: 'Tests', prefix: 'Tests' },
61
+ ],
62
+ extensions: ['.swift'],
63
+ skipDirs: ['build', 'Pods', '.build', 'DerivedData'],
64
+ language: 'swift',
65
+ fieldMap: { mutations: 'side-effects', dependencies: 'depends-on' },
66
+ generators: ['manifest', 'semantic-ids', 'side-effects', 'features'],
67
+ registryDir: '_registry',
68
+ };
69
+ ```
70
+
71
+ ## Config Reference
72
+
73
+ | Field | Type | Default | Description |
74
+ |-------|------|---------|-------------|
75
+ | `name` | string | `'my-project'` | Project name |
76
+ | `description` | string | `''` | Project description |
77
+ | `scanDirs` | array | `[{ dir: 'src', prefix: 'src' }]` | Directories to scan |
78
+ | `extensions` | array | `['.ts', '.tsx']` | File extensions to include |
79
+ | `skipDirs` | array | `['node_modules', ...]` | Directories to skip |
80
+ | `language` | string | `'typescript'` | `'typescript'` \| `'swift'` \| `'kotlin'` \| `'python'` |
81
+ | `fieldMap` | object | `{}` | Field name normalization map |
82
+ | `generators` | array\|null | `null` (all) | Which generators to run |
83
+ | `aliases` | object | `{}` | Import alias map (TS only) |
84
+ | `router` | string | `'none'` | Router type for route extraction |
85
+ | `dataStore` | string | `'none'` | Data store type |
86
+ | `registryDir` | string | `'_registry'` | Output directory |
87
+ | `agent` | string | `'claude-code'` | Target AI agent |
88
+ | `claudemd` | boolean | `true` | Generate CLAUDE.md references |
89
+
90
+ ## Multi-Language Support
91
+
92
+ The parser auto-detects the comment style from file content:
93
+
94
+ | Language | Comment Style | Field Syntax |
95
+ |----------|--------------|--------------|
96
+ | TypeScript/JS | `/** @ai-meta ... */` | `@field value` |
97
+ | Swift | `/// @ai-meta ...` | `field: value` |
98
+ | Kotlin/Go | `// @ai-meta ...` | `field: value` or `@field value` |
99
+
100
+ ### Field Normalization
101
+
102
+ Use `fieldMap` to map project-specific field names to canonical ones:
103
+
104
+ ```js
105
+ fieldMap: {
106
+ mutations: 'side-effects', // Swift uses "mutations:", canonical is "side-effects"
107
+ dependencies: 'depends-on', // Swift uses "dependencies:", canonical is "depends-on"
108
+ }
109
+ ```
110
+
111
+ ## Generated Registry Files
112
+
113
+ | File | Description |
114
+ |------|-------------|
115
+ | `manifest.json` | Every source file with domain, type, purpose, imports |
116
+ | `semantic-ids.json` | Stable entity ID to file path mapping |
117
+ | `deps.json` | Import dependency graph (TypeScript only) |
118
+ | `side-effects.json` | Mutation/trigger to downstream effects graph |
119
+ | `features.json` | Feature inventory by domain |
120
+
121
+ ## Deep Import Paths
122
+
123
+ | Import Path | Purpose |
124
+ |-------------|---------|
125
+ | `ai-codebase-registry` | Main entry (re-exports) |
126
+ | `ai-codebase-registry/core/file-discovery` | Recursive file scanning |
127
+ | `ai-codebase-registry/core/ai-meta-parser` | `@ai-meta` header parsing |
128
+ | `ai-codebase-registry/core/domain-detector` | Domain detection from paths |
129
+ | `ai-codebase-registry/core/type-detector` | File type detection |
130
+ | `ai-codebase-registry/core/semantic-id` | Semantic ID generation |
131
+ | `ai-codebase-registry/parsers` | Parser barrel export |
132
+ | `ai-codebase-registry/generators` | Generator barrel export |
133
+ | `ai-codebase-registry/query` | Registry query engine |
134
+
135
+ ## License
136
+
137
+ MIT
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @ai-meta
4
+ * @id module:ai-agent-infra:cli-add-headers
5
+ * @domain ai-agent-infra
6
+ * @type module
7
+ * @side-effects writes:filesystem:source-files
8
+ * @stability medium
9
+ * @complexity complex
10
+ * @token-budget 300
11
+ * @purpose CLI command: auto-add @ai-meta headers to source files missing them
12
+ *
13
+ * @filechangelog
14
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created add-headers CLI stub
15
+ */
16
+
17
+ import { existsSync } from 'fs';
18
+ import { resolve } from 'path';
19
+ import { loadConfig } from '../lib/core/config-loader.mjs';
20
+ import { collectFiles } from '../lib/core/file-discovery.mjs';
21
+
22
+ const C = {
23
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
24
+ green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m',
25
+ };
26
+
27
+ const isDryRun = process.argv.includes('--dry-run');
28
+
29
+ async function main() {
30
+ const rootDir = resolve('.');
31
+ const config = await loadConfig(rootDir);
32
+
33
+ console.log(`${C.bold}ai-codebase-registry add-headers${C.reset}`);
34
+ console.log(isDryRun ? `${C.yellow}DRY RUN — no files will be modified${C.reset}\n` : '\n');
35
+
36
+ // Discover files
37
+ const allFiles = [];
38
+ const extensions = new Set(config.extensions);
39
+ const skipDirs = new Set(config.skipDirs);
40
+
41
+ for (const { dir, prefix } of config.scanDirs) {
42
+ if (existsSync(dir)) {
43
+ allFiles.push(...collectFiles(dir, prefix, { extensions, skipDirs }));
44
+ }
45
+ }
46
+
47
+ console.log(`Scanned ${allFiles.length} files`);
48
+ console.log(`${C.dim}Note: For full header injection, use the project's scripts/add-ai-meta-headers.mjs${C.reset}`);
49
+ console.log(`${C.dim}This command provides the framework; the actual injection logic is in the project script.${C.reset}`);
50
+
51
+ // Count files without @ai-meta
52
+ let missing = 0;
53
+ for (const { fullPath } of allFiles) {
54
+ try {
55
+ const { readFileSync } = await import('fs');
56
+ const content = readFileSync(fullPath, 'utf-8');
57
+ if (!content.includes('@ai-meta')) missing++;
58
+ } catch {
59
+ // skip
60
+ }
61
+ }
62
+
63
+ console.log(`\nFiles without @ai-meta: ${missing}`);
64
+ if (missing === 0) {
65
+ console.log(`${C.green}All files have @ai-meta headers!${C.reset}`);
66
+ } else {
67
+ console.log(`${C.yellow}Run 'node scripts/add-ai-meta-headers.mjs' to add headers${C.reset}`);
68
+ }
69
+ }
70
+
71
+ main().catch(err => {
72
+ console.error(err);
73
+ process.exit(1);
74
+ });
package/bin/cli.mjs ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @ai-meta
4
+ * @id module:ai-agent-infra:cli
5
+ * @domain ai-agent-infra
6
+ * @type module
7
+ * @side-effects none
8
+ * @stability medium
9
+ * @complexity moderate
10
+ * @token-budget 200
11
+ * @purpose Unified CLI entry point for ai-codebase-registry — dispatches to init, generate, query, validate, add-headers
12
+ *
13
+ * @filechangelog
14
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created CLI dispatcher
15
+ */
16
+
17
+ const COMMANDS = {
18
+ init: './init.mjs',
19
+ generate: './generate.mjs',
20
+ query: './query.mjs',
21
+ validate: './validate.mjs',
22
+ 'add-headers': './add-headers.mjs',
23
+ };
24
+
25
+ function printUsage() {
26
+ console.log(`
27
+ \x1b[1mai-codebase-registry\x1b[0m — Registry-driven AI agent infrastructure
28
+
29
+ \x1b[1mUsage:\x1b[0m
30
+ ai-registry <command> [options]
31
+
32
+ \x1b[1mCommands:\x1b[0m
33
+ init Scaffold registry for a new project
34
+ generate Rebuild all registry files from source
35
+ query Query registry data (files, deps, impact, coverage)
36
+ validate Check @ai-meta coverage and completeness
37
+ add-headers Auto-add @ai-meta headers to files missing them
38
+
39
+ \x1b[1mExamples:\x1b[0m
40
+ ai-registry init
41
+ ai-registry generate
42
+ ai-registry query --domain trip --type hook
43
+ ai-registry query --impact functions/src/services/projectionService.ts
44
+ ai-registry query --coverage
45
+ ai-registry validate
46
+ ai-registry add-headers --dry-run
47
+
48
+ Run \x1b[1mai-registry <command> --help\x1b[0m for command-specific help.
49
+ `);
50
+ }
51
+
52
+ async function main() {
53
+ const args = process.argv.slice(2);
54
+ const command = args[0];
55
+
56
+ if (!command || command === '--help' || command === '-h') {
57
+ printUsage();
58
+ process.exit(0);
59
+ }
60
+
61
+ if (!COMMANDS[command]) {
62
+ console.error(`Unknown command: ${command}`);
63
+ console.error(`Run 'ai-registry --help' for available commands.`);
64
+ process.exit(1);
65
+ }
66
+
67
+ // Forward remaining args
68
+ process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
69
+
70
+ try {
71
+ await import(COMMANDS[command]);
72
+ } catch (err) {
73
+ if (err.code === 'ERR_MODULE_NOT_FOUND') {
74
+ console.error(`Command '${command}' is not yet implemented.`);
75
+ console.error(`Module not found: ${COMMANDS[command]}`);
76
+ process.exit(1);
77
+ }
78
+ throw err;
79
+ }
80
+ }
81
+
82
+ main();
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @ai-meta
4
+ * @id module:ai-agent-infra:cli-generate
5
+ * @domain ai-agent-infra
6
+ * @type module
7
+ * @side-effects writes:filesystem:_registry
8
+ * @stability medium
9
+ * @complexity complex
10
+ * @token-budget 300
11
+ * @purpose CLI command: rebuild all registry files from source files — supports multi-language projects
12
+ *
13
+ * @filechangelog
14
+ * @changelog 2026-02-23T10:00 [feat] [Claude Opus 4.6] Add fieldMap pass-through, language-aware deps skip, conditional generators
15
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created generate CLI command
16
+ */
17
+
18
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
19
+ import { resolve } from 'path';
20
+ import { loadConfig } from '../lib/core/config-loader.mjs';
21
+ import { collectFiles } from '../lib/core/file-discovery.mjs';
22
+ import { createDomainDetector } from '../lib/core/domain-detector.mjs';
23
+ import { generateManifest } from '../lib/generators/manifest.mjs';
24
+ import { generateDeps } from '../lib/generators/deps.mjs';
25
+ import { generateSemanticIds } from '../lib/generators/semantic-ids.mjs';
26
+ import { generateSideEffects } from '../lib/generators/side-effects.mjs';
27
+ import { generateFeatures } from '../lib/generators/features.mjs';
28
+
29
+ const C = {
30
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
31
+ green: '\x1b[32m', cyan: '\x1b[36m', yellow: '\x1b[33m',
32
+ };
33
+
34
+ /** Languages that support textual import parsing */
35
+ const IMPORT_GRAPH_LANGUAGES = new Set(['typescript', 'javascript']);
36
+
37
+ async function main() {
38
+ const rootDir = resolve('.');
39
+ const config = await loadConfig(rootDir);
40
+
41
+ const enabledGenerators = config.generators
42
+ ? new Set(config.generators)
43
+ : null; // null = all
44
+
45
+ const shouldGenerate = (name) => !enabledGenerators || enabledGenerators.has(name);
46
+ const canGenerateDeps = IMPORT_GRAPH_LANGUAGES.has(config.language || 'typescript');
47
+
48
+ console.log(`${C.bold}ai-codebase-registry generate${C.reset}`);
49
+ console.log(`Project: ${config.name}`);
50
+ if (config.language && config.language !== 'typescript') {
51
+ console.log(`Language: ${config.language}`);
52
+ }
53
+ console.log('');
54
+
55
+ // 1. Discover files
56
+ console.log('Scanning codebase...');
57
+ const allFiles = [];
58
+ const extensions = new Set(config.extensions);
59
+ const skipDirs = new Set(config.skipDirs);
60
+
61
+ for (const { dir, prefix } of config.scanDirs) {
62
+ if (existsSync(dir)) {
63
+ const files = collectFiles(dir, prefix, { extensions, skipDirs });
64
+ allFiles.push(...files);
65
+ console.log(` ${prefix}: ${files.length} files`);
66
+ } else {
67
+ console.log(` ${prefix}: directory not found, skipping`);
68
+ }
69
+ }
70
+ console.log(` Total: ${allFiles.length} files\n`);
71
+
72
+ // 2. Build manifest
73
+ console.log('Building manifest...');
74
+ const detectDomain = createDomainDetector();
75
+ const { manifest, importGraph, stats } = generateManifest(allFiles, {
76
+ aliasMap: config.aliases,
77
+ detectDomain,
78
+ fieldMap: config.fieldMap,
79
+ skipImportParsing: !canGenerateDeps,
80
+ });
81
+ console.log(` @ai-meta coverage: ${stats.coverage}`);
82
+ console.log(` With @purpose: ${stats.purposeCount}\n`);
83
+
84
+ // 3. Generate registry files
85
+ console.log('Generating registry files...');
86
+
87
+ const outputs = [];
88
+
89
+ if (shouldGenerate('manifest')) {
90
+ const manifestOutput = {
91
+ generated: new Date().toISOString(),
92
+ fileCount: Object.keys(manifest).length,
93
+ aiMetaCoverage: stats.coverage,
94
+ files: manifest,
95
+ };
96
+ outputs.push(['manifest.json', manifestOutput]);
97
+ }
98
+
99
+ if (shouldGenerate('deps') && canGenerateDeps) {
100
+ const importGraphSets = {};
101
+ for (const [key, value] of Object.entries(importGraph)) {
102
+ importGraphSets[key] = value;
103
+ }
104
+ const deps = generateDeps(importGraphSets);
105
+ outputs.push(['deps.json', deps]);
106
+ } else if (shouldGenerate('deps') && !canGenerateDeps) {
107
+ console.log(` ${C.yellow}skip${C.reset} deps.json (no import graph for ${config.language})`);
108
+ }
109
+
110
+ if (shouldGenerate('semantic-ids')) {
111
+ const semanticIds = generateSemanticIds(manifest);
112
+ outputs.push(['semantic-ids.json', semanticIds]);
113
+ }
114
+
115
+ if (shouldGenerate('side-effects')) {
116
+ const sideEffects = generateSideEffects(manifest);
117
+ outputs.push(['side-effects.json', sideEffects]);
118
+ }
119
+
120
+ if (shouldGenerate('features')) {
121
+ const features = generateFeatures(manifest);
122
+ outputs.push(['features.json', features]);
123
+ }
124
+
125
+ // 4. Write output
126
+ const registryDir = config.registryDir;
127
+ if (!existsSync(registryDir)) {
128
+ mkdirSync(registryDir, { recursive: true });
129
+ }
130
+
131
+ for (const [filename, data] of outputs) {
132
+ const filePath = resolve(registryDir, filename);
133
+ writeFileSync(filePath, JSON.stringify(data, null, 2));
134
+ console.log(` ${C.green}wrote${C.reset} ${filename}`);
135
+ }
136
+
137
+ console.log(`\n${C.bold}Registry generation complete!${C.reset}`);
138
+ console.log(` Files: ${Object.keys(manifest).length}`);
139
+
140
+ // Find generated data for summary
141
+ const semanticIds = outputs.find(([n]) => n === 'semantic-ids.json')?.[1];
142
+ const sideEffects = outputs.find(([n]) => n === 'side-effects.json')?.[1];
143
+ const features = outputs.find(([n]) => n === 'features.json')?.[1];
144
+
145
+ if (semanticIds) console.log(` Semantic IDs: ${semanticIds.explicitCount} explicit, ${semanticIds.derivedCount} derived`);
146
+ if (sideEffects) console.log(` Side-effects: ${sideEffects.documentedCount} documented, ${sideEffects.missingCount} missing`);
147
+ if (features) console.log(` Domains: ${features.domainCount}`);
148
+ }
149
+
150
+ main().catch(err => {
151
+ console.error(err);
152
+ process.exit(1);
153
+ });
package/bin/init.mjs ADDED
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @ai-meta
4
+ * @id module:ai-agent-infra:cli-init
5
+ * @domain ai-agent-infra
6
+ * @type module
7
+ * @side-effects writes:filesystem:_registry,writes:filesystem:ai-registry.config.mjs
8
+ * @stability medium
9
+ * @complexity complex
10
+ * @token-budget 300
11
+ * @purpose CLI command: scaffold registry infrastructure for a new project (config, directories, starter files)
12
+ *
13
+ * @filechangelog
14
+ * @changelog 2026-02-22T14:00 [feat] [Claude Opus 4.6] Created init CLI command with interactive prompts
15
+ */
16
+
17
+ import { createInterface } from 'readline';
18
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
19
+ import { resolve, join } from 'path';
20
+
21
+ const C = {
22
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
23
+ green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m',
24
+ };
25
+
26
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
27
+ const ask = (q) => new Promise((res) => rl.question(q, res));
28
+
29
+ async function main() {
30
+ console.log(`\n${C.bold}ai-codebase-registry init${C.reset}`);
31
+ console.log(`${C.dim}Scaffold AI agent infrastructure for your project${C.reset}\n`);
32
+
33
+ const rootDir = resolve('.');
34
+
35
+ // Check for existing config
36
+ if (existsSync(join(rootDir, 'ai-registry.config.mjs'))) {
37
+ console.log(`${C.yellow}ai-registry.config.mjs already exists.${C.reset}`);
38
+ const overwrite = await ask('Overwrite? (y/N) ');
39
+ if (overwrite.toLowerCase() !== 'y') {
40
+ console.log('Aborted.');
41
+ rl.close();
42
+ return;
43
+ }
44
+ }
45
+
46
+ // Gather info
47
+ const name = await ask(`${C.cyan}Project name${C.reset} [${rootDir.split('/').pop()}]: `) || rootDir.split('/').pop();
48
+ const description = await ask(`${C.cyan}Description${C.reset}: `);
49
+ const srcDir = await ask(`${C.cyan}Source directory${C.reset} [src]: `) || 'src';
50
+ const extraDirs = await ask(`${C.cyan}Additional source directories${C.reset} (comma-separated, or empty): `);
51
+ const router = await ask(`${C.cyan}Router framework${C.reset} (react-router/nextjs/expo-router/express/none) [none]: `) || 'none';
52
+ const dataStore = await ask(`${C.cyan}Data store${C.reset} (firestore/postgres/mongodb/none) [none]: `) || 'none';
53
+
54
+ rl.close();
55
+
56
+ // Build scanDirs
57
+ const scanDirs = [{ dir: srcDir, prefix: srcDir }];
58
+ if (extraDirs.trim()) {
59
+ for (const d of extraDirs.split(',').map(s => s.trim()).filter(Boolean)) {
60
+ scanDirs.push({ dir: d, prefix: d });
61
+ }
62
+ }
63
+
64
+ // Generate config file
65
+ const configContent = `/** @type {import('ai-codebase-registry').Config} */
66
+ export default {
67
+ name: ${JSON.stringify(name)},
68
+ description: ${JSON.stringify(description)},
69
+
70
+ scanDirs: ${JSON.stringify(scanDirs, null, 4).replace(/\n/g, '\n ')},
71
+ extensions: ['.ts', '.tsx'],
72
+ skipDirs: ['node_modules', 'dist', '.next', 'coverage', '__tests__', '__mocks__'],
73
+
74
+ aliases: {
75
+ '@/': '${srcDir}/',
76
+ },
77
+
78
+ router: '${router}',
79
+ routerEntryFile: ${router === 'react-router' ? `'${srcDir}/App.tsx'` : 'null'},
80
+ routeBuilderFile: ${router === 'react-router' ? `'${srcDir}/routes.ts'` : 'null'},
81
+
82
+ dataStore: '${dataStore}',
83
+
84
+ registryDir: '_registry',
85
+ agent: 'claude-code',
86
+ claudemd: true,
87
+ };
88
+ `;
89
+
90
+ writeFileSync(join(rootDir, 'ai-registry.config.mjs'), configContent);
91
+ console.log(`\n ${C.green}created${C.reset} ai-registry.config.mjs`);
92
+
93
+ // Create _registry directory
94
+ const registryDir = join(rootDir, '_registry');
95
+ if (!existsSync(registryDir)) {
96
+ mkdirSync(registryDir, { recursive: true });
97
+ }
98
+ console.log(` ${C.green}created${C.reset} _registry/`);
99
+
100
+ // Create starter relationships.json
101
+ const relationships = {
102
+ _meta: {
103
+ description: `Cross-cutting relationship graph for ${name}`,
104
+ generated: new Date().toISOString().split('T')[0],
105
+ version: '1.0.0',
106
+ },
107
+ firestore_paths: {},
108
+ change_impact_rules: [],
109
+ };
110
+ writeFileSync(join(registryDir, 'relationships.json'), JSON.stringify(relationships, null, 2));
111
+ console.log(` ${C.green}created${C.reset} _registry/relationships.json`);
112
+
113
+ // Create starter patterns.json
114
+ const patterns = {
115
+ version: '1.0',
116
+ generated: new Date().toISOString(),
117
+ patternCount: 0,
118
+ patterns: {},
119
+ };
120
+ writeFileSync(join(registryDir, 'patterns.json'), JSON.stringify(patterns, null, 2));
121
+ console.log(` ${C.green}created${C.reset} _registry/patterns.json`);
122
+
123
+ // Create starter nav-exceptions.json
124
+ const navExceptions = {
125
+ _meta: { description: 'Routes that intentionally have no nav entry point' },
126
+ exceptions: {},
127
+ };
128
+ writeFileSync(join(registryDir, 'nav-exceptions.json'), JSON.stringify(navExceptions, null, 2));
129
+ console.log(` ${C.green}created${C.reset} _registry/nav-exceptions.json`);
130
+
131
+ // Generate CLAUDE.md section
132
+ const claudeMdSection = generateClaudeMdSection(name, scanDirs);
133
+ const claudeMdPath = join(rootDir, 'CLAUDE.md');
134
+ if (existsSync(claudeMdPath)) {
135
+ console.log(`\n ${C.yellow}CLAUDE.md already exists — appending AI agent infrastructure section${C.reset}`);
136
+ const existing = (await import('fs')).readFileSync(claudeMdPath, 'utf-8');
137
+ if (!existing.includes('AI-Agent-First Architecture')) {
138
+ writeFileSync(claudeMdPath, existing + '\n\n' + claudeMdSection);
139
+ console.log(` ${C.green}updated${C.reset} CLAUDE.md`);
140
+ } else {
141
+ console.log(` ${C.dim}CLAUDE.md already has agent infrastructure section${C.reset}`);
142
+ }
143
+ } else {
144
+ writeFileSync(claudeMdPath, `# CLAUDE.md\n\n${claudeMdSection}`);
145
+ console.log(` ${C.green}created${C.reset} CLAUDE.md`);
146
+ }
147
+
148
+ console.log(`
149
+ ${C.bold}Next steps:${C.reset}
150
+ 1. Run ${C.cyan}ai-registry generate${C.reset} to build initial registry
151
+ 2. Run ${C.cyan}ai-registry add-headers${C.reset} to add @ai-meta headers to source files
152
+ 3. Run ${C.cyan}ai-registry validate${C.reset} to check coverage
153
+ 4. Add to pre-commit hook: ${C.cyan}ai-registry generate${C.reset}
154
+ `);
155
+ }
156
+
157
+ function generateClaudeMdSection(name) {
158
+ return `## AI-Agent-First Architecture Mandate
159
+
160
+ ${name} uses a registry-driven architecture where every source file carries machine-readable metadata.
161
+ AI agents MUST consult the registry before searching the filesystem.
162
+
163
+ ### Registry Loading Order
164
+ \`semantic-ids.json\` -> \`manifest.json\` -> \`side-effects.json\` -> \`patterns.json\`
165
+
166
+ ### \`@ai-meta\` Headers (MANDATORY)
167
+
168
+ Every source file MUST have an \`@ai-meta\` JSDoc header with these mandatory fields:
169
+ - \`@id\` — format: \`type:domain:kebab-name\`
170
+ - \`@domain\` — business domain this file belongs to
171
+ - \`@type\` — file type (hook, component, page, service, util, etc.)
172
+ - \`@side-effects\` — what this file mutates (\`none\` if pure)
173
+ - \`@stability\` — how often this changes (low/medium/high)
174
+ - \`@complexity\` — code complexity (trivial/moderate/complex)
175
+ - \`@token-budget\` — estimated tokens for AI agent to process
176
+ - \`@purpose\` — one-line description
177
+
178
+ ### Side-Effect Documentation
179
+
180
+ Before modifying any mutation, trigger, or data write:
181
+ 1. Query \`_registry/side-effects.json\` for the entity
182
+ 2. If NOT listed, add it before writing code
183
+ 3. After modifying: update \`@side-effects\` AND \`side-effects.json\`
184
+
185
+ ### Agent Session Protocol
186
+
187
+ At session start:
188
+ 1. Load: \`_registry/semantic-ids.json\` -> \`manifest.json\` -> \`side-effects.json\` -> \`patterns.json\`
189
+ 2. Check \`TODO.md\` for open items
190
+ 3. Auto-select highest-severity unblocked item
191
+ `;
192
+ }
193
+
194
+ main().catch(err => {
195
+ console.error(err);
196
+ rl.close();
197
+ process.exit(1);
198
+ });