ai-first-cli 1.1.0 → 1.1.2

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 (192) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.es.md +137 -1
  3. package/README.md +136 -4
  4. package/ai/ai_context.md +2 -2
  5. package/ai/architecture.md +3 -3
  6. package/ai/cache.json +85 -57
  7. package/ai/ccp/jira-123/context.json +7 -0
  8. package/ai/context/repo.json +56 -0
  9. package/ai/context/utils.json +7 -0
  10. package/ai/dependencies.json +51 -1026
  11. package/ai/files.json +195 -3
  12. package/ai/git/commit-activity.json +8646 -0
  13. package/ai/git/recent-features.json +1 -0
  14. package/ai/git/recent-files.json +52 -0
  15. package/ai/git/recent-flows.json +1 -0
  16. package/ai/graph/knowledge-graph.json +43643 -0
  17. package/ai/graph/module-graph.json +4 -0
  18. package/ai/graph/symbol-graph.json +3307 -879
  19. package/ai/graph/symbol-references.json +119 -32
  20. package/ai/index-state.json +843 -188
  21. package/ai/index.db +0 -0
  22. package/ai/modules.json +4 -0
  23. package/ai/repo-map.json +81 -17
  24. package/ai/repo_map.json +81 -17
  25. package/ai/repo_map.md +21 -7
  26. package/ai/summary.md +5 -5
  27. package/ai/symbols.json +1 -20287
  28. package/dist/analyzers/androidResources.d.ts +23 -0
  29. package/dist/analyzers/androidResources.d.ts.map +1 -0
  30. package/dist/analyzers/androidResources.js +93 -0
  31. package/dist/analyzers/androidResources.js.map +1 -0
  32. package/dist/analyzers/dependencies.d.ts.map +1 -1
  33. package/dist/analyzers/dependencies.js +37 -0
  34. package/dist/analyzers/dependencies.js.map +1 -1
  35. package/dist/analyzers/entrypoints.d.ts.map +1 -1
  36. package/dist/analyzers/entrypoints.js +71 -1
  37. package/dist/analyzers/entrypoints.js.map +1 -1
  38. package/dist/analyzers/gradleModules.d.ts +22 -0
  39. package/dist/analyzers/gradleModules.d.ts.map +1 -0
  40. package/dist/analyzers/gradleModules.js +75 -0
  41. package/dist/analyzers/gradleModules.js.map +1 -0
  42. package/dist/analyzers/techStack.d.ts +7 -0
  43. package/dist/analyzers/techStack.d.ts.map +1 -1
  44. package/dist/analyzers/techStack.js +44 -1
  45. package/dist/analyzers/techStack.js.map +1 -1
  46. package/dist/commands/ai-first.d.ts.map +1 -1
  47. package/dist/commands/ai-first.js +311 -1
  48. package/dist/commands/ai-first.js.map +1 -1
  49. package/dist/core/adapters/adapterRegistry.d.ts +39 -0
  50. package/dist/core/adapters/adapterRegistry.d.ts.map +1 -0
  51. package/dist/core/adapters/adapterRegistry.js +155 -0
  52. package/dist/core/adapters/adapterRegistry.js.map +1 -0
  53. package/dist/core/adapters/baseAdapter.d.ts +49 -0
  54. package/dist/core/adapters/baseAdapter.d.ts.map +1 -0
  55. package/dist/core/adapters/baseAdapter.js +28 -0
  56. package/dist/core/adapters/baseAdapter.js.map +1 -0
  57. package/dist/core/adapters/community/fastapiAdapter.d.ts +7 -0
  58. package/dist/core/adapters/community/fastapiAdapter.d.ts.map +1 -0
  59. package/dist/core/adapters/community/fastapiAdapter.js +40 -0
  60. package/dist/core/adapters/community/fastapiAdapter.js.map +1 -0
  61. package/dist/core/adapters/community/index.d.ts +11 -0
  62. package/dist/core/adapters/community/index.d.ts.map +1 -0
  63. package/dist/core/adapters/community/index.js +11 -0
  64. package/dist/core/adapters/community/index.js.map +1 -0
  65. package/dist/core/adapters/community/laravelAdapter.d.ts +7 -0
  66. package/dist/core/adapters/community/laravelAdapter.d.ts.map +1 -0
  67. package/dist/core/adapters/community/laravelAdapter.js +47 -0
  68. package/dist/core/adapters/community/laravelAdapter.js.map +1 -0
  69. package/dist/core/adapters/community/nestjsAdapter.d.ts +7 -0
  70. package/dist/core/adapters/community/nestjsAdapter.d.ts.map +1 -0
  71. package/dist/core/adapters/community/nestjsAdapter.js +48 -0
  72. package/dist/core/adapters/community/nestjsAdapter.js.map +1 -0
  73. package/dist/core/adapters/community/phoenixAdapter.d.ts +7 -0
  74. package/dist/core/adapters/community/phoenixAdapter.d.ts.map +1 -0
  75. package/dist/core/adapters/community/phoenixAdapter.js +45 -0
  76. package/dist/core/adapters/community/phoenixAdapter.js.map +1 -0
  77. package/dist/core/adapters/community/springBootAdapter.d.ts +7 -0
  78. package/dist/core/adapters/community/springBootAdapter.d.ts.map +1 -0
  79. package/dist/core/adapters/community/springBootAdapter.js +44 -0
  80. package/dist/core/adapters/community/springBootAdapter.js.map +1 -0
  81. package/dist/core/adapters/dotnetAdapter.d.ts +20 -0
  82. package/dist/core/adapters/dotnetAdapter.d.ts.map +1 -0
  83. package/dist/core/adapters/dotnetAdapter.js +86 -0
  84. package/dist/core/adapters/dotnetAdapter.js.map +1 -0
  85. package/dist/core/adapters/index.d.ts +18 -0
  86. package/dist/core/adapters/index.d.ts.map +1 -0
  87. package/dist/core/adapters/index.js +19 -0
  88. package/dist/core/adapters/index.js.map +1 -0
  89. package/dist/core/adapters/javascriptAdapter.d.ts +11 -0
  90. package/dist/core/adapters/javascriptAdapter.d.ts.map +1 -0
  91. package/dist/core/adapters/javascriptAdapter.js +47 -0
  92. package/dist/core/adapters/javascriptAdapter.js.map +1 -0
  93. package/dist/core/adapters/pythonAdapter.d.ts +20 -0
  94. package/dist/core/adapters/pythonAdapter.d.ts.map +1 -0
  95. package/dist/core/adapters/pythonAdapter.js +99 -0
  96. package/dist/core/adapters/pythonAdapter.js.map +1 -0
  97. package/dist/core/adapters/railsAdapter.d.ts +10 -0
  98. package/dist/core/adapters/railsAdapter.d.ts.map +1 -0
  99. package/dist/core/adapters/railsAdapter.js +52 -0
  100. package/dist/core/adapters/railsAdapter.js.map +1 -0
  101. package/dist/core/adapters/salesforceAdapter.d.ts +16 -0
  102. package/dist/core/adapters/salesforceAdapter.d.ts.map +1 -0
  103. package/dist/core/adapters/salesforceAdapter.js +64 -0
  104. package/dist/core/adapters/salesforceAdapter.js.map +1 -0
  105. package/dist/core/adapters/sdk.d.ts +83 -0
  106. package/dist/core/adapters/sdk.d.ts.map +1 -0
  107. package/dist/core/adapters/sdk.js +114 -0
  108. package/dist/core/adapters/sdk.js.map +1 -0
  109. package/dist/core/ccp.d.ts +37 -0
  110. package/dist/core/ccp.d.ts.map +1 -0
  111. package/dist/core/ccp.js +184 -0
  112. package/dist/core/ccp.js.map +1 -0
  113. package/dist/core/gitAnalyzer.d.ts +74 -0
  114. package/dist/core/gitAnalyzer.d.ts.map +1 -0
  115. package/dist/core/gitAnalyzer.js +298 -0
  116. package/dist/core/gitAnalyzer.js.map +1 -0
  117. package/dist/core/incrementalAnalyzer.d.ts +28 -0
  118. package/dist/core/incrementalAnalyzer.d.ts.map +1 -0
  119. package/dist/core/incrementalAnalyzer.js +343 -0
  120. package/dist/core/incrementalAnalyzer.js.map +1 -0
  121. package/dist/core/knowledgeGraphBuilder.d.ts +31 -0
  122. package/dist/core/knowledgeGraphBuilder.d.ts.map +1 -0
  123. package/dist/core/knowledgeGraphBuilder.js +197 -0
  124. package/dist/core/knowledgeGraphBuilder.js.map +1 -0
  125. package/dist/core/lazyAnalyzer.d.ts +57 -0
  126. package/dist/core/lazyAnalyzer.d.ts.map +1 -0
  127. package/dist/core/lazyAnalyzer.js +204 -0
  128. package/dist/core/lazyAnalyzer.js.map +1 -0
  129. package/dist/core/schema.d.ts +57 -0
  130. package/dist/core/schema.d.ts.map +1 -0
  131. package/dist/core/schema.js +131 -0
  132. package/dist/core/schema.js.map +1 -0
  133. package/dist/core/semanticContexts.d.ts +40 -0
  134. package/dist/core/semanticContexts.d.ts.map +1 -0
  135. package/dist/core/semanticContexts.js +454 -0
  136. package/dist/core/semanticContexts.js.map +1 -0
  137. package/docs/es/guide/adapters.md +143 -0
  138. package/docs/es/guide/ai-repository-schema.md +119 -0
  139. package/docs/es/guide/features.md +67 -0
  140. package/docs/es/guide/flows.md +134 -0
  141. package/docs/es/guide/git-intelligence.md +170 -0
  142. package/docs/es/guide/incremental-analysis.md +131 -0
  143. package/docs/es/guide/knowledge-graph.md +135 -0
  144. package/docs/es/guide/lazy-indexing.md +144 -0
  145. package/docs/es/guide/performance.md +125 -0
  146. package/docs/guide/adapters.md +225 -0
  147. package/docs/guide/ai-repository-schema.md +119 -0
  148. package/docs/guide/architecture.md +69 -1
  149. package/docs/guide/flows.md +134 -0
  150. package/docs/guide/git-intelligence.md +170 -0
  151. package/docs/guide/incremental-analysis.md +131 -0
  152. package/docs/guide/knowledge-graph.md +135 -0
  153. package/docs/guide/lazy-indexing.md +144 -0
  154. package/docs/guide/performance.md +125 -0
  155. package/package.json +6 -3
  156. package/src/analyzers/androidResources.ts +113 -0
  157. package/src/analyzers/dependencies.ts +41 -0
  158. package/src/analyzers/entrypoints.ts +80 -1
  159. package/src/analyzers/gradleModules.ts +100 -0
  160. package/src/analyzers/techStack.ts +56 -0
  161. package/src/commands/ai-first.ts +342 -1
  162. package/src/core/adapters/adapterRegistry.ts +187 -0
  163. package/src/core/adapters/baseAdapter.ts +82 -0
  164. package/src/core/adapters/community/fastapiAdapter.ts +50 -0
  165. package/src/core/adapters/community/index.ts +11 -0
  166. package/src/core/adapters/community/laravelAdapter.ts +56 -0
  167. package/src/core/adapters/community/nestjsAdapter.ts +57 -0
  168. package/src/core/adapters/community/phoenixAdapter.ts +54 -0
  169. package/src/core/adapters/community/springBootAdapter.ts +53 -0
  170. package/src/core/adapters/dotnetAdapter.ts +104 -0
  171. package/src/core/adapters/index.ts +24 -0
  172. package/src/core/adapters/javascriptAdapter.ts +56 -0
  173. package/src/core/adapters/pythonAdapter.ts +118 -0
  174. package/src/core/adapters/railsAdapter.ts +65 -0
  175. package/src/core/adapters/salesforceAdapter.ts +76 -0
  176. package/src/core/adapters/sdk.ts +172 -0
  177. package/src/core/ccp.ts +240 -0
  178. package/src/core/gitAnalyzer.ts +391 -0
  179. package/src/core/incrementalAnalyzer.ts +382 -0
  180. package/src/core/knowledgeGraphBuilder.ts +181 -0
  181. package/src/core/lazyAnalyzer.ts +261 -0
  182. package/src/core/schema.ts +157 -0
  183. package/src/core/semanticContexts.ts +575 -0
  184. package/tests/adapters.test.ts +159 -0
  185. package/tests/gitAnalyzer.test.ts +133 -0
  186. package/tests/incrementalAnalyzer.test.ts +83 -0
  187. package/tests/knowledgeGraph.test.ts +146 -0
  188. package/tests/lazyAnalyzer.test.ts +230 -0
  189. package/tests/schema.test.ts +203 -0
  190. package/tests/semanticContexts.test.ts +435 -0
  191. package/ai/context/analyzers.Symbol.json +0 -19
  192. package/ai/context/analyzers.extractSymbols.json +0 -19
@@ -18,8 +18,17 @@ import { generateModuleGraph } from "../core/moduleGraph.js";
18
18
  import { loadIndexState, computeFileHash, getFilesToIndex, getChangedFilesGit } from "../core/indexState.js";
19
19
  import { chunkFiles } from "../core/chunker.js";
20
20
  import { generateEmbeddings, saveEmbeddings } from "../core/embeddings.js";
21
+ import { generateContextModules, createCCP, listCCPs, getCCP } from "../core/ccp.js";
22
+ import { generateSemanticContexts } from "../core/semanticContexts.js";
21
23
  import { doctorMain } from "./doctor.js";
22
24
  import { exploreMain } from "./explore.js";
25
+ import { listAdapters } from "../core/adapters/index.js";
26
+ import { detectGitRepository, generateGitContext, analyzeGitActivity, getRecentFiles } from "../core/gitAnalyzer.js";
27
+ import { buildKnowledgeGraph, loadKnowledgeGraph } from "../core/knowledgeGraphBuilder.js";
28
+ import { runIncrementalUpdate, detectChangedFiles } from "../core/incrementalAnalyzer.js";
29
+ import { generateAllSchema } from "../core/schema.js";
30
+ import ora from "ora";
31
+ import process from "process";
23
32
 
24
33
  const __filename = fileURLToPath(import.meta.url);
25
34
  const __dirname = path.dirname(__filename);
@@ -147,6 +156,26 @@ export async function runAIFirst(options: AIFirstOptions = {}): Promise<AIFirstR
147
156
  filesCreated.push(aiContextPath);
148
157
  console.log(" ✅ Created ai_context.md");
149
158
 
159
+ // Generate semantic contexts (features and flows)
160
+ try {
161
+ const { features, flows } = generateSemanticContexts(outputDir);
162
+ console.log(` ✅ Created ${features.length} features, ${flows.length} flows`);
163
+ } catch (e: any) {
164
+ console.log(" ⚠️ Semantic contexts: " + (e.message || e));
165
+ }
166
+
167
+ // Generate AI Repository Schema (schema.json, project.json, tools.json)
168
+ try {
169
+ generateAllSchema(rootDir, outputDir);
170
+ console.log(" ✅ Created schema.json, project.json, tools.json");
171
+ } catch (e: any) {
172
+ console.log(" ⚠️ Schema generation: " + (e.message || e));
173
+ }
174
+
175
+ console.log("\n✨ Done! Created the following files:");
176
+
177
+ console.log("\n✨ Done! Created the following files:");
178
+
150
179
  console.log("\n✨ Done! Created the following files:");
151
180
  for (const file of filesCreated) {
152
181
  console.log(` - ${path.relative(rootDir, file)}`);
@@ -1016,6 +1045,9 @@ Commands:
1016
1045
  doctor Check repository health and AI readiness
1017
1046
  explore <module> Explore module dependencies
1018
1047
  map Generate repository map (files, modules, graph)
1048
+ adapters List available adapters
1049
+ explore <module> Explore module dependencies
1050
+ map Generate repository map (files, modules, graph)
1019
1051
 
1020
1052
  Options:
1021
1053
  -r, --root <dir> Root directory to scan (default: current directory)
@@ -1080,12 +1112,321 @@ Options:
1080
1112
  await generateSymbolGraph(rootDir, aiDir);
1081
1113
  console.log(" ✅ symbol-graph.json");
1082
1114
 
1083
- console.log("\n✅ Repository map generated!");
1115
+ // Generate semantic contexts (features and flows)
1116
+ const { features, flows } = generateSemanticContexts(aiDir);
1117
+ console.log(` ✅ Created ${features.length} features, ${flows.length} flows`);
1118
+
1084
1119
  process.exit(0);
1085
1120
  } else if (command === 'doctor') {
1086
1121
  doctorMain(args.slice(1));
1087
1122
  } else if (command === 'explore') {
1088
1123
  exploreMain(args.slice(1));
1124
+ } else if (command === 'adapters') {
1125
+ // Adapters command - list available adapters
1126
+ args.shift();
1127
+
1128
+ const showJson = args.includes('--json');
1129
+
1130
+ if (args.includes('--help') || args.includes('-h')) {
1131
+ console.log(`
1132
+ adapters - List available adapters
1133
+
1134
+ Usage: ai-first adapters [options]
1135
+
1136
+ Options:
1137
+ --json Output as JSON
1138
+ -h, --help Show help message
1139
+
1140
+ Examples:
1141
+ ai-first adapters
1142
+ ai-first adapters --json
1143
+ `);
1144
+ process.exit(0);
1145
+ }
1146
+
1147
+ const adapters = listAdapters();
1148
+
1149
+ if (showJson) {
1150
+ console.log(JSON.stringify(adapters, null, 2));
1151
+ } else {
1152
+ console.log("\n📦 Available adapters:\n");
1153
+ console.log("Name | Display Name");
1154
+ console.log("--------------------|-------------------");
1155
+ for (const adapter of adapters) {
1156
+ console.log(`${adapter.name.padEnd(18)}| ${adapter.displayName}`);
1157
+ }
1158
+ console.log(`\nTotal: ${adapters.length} adapters`);
1159
+ }
1160
+ process.exit(0);
1161
+ } else if (command === 'git') {
1162
+ // Git intelligence command
1163
+ args.shift();
1164
+
1165
+ let rootDir = process.cwd();
1166
+ let aiDir = path.join(rootDir, "ai");
1167
+ let limit = 50;
1168
+ let showActivity = false;
1169
+ let showJson = false;
1170
+
1171
+ for (let i = 0; i < args.length; i++) {
1172
+ const arg = args[i];
1173
+ switch (arg) {
1174
+ case "--root":
1175
+ case "-r":
1176
+ rootDir = args[++i];
1177
+ aiDir = path.join(rootDir, "ai");
1178
+ break;
1179
+ case "--limit":
1180
+ case "-n":
1181
+ limit = parseInt(args[++i], 10) || 50;
1182
+ break;
1183
+ case "--activity":
1184
+ case "-a":
1185
+ showActivity = true;
1186
+ break;
1187
+ case "--json":
1188
+ showJson = true;
1189
+ break;
1190
+ case "--help":
1191
+ case "-h":
1192
+ console.log(`
1193
+ git - Analyze recent git activity
1194
+
1195
+ Usage: ai-first git [options]
1196
+
1197
+ Options:
1198
+ -r, --root <dir> Root directory (default: current directory)
1199
+ -n, --limit <n> Number of commits to analyze (default: 50)
1200
+ -a, --activity Show commit activity details
1201
+ --json Output as JSON
1202
+ -h, --help Show help message
1203
+
1204
+ Examples:
1205
+ ai-first git
1206
+ ai-first git --limit 100
1207
+ ai-first git --activity
1208
+ `);
1209
+ process.exit(0);
1210
+ }
1211
+ }
1212
+
1213
+ if (!detectGitRepository(rootDir)) {
1214
+ console.log("❌ Not a git repository");
1215
+ process.exit(1);
1216
+ }
1217
+
1218
+ console.log(`\n📊 Analyzing git activity in: ${rootDir}\n`);
1219
+
1220
+ const result = generateGitContext(rootDir, aiDir);
1221
+
1222
+ if (showJson) {
1223
+ console.log(JSON.stringify(result, null, 2));
1224
+ } else {
1225
+ console.log("📁 Recent files:");
1226
+ for (const file of result.recentFiles.slice(0, 10)) {
1227
+ console.log(` - ${file}`);
1228
+ }
1229
+ if (result.recentFiles.length > 10) {
1230
+ console.log(` ... and ${result.recentFiles.length - 10} more`);
1231
+ }
1232
+
1233
+ if (result.recentFeatures.length > 0) {
1234
+ console.log("\n🎯 Recent features:");
1235
+ console.log(` ${result.recentFeatures.join(", ")}`);
1236
+ }
1237
+
1238
+ if (result.recentFlows.length > 0) {
1239
+ console.log("\n🔄 Recent flows:");
1240
+ console.log(` ${result.recentFlows.join(", ")}`);
1241
+ }
1242
+
1243
+ if (showActivity && result.activity) {
1244
+ console.log("\n📈 Commit activity:");
1245
+ console.log(` Total commits: ${result.activity.totalCommits}`);
1246
+ console.log(` Date range: ${result.activity.dateRange.start.slice(0, 10)} - ${result.activity.dateRange.end.slice(0, 10)}`);
1247
+
1248
+ const topFiles = Object.entries(result.activity.files)
1249
+ .sort((a, b) => b[1] - a[1])
1250
+ .slice(0, 5);
1251
+ if (topFiles.length > 0) {
1252
+ console.log("\n Most changed files:");
1253
+ for (const [file, count] of topFiles) {
1254
+ console.log(` - ${file}: ${count} commits`);
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
+ console.log("\n✅ Generated:");
1260
+ console.log(" - ai/git/recent-files.json");
1261
+ console.log(" - ai/git/recent-features.json");
1262
+ console.log(" - ai/git/recent-flows.json");
1263
+ if (result.activity) {
1264
+ console.log(" - ai/git/commit-activity.json");
1265
+ }
1266
+ }
1267
+ process.exit(0);
1268
+ } else if (command === 'graph') {
1269
+ // Knowledge Graph command
1270
+ args.shift();
1271
+
1272
+ let rootDir = process.cwd();
1273
+ let aiDir = path.join(rootDir, "ai");
1274
+ let showJson = false;
1275
+ let showStats = false;
1276
+
1277
+ for (let i = 0; i < args.length; i++) {
1278
+ const arg = args[i];
1279
+ switch (arg) {
1280
+ case "--root":
1281
+ case "-r":
1282
+ rootDir = args[++i];
1283
+ aiDir = path.join(rootDir, "ai");
1284
+ break;
1285
+ case "--json":
1286
+ showJson = true;
1287
+ break;
1288
+ case "--stats":
1289
+ case "-s":
1290
+ showStats = true;
1291
+ break;
1292
+ case "--help":
1293
+ case "-h":
1294
+ console.log(`
1295
+ graph - Generate repository knowledge graph
1296
+
1297
+ Usage: ai-first graph [options]
1298
+
1299
+ Options:
1300
+ -r, --root <dir> Root directory (default: current directory)
1301
+ -s, --stats Show graph statistics
1302
+ --json Output as JSON
1303
+ -h, --help Show help message
1304
+
1305
+ Examples:
1306
+ ai-first graph
1307
+ ai-first graph --stats
1308
+ `);
1309
+ process.exit(0);
1310
+ }
1311
+ }
1312
+
1313
+ if (!detectGitRepository(rootDir)) {
1314
+ console.log("❌ Not a git repository");
1315
+ process.exit(1);
1316
+ }
1317
+
1318
+ console.log(`\n🕸️ Building knowledge graph in: ${rootDir}\n`);
1319
+
1320
+ const graph = buildKnowledgeGraph(rootDir, aiDir);
1321
+
1322
+ if (showJson) {
1323
+ console.log(JSON.stringify(graph, null, 2));
1324
+ } else {
1325
+ console.log("📊 Graph Statistics:");
1326
+ console.log(` Nodes: ${graph.nodes.length}`);
1327
+ console.log(` Edges: ${graph.edges.length}`);
1328
+ console.log(` Sources: ${graph.metadata.sources.join(", ") || "none"}`);
1329
+
1330
+ const nodeTypes = graph.nodes.reduce((acc, n) => { acc[n.type] = (acc[n.type] || 0) + 1; return acc; }, {} as Record<string, number>);
1331
+ const edgeTypes = graph.edges.reduce((acc, e) => { acc[e.type] = (acc[e.type] || 0) + 1; return acc; }, {} as Record<string, number>);
1332
+
1333
+ console.log("\n📍 Node Types:");
1334
+ for (const [type, count] of Object.entries(nodeTypes)) {
1335
+ console.log(` ${type}: ${count}`);
1336
+ }
1337
+
1338
+ console.log("\n🔗 Edge Types:");
1339
+ for (const [type, count] of Object.entries(edgeTypes)) {
1340
+ console.log(` ${type}: ${count}`);
1341
+ }
1342
+
1343
+ console.log("\n✅ Generated:");
1344
+ console.log(" - ai/graph/knowledge-graph.json");
1345
+ }
1346
+ process.exit(0);
1347
+ } else if (command === 'update') {
1348
+ // Incremental update command
1349
+ args.shift();
1350
+
1351
+ let rootDir = process.cwd();
1352
+ let aiDir = path.join(rootDir, "ai");
1353
+ let useGit = true;
1354
+ let showJson = false;
1355
+
1356
+ for (let i = 0; i < args.length; i++) {
1357
+ const arg = args[i];
1358
+ switch (arg) {
1359
+ case "--root":
1360
+ case "-r":
1361
+ rootDir = args[++i];
1362
+ aiDir = path.join(rootDir, "ai");
1363
+ break;
1364
+ case "--no-git":
1365
+ useGit = false;
1366
+ break;
1367
+ case "--json":
1368
+ showJson = true;
1369
+ break;
1370
+ case "--help":
1371
+ case "-h":
1372
+ console.log(`
1373
+ update - Incrementally update repository context
1374
+
1375
+ Usage: ai-first update [options]
1376
+
1377
+ Options:
1378
+ -r, --root <dir> Root directory (default: current directory)
1379
+ --no-git Use filesystem timestamps instead of git
1380
+ --json Output as JSON
1381
+ -h, --help Show help message
1382
+
1383
+ Examples:
1384
+ ai-first update
1385
+ ai-first update --no-git
1386
+ `);
1387
+ process.exit(0);
1388
+ }
1389
+ }
1390
+
1391
+ if (!fs.existsSync(aiDir)) {
1392
+ console.log("❌ AI context not found. Run 'ai-first init' first.");
1393
+ process.exit(1);
1394
+ }
1395
+
1396
+ console.log(`\n🔄 Running incremental update in: ${rootDir}\n`);
1397
+
1398
+ const result = runIncrementalUpdate(rootDir, aiDir);
1399
+
1400
+ if (result.errors.length > 0) {
1401
+ console.log("⚠️ Errors:");
1402
+ for (const error of result.errors) {
1403
+ console.log(` - ${error}`);
1404
+ }
1405
+ }
1406
+
1407
+ if (showJson) {
1408
+ console.log(JSON.stringify(result, null, 2));
1409
+ } else {
1410
+ console.log(`📁 Changed files: ${result.changedFiles.length}`);
1411
+ if (result.changedFiles.length > 0) {
1412
+ for (const file of result.changedFiles.slice(0, 5)) {
1413
+ console.log(` ${file.status}: ${file.path}`);
1414
+ }
1415
+ if (result.changedFiles.length > 5) {
1416
+ console.log(` ... and ${result.changedFiles.length - 5} more`);
1417
+ }
1418
+ }
1419
+
1420
+ console.log(`\n🔧 Updated:`);
1421
+ console.log(` Symbols: ${result.updatedSymbols}`);
1422
+ console.log(` Dependencies: ${result.updatedDependencies}`);
1423
+ console.log(` Features: ${result.updatedFeatures.join(", ") || "none"}`);
1424
+ console.log(` Flows: ${result.updatedFlows.join(", ") || "none"}`);
1425
+ console.log(` Knowledge Graph: ${result.graphUpdated ? "✅" : "❌"}`);
1426
+ }
1427
+
1428
+ process.exit(0);
1429
+ process.exit(0);
1089
1430
  } else {
1090
1431
  console.log(`Unknown command: ${command}`);
1091
1432
  console.log(`Use 'ai-first --help' for usage information`);
@@ -0,0 +1,187 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { AnalysisAdapter, DEFAULT_ADAPTER } from "./baseAdapter.js";
4
+ import { javascriptAdapter } from "./javascriptAdapter.js";
5
+ import { pythonAdapter, djangoAdapter, flaskAdapter } from "./pythonAdapter.js";
6
+ import { railsAdapter, rubyAdapter } from "./railsAdapter.js";
7
+ import { salesforceAdapter, sfdxAdapter } from "./salesforceAdapter.js";
8
+ import { dotnetAdapter, aspnetCoreAdapter, blazorAdapter } from "./dotnetAdapter.js";
9
+ import { laravelAdapter, nestjsAdapter, springBootAdapter, phoenixAdapter, fastapiAdapter } from "./community/index.js";
10
+
11
+ /**
12
+ * Registry of all available adapters
13
+ */
14
+ export const ADAPTERS: AnalysisAdapter[] = [
15
+ // JavaScript/TypeScript
16
+ javascriptAdapter,
17
+ nestjsAdapter,
18
+
19
+ // Ruby (must be before Python to detect Gemfile correctly)
20
+ railsAdapter,
21
+ rubyAdapter,
22
+
23
+ // Python
24
+ djangoAdapter,
25
+ flaskAdapter,
26
+ pythonAdapter,
27
+ fastapiAdapter,
28
+
29
+ // PHP
30
+ laravelAdapter,
31
+
32
+ // Elixir
33
+ phoenixAdapter,
34
+
35
+ // Java/Kotlin
36
+ springBootAdapter,
37
+
38
+ // Salesforce
39
+ sfdxAdapter,
40
+ salesforceAdapter,
41
+
42
+ // .NET
43
+ blazorAdapter,
44
+ aspnetCoreAdapter,
45
+ dotnetAdapter
46
+ ];
47
+
48
+ /**
49
+ * Adapter detection result
50
+ */
51
+ export interface AdapterDetectionResult {
52
+ adapter: AnalysisAdapter;
53
+ confidence: number;
54
+ matchedSignals: string[];
55
+ }
56
+
57
+ /**
58
+ * Detect the appropriate adapter for a project
59
+ *
60
+ * @param rootDir - Project root directory
61
+ * @returns The best matching adapter
62
+ */
63
+ export function detectAdapter(rootDir: string): AnalysisAdapter {
64
+ const results = detectAllAdapters(rootDir);
65
+
66
+ if (results.length === 0) {
67
+ return DEFAULT_ADAPTER;
68
+ }
69
+
70
+ // Return the adapter with highest confidence
71
+ return results[0].adapter;
72
+ }
73
+
74
+ /**
75
+ * Detect all matching adapters with confidence scores
76
+ *
77
+ * @param rootDir - Project root directory
78
+ * @returns Array of matching adapters sorted by confidence
79
+ */
80
+ export function detectAllAdapters(rootDir: string): AdapterDetectionResult[] {
81
+ const results: AdapterDetectionResult[] = [];
82
+
83
+ for (const adapter of ADAPTERS) {
84
+ const matchedSignals: string[] = [];
85
+ let score = 0;
86
+
87
+ for (const signal of adapter.detectionSignals) {
88
+ const matched = matchSignal(rootDir, signal);
89
+ if (matched) {
90
+ matchedSignals.push(signal.pattern);
91
+ score += getSignalWeight(signal);
92
+ }
93
+ }
94
+
95
+ if (score > 0) {
96
+ results.push({
97
+ adapter,
98
+ confidence: score,
99
+ matchedSignals
100
+ });
101
+ }
102
+ }
103
+
104
+ // Sort by confidence (highest first)
105
+ results.sort((a, b) => b.confidence - a.confidence);
106
+
107
+ return results;
108
+ }
109
+
110
+ /**
111
+ * Match a detection signal against the project
112
+ */
113
+ function matchSignal(rootDir: string, signal: { type: string; pattern: string; contentPattern?: string }): boolean {
114
+ switch (signal.type) {
115
+ case 'file':
116
+ return matchFileSignal(rootDir, signal.pattern);
117
+
118
+ case 'directory':
119
+ return matchDirectorySignal(rootDir, signal.pattern);
120
+
121
+ case 'content':
122
+ // Content signals require more complex implementation
123
+ // For now, we skip content-based detection
124
+ return false;
125
+
126
+ default:
127
+ return false;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Match a file signal
133
+ */
134
+ function matchFileSignal(rootDir: string, pattern: string): boolean {
135
+ // Simple glob support for *.csproj style patterns
136
+ if (pattern.startsWith('*')) {
137
+ const ext = pattern.slice(1);
138
+ if (!fs.existsSync(rootDir)) return false;
139
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
140
+ return entries.some(e => e.isFile() && e.name.endsWith(ext));
141
+ }
142
+
143
+ return fs.existsSync(path.join(rootDir, pattern));
144
+ }
145
+
146
+ /**
147
+ * Match a directory signal
148
+ */
149
+ function matchDirectorySignal(rootDir: string, pattern: string): boolean {
150
+ if (!fs.existsSync(rootDir)) return false;
151
+
152
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
153
+ return entries.some(e => e.isDirectory() && e.name === pattern);
154
+ }
155
+
156
+ /**
157
+ * Get weight for a signal type
158
+ */
159
+ function getSignalWeight(signal: { type: string; pattern: string }): number {
160
+ // Higher weight for more specific signals
161
+ switch (signal.type) {
162
+ case 'content':
163
+ return 3;
164
+ case 'directory':
165
+ return 2;
166
+ case 'file':
167
+ default:
168
+ // Higher weight for more specific file patterns
169
+ if (signal.pattern.startsWith('*')) return 1;
170
+ if (signal.pattern.includes('.')) return 2;
171
+ return 1;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Get adapter by name
177
+ */
178
+ export function getAdapter(name: string): AnalysisAdapter | undefined {
179
+ return ADAPTERS.find(a => a.name === name);
180
+ }
181
+
182
+ /**
183
+ * List all available adapters
184
+ */
185
+ export function listAdapters(): { name: string; displayName: string }[] {
186
+ return ADAPTERS.map(a => ({ name: a.name, displayName: a.displayName }));
187
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Analysis Adapter Interface
3
+ *
4
+ * Defines the contract for language/framework-specific adapters
5
+ * that customize feature and flow detection.
6
+ */
7
+
8
+ export interface AnalysisAdapter {
9
+ /** Unique identifier for the adapter */
10
+ name: string;
11
+
12
+ /** Human-readable display name */
13
+ displayName: string;
14
+
15
+ /** File/directory markers that indicate this adapter should be used */
16
+ detectionSignals: DetectionSignal[];
17
+
18
+ /** Root directories to scan for features */
19
+ featureRoots: string[];
20
+
21
+ /** Folders to ignore when detecting features */
22
+ ignoredFolders: string[];
23
+
24
+ /** File name patterns that indicate entrypoints */
25
+ entrypointPatterns: string[];
26
+
27
+ /** Layer definitions for flow detection */
28
+ layerRules: LayerRule[];
29
+
30
+ /** Additional file extensions to include */
31
+ supportedExtensions: string[];
32
+
33
+ /** Patterns for flow entrypoints */
34
+ flowEntrypointPatterns: string[];
35
+
36
+ /** Patterns to exclude from flows */
37
+ flowExcludePatterns: string[];
38
+ }
39
+
40
+ export interface DetectionSignal {
41
+ /** Type of signal */
42
+ type: 'file' | 'directory' | 'content';
43
+
44
+ /** Pattern to match */
45
+ pattern: string;
46
+
47
+ /** Optional: content to match inside file */
48
+ contentPattern?: string;
49
+ }
50
+
51
+ export interface LayerRule {
52
+ /** Layer name */
53
+ name: string;
54
+
55
+ /** Priority (lower = earlier in flow) */
56
+ priority: number;
57
+
58
+ /** Patterns that identify this layer */
59
+ patterns: string[];
60
+ }
61
+
62
+ /**
63
+ * Default adapter configuration
64
+ */
65
+ export const DEFAULT_ADAPTER: AnalysisAdapter = {
66
+ name: 'default',
67
+ displayName: 'Default',
68
+ detectionSignals: [],
69
+ featureRoots: ['src', 'app', 'lib', 'modules'],
70
+ ignoredFolders: ['utils', 'helpers', 'types', 'interfaces', 'constants', 'config', 'dto', 'models', 'common', 'shared', 'node_modules', '.git'],
71
+ entrypointPatterns: ['controller', 'route', 'handler', 'command', 'service'],
72
+ layerRules: [
73
+ { name: 'api', priority: 1, patterns: ['controller', 'handler', 'route', 'router', 'api', 'endpoint'] },
74
+ { name: 'service', priority: 2, patterns: ['service', 'services', 'usecase', 'interactor'] },
75
+ { name: 'data', priority: 3, patterns: ['repository', 'repo', 'dal', 'dao', 'data', 'persistence'] },
76
+ { name: 'domain', priority: 4, patterns: ['model', 'entity', 'schema', 'domain'] },
77
+ { name: 'util', priority: 5, patterns: ['util', 'helper', 'lib', 'common'] }
78
+ ],
79
+ supportedExtensions: ['.ts', '.tsx', '.js', '.jsx', '.py', '.java', '.kt', '.go', '.rs', '.rb', '.php', '.cs', '.vue', '.svelte'],
80
+ flowEntrypointPatterns: ['controller', 'route', 'handler', 'command'],
81
+ flowExcludePatterns: ['repository', 'repo', 'utils', 'helper', 'model', 'entity', 'dto', 'type', 'interface', 'constant', 'config']
82
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * FastAPI Adapter
3
+ *
4
+ * Community adapter for Python FastAPI projects
5
+ */
6
+
7
+ import { createAdapter, fileSignal, directorySignal, layerRule } from '../sdk.js';
8
+
9
+ export const fastapiAdapter = createAdapter({
10
+ name: 'fastapi',
11
+ displayName: 'FastAPI',
12
+
13
+ detectionSignals: [
14
+ ],
15
+
16
+ featureRoots: [
17
+ 'app',
18
+ 'app/api',
19
+ 'app/services',
20
+ 'app/models',
21
+ 'app/schemas'
22
+ ],
23
+
24
+ ignoredFolders: [
25
+ 'venv', '.venv', '__pycache__', '.git',
26
+ 'tests', 'test', '.pytest_cache', 'docs'
27
+ ],
28
+
29
+ entrypointPatterns: [
30
+ 'router', 'APIRouter', 'endpoint', 'route',
31
+ 'view', 'controller', 'service'
32
+ ],
33
+
34
+ layerRules: [
35
+ layerRule('api', 1, ['router', 'APIRouter', 'endpoint', 'route', 'view']),
36
+ layerRule('service', 2, ['service', 'usecase', 'interactor', 'business']),
37
+ layerRule('data', 3, ['model', 'schema', 'repository', 'dao']),
38
+ layerRule('domain', 4, ['entity', 'schema', 'domain']),
39
+ layerRule('util', 5, ['util', 'helper', 'lib', 'core'])
40
+ ],
41
+
42
+ supportedExtensions: ['.py'],
43
+
44
+ flowEntrypointPatterns: ['router', 'APIRouter', 'endpoint', 'route'],
45
+
46
+ flowExcludePatterns: [
47
+ 'model', 'schema', 'repository', 'test', 'tests',
48
+ '__pycache__', 'conftest'
49
+ ]
50
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Community Adapters
3
+ *
4
+ * Community-contributed adapters for various frameworks
5
+ */
6
+
7
+ export { laravelAdapter } from './laravelAdapter.js';
8
+ export { nestjsAdapter } from './nestjsAdapter.js';
9
+ export { springBootAdapter } from './springBootAdapter.js';
10
+ export { phoenixAdapter } from './phoenixAdapter.js';
11
+ export { fastapiAdapter } from './fastapiAdapter.js';