@codexa/cli 8.5.0 → 8.6.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.
@@ -1,754 +1,754 @@
1
- import { getDb } from "../db/connection";
2
- import { initSchema } from "../db/schema";
3
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
- import { join } from "path";
5
- import { getDetectors } from "../detectors/loader";
6
-
7
- // ============================================================
8
- // UNIVERSAL DOCS REGISTRY
9
- // Maps technology names (as returned by detectors) to canonical name + docs URL.
10
- // Covers all 7 ecosystems: Node, Python, Go, .NET, Rust, JVM, Flutter.
11
- // ============================================================
12
-
13
- const UNIVERSAL_DOCS_REGISTRY: Record<string, { canonical: string; docsUrl: string }> = {
14
- // ── Node.js / JavaScript / TypeScript ──────────────────────
15
- // Frontend
16
- "Next.js": { canonical: "nextjs", docsUrl: "https://nextjs.org/docs" },
17
- "React": { canonical: "react", docsUrl: "https://react.dev" },
18
- "Vue": { canonical: "vue", docsUrl: "https://vuejs.org/guide" },
19
- "Svelte": { canonical: "svelte", docsUrl: "https://svelte.dev/docs" },
20
- "Angular": { canonical: "angular", docsUrl: "https://angular.dev/overview" },
21
- "Nuxt": { canonical: "nuxt", docsUrl: "https://nuxt.com/docs" },
22
- "Remix": { canonical: "remix", docsUrl: "https://remix.run/docs" },
23
- "Gatsby": { canonical: "gatsby", docsUrl: "https://www.gatsbyjs.com/docs" },
24
- "SolidJS": { canonical: "solidjs", docsUrl: "https://www.solidjs.com/docs" },
25
- "Preact": { canonical: "preact", docsUrl: "https://preactjs.com/guide" },
26
- "Astro": { canonical: "astro", docsUrl: "https://docs.astro.build" },
27
- "Qwik": { canonical: "qwik", docsUrl: "https://qwik.dev/docs" },
28
- // Backend
29
- "NestJS": { canonical: "nestjs", docsUrl: "https://docs.nestjs.com" },
30
- "Hono": { canonical: "hono", docsUrl: "https://hono.dev/docs" },
31
- "Elysia": { canonical: "elysia", docsUrl: "https://elysiajs.com/introduction" },
32
- "Fastify": { canonical: "fastify", docsUrl: "https://fastify.dev/docs/latest" },
33
- "Koa": { canonical: "koa", docsUrl: "https://koajs.com" },
34
- "Express": { canonical: "express", docsUrl: "https://expressjs.com/en/guide" },
35
- "Hapi": { canonical: "hapi", docsUrl: "https://hapi.dev/tutorials" },
36
- "tRPC": { canonical: "trpc", docsUrl: "https://trpc.io/docs" },
37
- // ORM / Database
38
- "Drizzle": { canonical: "drizzle", docsUrl: "https://orm.drizzle.team/docs" },
39
- "Prisma": { canonical: "prisma", docsUrl: "https://www.prisma.io/docs" },
40
- "TypeORM": { canonical: "typeorm", docsUrl: "https://typeorm.io" },
41
- "Sequelize": { canonical: "sequelize", docsUrl: "https://sequelize.org/docs" },
42
- "Kysely": { canonical: "kysely", docsUrl: "https://kysely.dev/docs/intro" },
43
- "Mongoose": { canonical: "mongoose", docsUrl: "https://mongoosejs.com/docs" },
44
- "Knex": { canonical: "knex", docsUrl: "https://knexjs.org/guide" },
45
- "MikroORM": { canonical: "mikro-orm", docsUrl: "https://mikro-orm.io/docs" },
46
- "Supabase": { canonical: "supabase", docsUrl: "https://supabase.com/docs" },
47
- "Firebase": { canonical: "firebase", docsUrl: "https://firebase.google.com/docs" },
48
- // Styling
49
- "Tailwind CSS": { canonical: "tailwind", docsUrl: "https://tailwindcss.com/docs" },
50
- "Styled Components": { canonical: "styled-components", docsUrl: "https://styled-components.com/docs" },
51
- "Emotion": { canonical: "emotion", docsUrl: "https://emotion.sh/docs/introduction" },
52
- "Sass": { canonical: "sass", docsUrl: "https://sass-lang.com/documentation" },
53
- "Vanilla Extract": { canonical: "vanilla-extract", docsUrl: "https://vanilla-extract.style/documentation" },
54
- // Auth
55
- "NextAuth.js": { canonical: "next-auth", docsUrl: "https://next-auth.js.org/getting-started" },
56
- "Auth.js": { canonical: "auth-js", docsUrl: "https://authjs.dev/getting-started" },
57
- "Clerk": { canonical: "clerk", docsUrl: "https://clerk.com/docs" },
58
- "Passport.js": { canonical: "passport", docsUrl: "https://www.passportjs.org/docs" },
59
- "Lucia": { canonical: "lucia", docsUrl: "https://lucia-auth.com" },
60
- "Better Auth": { canonical: "better-auth", docsUrl: "https://www.better-auth.com/docs" },
61
- // Testing
62
- "Vitest": { canonical: "vitest", docsUrl: "https://vitest.dev/guide" },
63
- "Jest": { canonical: "jest", docsUrl: "https://jestjs.io/docs/getting-started" },
64
- "Testing Library": { canonical: "testing-library", docsUrl: "https://testing-library.com/docs" },
65
- "Playwright": { canonical: "playwright", docsUrl: "https://playwright.dev/docs/intro" },
66
- "Cypress": { canonical: "cypress", docsUrl: "https://docs.cypress.io" },
67
- "Mocha": { canonical: "mocha", docsUrl: "https://mochajs.org" },
68
- // State / Validation
69
- "zustand": { canonical: "zustand", docsUrl: "https://docs.pmnd.rs/zustand/getting-started/introduction" },
70
- "TanStack Query": { canonical: "tanstack-query", docsUrl: "https://tanstack.com/query/latest/docs" },
71
- "SWR": { canonical: "swr", docsUrl: "https://swr.vercel.app/docs/getting-started" },
72
- "Zod": { canonical: "zod", docsUrl: "https://zod.dev" },
73
-
74
- // ── Python ─────────────────────────────────────────────────
75
- "Django": { canonical: "django", docsUrl: "https://docs.djangoproject.com" },
76
- "FastAPI": { canonical: "fastapi", docsUrl: "https://fastapi.tiangolo.com" },
77
- "Flask": { canonical: "flask", docsUrl: "https://flask.palletsprojects.com" },
78
- "Starlette": { canonical: "starlette", docsUrl: "https://www.starlette.io" },
79
- "Litestar": { canonical: "litestar", docsUrl: "https://docs.litestar.dev" },
80
- "Tornado": { canonical: "tornado", docsUrl: "https://www.tornadoweb.org/en/stable" },
81
- "Pyramid": { canonical: "pyramid", docsUrl: "https://docs.pylonsproject.org/projects/pyramid" },
82
- "aiohttp": { canonical: "aiohttp", docsUrl: "https://docs.aiohttp.org" },
83
- "Sanic": { canonical: "sanic", docsUrl: "https://sanic.readthedocs.io" },
84
- "SQLAlchemy": { canonical: "sqlalchemy", docsUrl: "https://docs.sqlalchemy.org" },
85
- "Tortoise ORM": { canonical: "tortoise-orm", docsUrl: "https://tortoise.github.io" },
86
- "Peewee": { canonical: "peewee", docsUrl: "https://docs.peewee-orm.com" },
87
- "SQLModel": { canonical: "sqlmodel", docsUrl: "https://sqlmodel.tiangolo.com" },
88
- "Beanie": { canonical: "beanie", docsUrl: "https://beanie-odm.dev" },
89
- "MongoEngine": { canonical: "mongoengine", docsUrl: "https://docs.mongoengine.org" },
90
- "pytest": { canonical: "pytest", docsUrl: "https://docs.pytest.org" },
91
- "Hypothesis": { canonical: "hypothesis", docsUrl: "https://hypothesis.readthedocs.io" },
92
- "TensorFlow": { canonical: "tensorflow", docsUrl: "https://www.tensorflow.org/guide" },
93
- "PyTorch": { canonical: "pytorch", docsUrl: "https://pytorch.org/docs/stable" },
94
- "scikit-learn": { canonical: "scikit-learn", docsUrl: "https://scikit-learn.org/stable/user_guide" },
95
- "LangChain": { canonical: "langchain", docsUrl: "https://python.langchain.com/docs" },
96
- "Pandas": { canonical: "pandas", docsUrl: "https://pandas.pydata.org/docs" },
97
- "Hugging Face Transformers": { canonical: "transformers", docsUrl: "https://huggingface.co/docs/transformers" },
98
- "OpenAI SDK": { canonical: "openai-sdk", docsUrl: "https://platform.openai.com/docs" },
99
-
100
- // ── Go ─────────────────────────────────────────────────────
101
- "Gin": { canonical: "gin", docsUrl: "https://gin-gonic.com/docs" },
102
- "Echo": { canonical: "echo", docsUrl: "https://echo.labstack.com/docs" },
103
- "Fiber": { canonical: "fiber", docsUrl: "https://docs.gofiber.io" },
104
- "Chi": { canonical: "chi", docsUrl: "https://go-chi.io" },
105
- "Gorilla Mux": { canonical: "gorilla-mux", docsUrl: "https://github.com/gorilla/mux" },
106
- "Beego": { canonical: "beego", docsUrl: "https://beego.wiki" },
107
- "GORM": { canonical: "gorm", docsUrl: "https://gorm.io/docs" },
108
- "Ent": { canonical: "ent", docsUrl: "https://entgo.io/docs/getting-started" },
109
- "SQLx": { canonical: "sqlx-go", docsUrl: "https://github.com/jmoiron/sqlx" },
110
- "SQLC": { canonical: "sqlc", docsUrl: "https://docs.sqlc.dev" },
111
- "Testify": { canonical: "testify", docsUrl: "https://github.com/stretchr/testify" },
112
- "Ginkgo": { canonical: "ginkgo", docsUrl: "https://onsi.github.io/ginkgo" },
113
- "Cobra CLI": { canonical: "cobra", docsUrl: "https://cobra.dev" },
114
- "Viper Config": { canonical: "viper", docsUrl: "https://github.com/spf13/viper" },
115
- "Zap Logger": { canonical: "zap", docsUrl: "https://pkg.go.dev/go.uber.org/zap" },
116
-
117
- // ── .NET / C# ──────────────────────────────────────────────
118
- "ASP.NET Core": { canonical: "aspnet-core", docsUrl: "https://learn.microsoft.com/aspnet/core" },
119
- "ASP.NET Core MVC": { canonical: "aspnet-mvc", docsUrl: "https://learn.microsoft.com/aspnet/core/mvc" },
120
- "Blazor": { canonical: "blazor", docsUrl: "https://learn.microsoft.com/aspnet/core/blazor" },
121
- "Blazor WebAssembly": { canonical: "blazor-wasm", docsUrl: "https://learn.microsoft.com/aspnet/core/blazor" },
122
- ".NET MAUI": { canonical: "maui", docsUrl: "https://learn.microsoft.com/dotnet/maui" },
123
- "Entity Framework": { canonical: "ef-core", docsUrl: "https://learn.microsoft.com/ef/core" },
124
- "EF Core + PostgreSQL": { canonical: "ef-postgres", docsUrl: "https://www.npgsql.org/efcore" },
125
- "EF Core + SQL Server": { canonical: "ef-sqlserver", docsUrl: "https://learn.microsoft.com/ef/core/providers/sql-server" },
126
- "Dapper": { canonical: "dapper", docsUrl: "https://github.com/DapperLib/Dapper" },
127
- "NHibernate": { canonical: "nhibernate", docsUrl: "https://nhibernate.info/doc" },
128
- "xUnit": { canonical: "xunit", docsUrl: "https://xunit.net/docs/getting-started" },
129
- "NUnit": { canonical: "nunit", docsUrl: "https://docs.nunit.org" },
130
- "MSTest": { canonical: "mstest", docsUrl: "https://learn.microsoft.com/dotnet/core/testing/unit-testing-with-mstest" },
131
- "FluentAssertions": { canonical: "fluent-assertions", docsUrl: "https://fluentassertions.com/introduction" },
132
- "Moq": { canonical: "moq", docsUrl: "https://github.com/devlooped/moq" },
133
- "ASP.NET Identity": { canonical: "aspnet-identity", docsUrl: "https://learn.microsoft.com/aspnet/core/security/authentication/identity" },
134
- "Duende IdentityServer":{ canonical: "duende-ids", docsUrl: "https://docs.duendesoftware.com" },
135
- "JWT Bearer": { canonical: "jwt-bearer-dotnet", docsUrl: "https://learn.microsoft.com/aspnet/core/security/authentication" },
136
-
137
- // ── Rust ───────────────────────────────────────────────────
138
- "Actix Web": { canonical: "actix-web", docsUrl: "https://actix.rs/docs" },
139
- "Axum": { canonical: "axum", docsUrl: "https://docs.rs/axum" },
140
- "Rocket": { canonical: "rocket", docsUrl: "https://rocket.rs/guide" },
141
- "Warp": { canonical: "warp", docsUrl: "https://docs.rs/warp" },
142
- "Tide": { canonical: "tide", docsUrl: "https://docs.rs/tide" },
143
- "Poem": { canonical: "poem", docsUrl: "https://docs.rs/poem" },
144
- "Diesel": { canonical: "diesel", docsUrl: "https://diesel.rs/guides" },
145
- "SeaORM": { canonical: "sea-orm", docsUrl: "https://www.sea-ql.org/SeaORM/docs" },
146
- "SQLx (Rust)": { canonical: "sqlx-rust", docsUrl: "https://docs.rs/sqlx" },
147
- "Tokio": { canonical: "tokio", docsUrl: "https://tokio.rs/tokio/tutorial" },
148
- "Serde": { canonical: "serde", docsUrl: "https://serde.rs" },
149
-
150
- // ── JVM (Java / Kotlin) ────────────────────────────────────
151
- "Spring Boot": { canonical: "spring-boot", docsUrl: "https://docs.spring.io/spring-boot/reference" },
152
- "Spring WebFlux": { canonical: "spring-webflux", docsUrl: "https://docs.spring.io/spring-framework/reference/web/webflux.html" },
153
- "Quarkus": { canonical: "quarkus", docsUrl: "https://quarkus.io/guides" },
154
- "Micronaut": { canonical: "micronaut", docsUrl: "https://docs.micronaut.io" },
155
- "Ktor": { canonical: "ktor", docsUrl: "https://ktor.io/docs" },
156
- "Vert.x": { canonical: "vertx", docsUrl: "https://vertx.io/docs" },
157
- "Hibernate": { canonical: "hibernate", docsUrl: "https://hibernate.org/orm/documentation" },
158
- "Spring Data JPA": { canonical: "spring-data-jpa", docsUrl: "https://docs.spring.io/spring-data/jpa/reference" },
159
- "MyBatis": { canonical: "mybatis", docsUrl: "https://mybatis.org/mybatis-3" },
160
- "Exposed": { canonical: "exposed", docsUrl: "https://github.com/JetBrains/Exposed/wiki" },
161
- "jOOQ": { canonical: "jooq", docsUrl: "https://www.jooq.org/doc" },
162
- "JUnit": { canonical: "junit", docsUrl: "https://junit.org/junit5/docs/current/user-guide" },
163
- "TestNG": { canonical: "testng", docsUrl: "https://testng.org/doc" },
164
- "Kotest": { canonical: "kotest", docsUrl: "https://kotest.io/docs/framework/framework.html" },
165
- "Mockito": { canonical: "mockito", docsUrl: "https://site.mockito.org" },
166
- "MockK": { canonical: "mockk", docsUrl: "https://mockk.io" },
167
- "Spring Security": { canonical: "spring-security", docsUrl: "https://docs.spring.io/spring-security/reference" },
168
-
169
- // ── Flutter / Dart ─────────────────────────────────────────
170
- "Flutter": { canonical: "flutter", docsUrl: "https://docs.flutter.dev" },
171
- "Riverpod": { canonical: "riverpod", docsUrl: "https://riverpod.dev/docs" },
172
- "Bloc": { canonical: "bloc", docsUrl: "https://bloclibrary.dev" },
173
- "Provider": { canonical: "provider", docsUrl: "https://pub.dev/packages/provider" },
174
- "GoRouter": { canonical: "go-router", docsUrl: "https://pub.dev/packages/go_router" },
175
- "Dio": { canonical: "dio", docsUrl: "https://pub.dev/packages/dio" },
176
- "Hive": { canonical: "hive", docsUrl: "https://docs.hivedb.dev" },
177
- "Drift": { canonical: "drift", docsUrl: "https://drift.simonbinder.eu/docs" },
178
- "Isar": { canonical: "isar", docsUrl: "https://isar.dev/docs" },
179
- "Mocktail": { canonical: "mocktail", docsUrl: "https://pub.dev/packages/mocktail" },
180
-
181
- // ── Databases (cross-ecosystem) ────────────────────────────
182
- "PostgreSQL": { canonical: "postgresql", docsUrl: "https://www.postgresql.org/docs" },
183
- "MySQL": { canonical: "mysql", docsUrl: "https://dev.mysql.com/doc" },
184
- "MongoDB": { canonical: "mongodb", docsUrl: "https://www.mongodb.com/docs" },
185
- "Redis": { canonical: "redis", docsUrl: "https://redis.io/docs" },
186
- "SQLite": { canonical: "sqlite", docsUrl: "https://www.sqlite.org/docs.html" },
187
- "Elasticsearch": { canonical: "elasticsearch", docsUrl: "https://www.elastic.co/guide/en/elasticsearch" },
188
- "SQL Server": { canonical: "sql-server", docsUrl: "https://learn.microsoft.com/sql/sql-server" },
189
- };
190
-
191
- // ============================================================
192
- // UNIVERSAL AGENT-LIB MAPPING
193
- // Maps agent types to library categories + optional ecosystem filter.
194
- // If ecosystems is set, only libs from those ecosystems are mapped.
195
- // ============================================================
196
-
197
- const UNIVERSAL_AGENT_LIB_CATEGORIES: Record<string, { categories: string[]; ecosystems?: string[] }> = {
198
- // Generic agents (any ecosystem)
199
- "frontend": { categories: ["frontend", "styling", "auth"] },
200
- "backend": { categories: ["backend", "auth"] },
201
- "database": { categories: ["database", "orm"] },
202
- "testing": { categories: ["testing"] },
203
-
204
- // Node.js specific
205
- "frontend-next": { categories: ["frontend", "styling", "auth", "state", "validation"], ecosystems: ["node"] },
206
- "frontend-react": { categories: ["frontend", "styling", "state", "validation"], ecosystems: ["node"] },
207
- "backend-bun": { categories: ["backend", "auth", "validation"], ecosystems: ["node"] },
208
- "backend-node": { categories: ["backend", "auth", "validation"], ecosystems: ["node"] },
209
-
210
- // .NET
211
- "backend-csharp": { categories: ["backend", "auth", "database", "orm"], ecosystems: ["dotnet"] },
212
-
213
- // Go
214
- "backend-go": { categories: ["backend", "auth", "database", "orm"], ecosystems: ["go"] },
215
-
216
- // Flutter
217
- "frontend-flutter": { categories: ["frontend", "auth", "database"], ecosystems: ["flutter"] },
218
-
219
- // Ecosystem-agnostic specialists
220
- "database-postgres": { categories: ["database", "orm"] },
221
- "testing-unit": { categories: ["testing"] },
222
- };
223
-
224
- // ============================================================
225
- // TYPES
226
- // ============================================================
227
-
228
- interface DetectedLib {
229
- name: string;
230
- version: string;
231
- canonical: string;
232
- docsUrl: string;
233
- category: string;
234
- ecosystem: string;
235
- }
236
-
237
- // ============================================================
238
- // UNIVERSAL DETECTION
239
- // ============================================================
240
-
241
- async function detectLibsUniversal(): Promise<DetectedLib[]> {
242
- const detectors = getDetectors();
243
- const detected: DetectedLib[] = [];
244
- const seen = new Set<string>();
245
-
246
- const detectorPromises = detectors.map(async (detector) => {
247
- try {
248
- return await detector.detect(process.cwd());
249
- } catch {
250
- return null;
251
- }
252
- });
253
-
254
- const results = await Promise.all(detectorPromises);
255
-
256
- for (const result of results) {
257
- if (!result || result.technologies.length === 0) continue;
258
-
259
- for (const tech of result.technologies) {
260
- // Skip runtime/build categories — those are languages/tools, not libraries
261
- if (tech.category === "runtime" || tech.category === "build") continue;
262
-
263
- const registry = UNIVERSAL_DOCS_REGISTRY[tech.name];
264
- const canonical = registry?.canonical || tech.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
265
- const docsUrl = registry?.docsUrl || "";
266
-
267
- // Deduplicate by canonical name
268
- if (seen.has(canonical)) continue;
269
- seen.add(canonical);
270
-
271
- detected.push({
272
- name: tech.name,
273
- version: (tech.version || "").replace(/[\^~]/g, ""),
274
- canonical,
275
- docsUrl,
276
- category: tech.category,
277
- ecosystem: result.ecosystem,
278
- });
279
- }
280
- }
281
-
282
- return detected;
283
- }
284
-
285
- // ============================================================
286
- // HELPERS
287
- // ============================================================
288
-
289
- function ensureLibContextDir(): string {
290
- const dir = join(process.cwd(), ".codexa", "lib-context");
291
- if (!existsSync(dir)) {
292
- mkdirSync(dir, { recursive: true });
293
- }
294
- return dir;
295
- }
296
-
297
- function generateLibContextTemplate(lib: DetectedLib): string {
298
- const now = new Date().toISOString().split("T")[0];
299
- const versionDisplay = lib.version || "latest";
300
- const docsLine = lib.docsUrl ? `\nOu edite manualmente com base na documentacao:\n${lib.docsUrl}` : "";
301
-
302
- return `# ${lib.canonical} Context
303
- <!-- version: ${versionDisplay} -->
304
- <!-- updated: ${now} -->
305
- <!-- source: ${lib.docsUrl} -->
306
- <!-- ecosystem: ${lib.ecosystem} -->
307
- <!-- status: pending-research -->
308
-
309
- ## IMPORTANTE
310
-
311
- Este arquivo foi criado automaticamente e precisa ser preenchido com:
312
- 1. Padroes obrigatorios para esta versao
313
- 2. APIs deprecated a evitar
314
- 3. Melhores praticas atuais
315
-
316
- ## Como Preencher
317
-
318
- Use o comando:
319
- \`\`\`bash
320
- bun run .claude/cli/workflow.ts research fill --lib ${lib.canonical}
321
- \`\`\`
322
- ${docsLine}
323
-
324
- ---
325
-
326
- ## Padroes Obrigatorios (v${versionDisplay})
327
-
328
- <!-- Adicione padroes aqui -->
329
-
330
- ## APIs/Padroes Deprecated
331
-
332
- <!-- Liste o que evitar -->
333
-
334
- ## Melhores Praticas
335
-
336
- <!-- Liste melhores praticas -->
337
-
338
- ## Exemplos
339
-
340
- <!-- Adicione exemplos de codigo -->
341
- `;
342
- }
343
-
344
- // ============================================================
345
- // COMMAND: research start
346
- // ============================================================
347
-
348
- export async function researchStart(options: { json?: boolean } = {}): Promise<void> {
349
- initSchema();
350
- const db = getDb();
351
-
352
- const libs = await detectLibsUniversal();
353
-
354
- if (libs.length === 0) {
355
- if (options.json) {
356
- console.log(JSON.stringify({ error: "no_ecosystem_detected", libs: [] }));
357
- } else {
358
- console.error("\nNenhum ecossistema ou biblioteca detectada.");
359
- console.error("Certifique-se de estar na raiz do projeto.");
360
- console.error("Ecossistemas suportados: Node.js, Python, Go, .NET, Rust, Java/Kotlin, Flutter\n");
361
- }
362
- process.exit(1);
363
- }
364
-
365
- const libContextDir = ensureLibContextDir();
366
- const created: string[] = [];
367
- const existing: string[] = [];
368
-
369
- for (const lib of libs) {
370
- const filePath = join(libContextDir, `${lib.canonical}.md`);
371
-
372
- // Registrar no banco
373
- const existingRecord = db
374
- .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
375
- .get(lib.canonical) as any;
376
-
377
- if (existingRecord) {
378
- // Atualizar versao se mudou
379
- if (existingRecord.version !== lib.version && lib.version) {
380
- db.run(
381
- "UPDATE lib_contexts SET version = ?, updated_at = ? WHERE lib_name = ?",
382
- [lib.version, new Date().toISOString(), lib.canonical]
383
- );
384
- }
385
- existing.push(lib.canonical);
386
- } else {
387
- // Criar novo registro
388
- db.run(
389
- `INSERT INTO lib_contexts (lib_name, version, context_file, source_url, researched_at)
390
- VALUES (?, ?, ?, ?, ?)`,
391
- [lib.canonical, lib.version || "latest", filePath, lib.docsUrl, new Date().toISOString()]
392
- );
393
-
394
- // Criar arquivo se nao existe
395
- if (!existsSync(filePath)) {
396
- writeFileSync(filePath, generateLibContextTemplate(lib));
397
- }
398
-
399
- created.push(lib.canonical);
400
- }
401
-
402
- // Registrar mapeamento de agente (com filtro de ecosystem)
403
- const agentTypes = Object.entries(UNIVERSAL_AGENT_LIB_CATEGORIES)
404
- .filter(([_, config]) => {
405
- const categoryMatch = config.categories.includes(lib.category);
406
- const ecosystemMatch = !config.ecosystems || config.ecosystems.includes(lib.ecosystem);
407
- return categoryMatch && ecosystemMatch;
408
- })
409
- .map(([agent]) => agent);
410
-
411
- for (const agentType of agentTypes) {
412
- const existingMapping = db
413
- .query("SELECT * FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?")
414
- .get(agentType, lib.canonical);
415
-
416
- if (!existingMapping) {
417
- db.run(
418
- "INSERT INTO agent_lib_mappings (agent_type, lib_name, priority) VALUES (?, ?, ?)",
419
- [agentType, lib.canonical, 0]
420
- );
421
- }
422
- }
423
- }
424
-
425
- if (options.json) {
426
- // Coletar ecossistemas detectados
427
- const ecosystems = [...new Set(libs.map(l => l.ecosystem))];
428
- console.log(JSON.stringify({
429
- detected: libs.length,
430
- created: created.length,
431
- existing: existing.length,
432
- ecosystems,
433
- libs: libs.map(l => ({
434
- name: l.canonical,
435
- version: l.version || "latest",
436
- category: l.category,
437
- ecosystem: l.ecosystem,
438
- status: created.includes(l.canonical) ? "created" : "existing"
439
- }))
440
- }, null, 2));
441
- return;
442
- }
443
-
444
- // Coletar ecossistemas
445
- const ecosystems = [...new Set(libs.map(l => l.ecosystem))];
446
-
447
- console.log(`\n${"=".repeat(60)}`);
448
- console.log(`RESEARCH: Bibliotecas Detectadas`);
449
- console.log(`${"=".repeat(60)}`);
450
- console.log(`\nEcossistemas: ${ecosystems.join(", ")}`);
451
- console.log(`Encontradas ${libs.length} bibliotecas:\n`);
452
-
453
- // Agrupar por ecossistema > categoria
454
- const byEcosystem: Record<string, Record<string, DetectedLib[]>> = {};
455
- for (const lib of libs) {
456
- if (!byEcosystem[lib.ecosystem]) {
457
- byEcosystem[lib.ecosystem] = {};
458
- }
459
- if (!byEcosystem[lib.ecosystem][lib.category]) {
460
- byEcosystem[lib.ecosystem][lib.category] = [];
461
- }
462
- byEcosystem[lib.ecosystem][lib.category].push(lib);
463
- }
464
-
465
- for (const [ecosystem, categories] of Object.entries(byEcosystem)) {
466
- console.log(` [${ecosystem.toUpperCase()}]`);
467
- for (const [category, categoryLibs] of Object.entries(categories)) {
468
- console.log(` ${category}:`);
469
- for (const lib of categoryLibs) {
470
- const status = created.includes(lib.canonical) ? "[NOVO]" : "[OK]";
471
- const version = lib.version ? `@${lib.version}` : "";
472
- console.log(` ${status} ${lib.canonical}${version}`);
473
- }
474
- }
475
- console.log("");
476
- }
477
-
478
- console.log(`${"─".repeat(60)}`);
479
- console.log(`Arquivos de contexto em: .codexa/lib-context/`);
480
-
481
- if (created.length > 0) {
482
- console.log(`\nCriados ${created.length} novos arquivos de contexto.`);
483
- console.log(`\nProximos passos:`);
484
- console.log(`1. Preencha os arquivos com padroes atualizados`);
485
- console.log(`2. Use: research fill --lib <nome> para assistencia`);
486
- console.log(`3. Ou edite manualmente baseado na documentacao\n`);
487
- } else {
488
- console.log(`\nTodos os arquivos de contexto ja existem.`);
489
- console.log(`Use: research show para ver o status\n`);
490
- }
491
- }
492
-
493
- // ============================================================
494
- // COMMAND: research show
495
- // ============================================================
496
-
497
- export function researchShow(options: { json?: boolean; lib?: string } = {}): void {
498
- initSchema();
499
- const db = getDb();
500
-
501
- let query = "SELECT * FROM lib_contexts";
502
- let params: any[] = [];
503
-
504
- if (options.lib) {
505
- query += " WHERE lib_name = ?";
506
- params.push(options.lib);
507
- }
508
-
509
- query += " ORDER BY lib_name";
510
-
511
- const libs = db.query(query).all(...params) as any[];
512
-
513
- if (libs.length === 0) {
514
- if (options.json) {
515
- console.log(JSON.stringify({ libs: [] }));
516
- } else {
517
- console.error("\nNenhuma biblioteca registrada.");
518
- console.error("Execute: research start\n");
519
- }
520
- process.exit(1);
521
- }
522
-
523
- if (options.json) {
524
- // Adicionar mapeamentos de agentes
525
- const result = libs.map(lib => {
526
- const mappings = db
527
- .query("SELECT agent_type FROM agent_lib_mappings WHERE lib_name = ?")
528
- .all(lib.lib_name) as any[];
529
-
530
- return {
531
- ...lib,
532
- agents: mappings.map(m => m.agent_type)
533
- };
534
- });
535
-
536
- console.log(JSON.stringify({ libs: result }, null, 2));
537
- return;
538
- }
539
-
540
- console.log(`\n${"=".repeat(60)}`);
541
- console.log(`RESEARCH: Status das Bibliotecas`);
542
- console.log(`${"=".repeat(60)}\n`);
543
-
544
- for (const lib of libs) {
545
- const mappings = db
546
- .query("SELECT agent_type FROM agent_lib_mappings WHERE lib_name = ?")
547
- .all(lib.lib_name) as any[];
548
-
549
- const fileExists = existsSync(lib.context_file);
550
- let status = "pendente";
551
-
552
- if (fileExists) {
553
- const content = readFileSync(lib.context_file, "utf-8");
554
- if (content.includes("status: pending-research")) {
555
- status = "pendente";
556
- } else {
557
- status = "preenchido";
558
- }
559
- }
560
-
561
- const statusIcon = status === "preenchido" ? "[x]" : "[ ]";
562
-
563
- console.log(`${statusIcon} ${lib.lib_name}@${lib.version}`);
564
- console.log(` Arquivo: ${lib.context_file}`);
565
- console.log(` Agentes: ${mappings.map(m => m.agent_type).join(", ") || "nenhum"}`);
566
- console.log(` Docs: ${lib.source_url}`);
567
- console.log("");
568
- }
569
-
570
- console.log(`${"─".repeat(60)}`);
571
- console.log(`Legenda: [x] preenchido [ ] pendente\n`);
572
- }
573
-
574
- // ============================================================
575
- // COMMAND: research fill
576
- // ============================================================
577
-
578
- export function researchFill(libName: string, options: { json?: boolean } = {}): void {
579
- initSchema();
580
- const db = getDb();
581
-
582
- const lib = db
583
- .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
584
- .get(libName) as any;
585
-
586
- if (!lib) {
587
- if (options.json) {
588
- console.log(JSON.stringify({ error: "lib_not_found", lib: libName }));
589
- } else {
590
- console.error(`\nBiblioteca '${libName}' nao encontrada.`);
591
- console.error("Execute: research show para ver bibliotecas disponiveis.\n");
592
- }
593
- process.exit(1);
594
- }
595
-
596
- const versionStr = lib.version && lib.version !== "latest" ? lib.version : "latest";
597
-
598
- // Retornar instrucoes para o agente preencher
599
- const instructions = {
600
- lib_name: lib.lib_name,
601
- version: lib.version,
602
- docs_url: lib.source_url,
603
- context_file: lib.context_file,
604
- instructions: `
605
- Voce deve pesquisar e preencher o arquivo de contexto para ${lib.lib_name}@${versionStr}.
606
-
607
- PASSOS:
608
- 1. Use WebSearch para buscar: "${lib.lib_name} ${versionStr} best practices migration guide"
609
- 2. ${lib.source_url ? `Use WebFetch para acessar a documentacao: ${lib.source_url}` : "Busque a documentacao oficial da biblioteca"}
610
- 3. Identifique:
611
- - Padroes obrigatorios para esta versao
612
- - APIs ou padroes deprecated
613
- - Melhores praticas atuais
614
- - Exemplos de codigo idiomatico
615
-
616
- 4. Edite o arquivo: ${lib.context_file}
617
- 5. Remova o comentario "status: pending-research" quando concluir
618
-
619
- FORMATO DO ARQUIVO:
620
- - Secoes claras e objetivas
621
- - Exemplos de codigo quando relevante
622
- - Referencias para documentacao
623
- `.trim()
624
- };
625
-
626
- if (options.json) {
627
- console.log(JSON.stringify(instructions, null, 2));
628
- } else {
629
- console.log(`\n${"=".repeat(60)}`);
630
- console.log(`RESEARCH: Preencher ${lib.lib_name}@${versionStr}`);
631
- console.log(`${"=".repeat(60)}`);
632
- console.log(`\n${instructions.instructions}\n`);
633
- }
634
- }
635
-
636
- // ============================================================
637
- // COMMAND: research map-agent
638
- // ============================================================
639
-
640
- export function researchMapAgent(options: {
641
- agent: string;
642
- add?: string;
643
- remove?: string;
644
- json?: boolean;
645
- }): void {
646
- initSchema();
647
- const db = getDb();
648
-
649
- if (options.add) {
650
- const libs = options.add.split(",").map(s => s.trim());
651
- for (const lib of libs) {
652
- const existingLib = db
653
- .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
654
- .get(lib);
655
-
656
- if (!existingLib) {
657
- console.error(`\nBiblioteca '${lib}' nao encontrada. Execute research start primeiro.\n`);
658
- continue;
659
- }
660
-
661
- const existing = db
662
- .query("SELECT * FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?")
663
- .get(options.agent, lib);
664
-
665
- if (!existing) {
666
- db.run(
667
- "INSERT INTO agent_lib_mappings (agent_type, lib_name) VALUES (?, ?)",
668
- [options.agent, lib]
669
- );
670
- console.log(`Adicionado: ${lib} -> ${options.agent}`);
671
- }
672
- }
673
- }
674
-
675
- if (options.remove) {
676
- const libs = options.remove.split(",").map(s => s.trim());
677
- for (const lib of libs) {
678
- db.run(
679
- "DELETE FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?",
680
- [options.agent, lib]
681
- );
682
- console.log(`Removido: ${lib} -> ${options.agent}`);
683
- }
684
- }
685
-
686
- // Mostrar mapeamentos atuais
687
- const mappings = db
688
- .query("SELECT lib_name FROM agent_lib_mappings WHERE agent_type = ? ORDER BY lib_name")
689
- .all(options.agent) as any[];
690
-
691
- if (options.json) {
692
- console.log(JSON.stringify({ agent: options.agent, libs: mappings.map(m => m.lib_name) }));
693
- } else if (!options.add && !options.remove) {
694
- console.log(`\nMapeamentos para agente '${options.agent}':`);
695
- if (mappings.length === 0) {
696
- console.log(" (nenhum)");
697
- } else {
698
- for (const m of mappings) {
699
- console.log(` - ${m.lib_name}`);
700
- }
701
- }
702
- console.log("");
703
- }
704
- }
705
-
706
- // ============================================================
707
- // COMMAND: research reset
708
- // ============================================================
709
-
710
- export function researchReset(): void {
711
- initSchema();
712
- const db = getDb();
713
-
714
- db.run("DELETE FROM agent_lib_mappings");
715
- db.run("DELETE FROM lib_contexts");
716
-
717
- console.log("\nResearch resetado. Todos os contextos de biblioteca foram removidos.");
718
- console.log("Execute: research start para redetectar.\n");
719
- }
720
-
721
- // ============================================================
722
- // INTEGRATION: getLibContextsForAgent
723
- // Used by utils.ts to inject lib context into subagent prompts.
724
- // ============================================================
725
-
726
- export function getLibContextsForAgent(agentType: string): string[] {
727
- initSchema();
728
- const db = getDb();
729
-
730
- // Buscar libs mapeadas para o agente
731
- const mappings = db
732
- .query(`
733
- SELECT lc.context_file
734
- FROM agent_lib_mappings alm
735
- JOIN lib_contexts lc ON alm.lib_name = lc.lib_name
736
- WHERE alm.agent_type = ? OR alm.agent_type = ?
737
- ORDER BY alm.priority DESC
738
- `)
739
- .all(agentType, agentType.split("-")[0]) as any[];
740
-
741
- const contexts: string[] = [];
742
-
743
- for (const mapping of mappings) {
744
- if (existsSync(mapping.context_file)) {
745
- const content = readFileSync(mapping.context_file, "utf-8");
746
- // So inclui se nao estiver pendente
747
- if (!content.includes("status: pending-research")) {
748
- contexts.push(content);
749
- }
750
- }
751
- }
752
-
753
- return contexts;
754
- }
1
+ import { getDb } from "../db/connection";
2
+ import { initSchema } from "../db/schema";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import { join } from "path";
5
+ import { getDetectors } from "../detectors/loader";
6
+
7
+ // ============================================================
8
+ // UNIVERSAL DOCS REGISTRY
9
+ // Maps technology names (as returned by detectors) to canonical name + docs URL.
10
+ // Covers all 7 ecosystems: Node, Python, Go, .NET, Rust, JVM, Flutter.
11
+ // ============================================================
12
+
13
+ const UNIVERSAL_DOCS_REGISTRY: Record<string, { canonical: string; docsUrl: string }> = {
14
+ // ── Node.js / JavaScript / TypeScript ──────────────────────
15
+ // Frontend
16
+ "Next.js": { canonical: "nextjs", docsUrl: "https://nextjs.org/docs" },
17
+ "React": { canonical: "react", docsUrl: "https://react.dev" },
18
+ "Vue": { canonical: "vue", docsUrl: "https://vuejs.org/guide" },
19
+ "Svelte": { canonical: "svelte", docsUrl: "https://svelte.dev/docs" },
20
+ "Angular": { canonical: "angular", docsUrl: "https://angular.dev/overview" },
21
+ "Nuxt": { canonical: "nuxt", docsUrl: "https://nuxt.com/docs" },
22
+ "Remix": { canonical: "remix", docsUrl: "https://remix.run/docs" },
23
+ "Gatsby": { canonical: "gatsby", docsUrl: "https://www.gatsbyjs.com/docs" },
24
+ "SolidJS": { canonical: "solidjs", docsUrl: "https://www.solidjs.com/docs" },
25
+ "Preact": { canonical: "preact", docsUrl: "https://preactjs.com/guide" },
26
+ "Astro": { canonical: "astro", docsUrl: "https://docs.astro.build" },
27
+ "Qwik": { canonical: "qwik", docsUrl: "https://qwik.dev/docs" },
28
+ // Backend
29
+ "NestJS": { canonical: "nestjs", docsUrl: "https://docs.nestjs.com" },
30
+ "Hono": { canonical: "hono", docsUrl: "https://hono.dev/docs" },
31
+ "Elysia": { canonical: "elysia", docsUrl: "https://elysiajs.com/introduction" },
32
+ "Fastify": { canonical: "fastify", docsUrl: "https://fastify.dev/docs/latest" },
33
+ "Koa": { canonical: "koa", docsUrl: "https://koajs.com" },
34
+ "Express": { canonical: "express", docsUrl: "https://expressjs.com/en/guide" },
35
+ "Hapi": { canonical: "hapi", docsUrl: "https://hapi.dev/tutorials" },
36
+ "tRPC": { canonical: "trpc", docsUrl: "https://trpc.io/docs" },
37
+ // ORM / Database
38
+ "Drizzle": { canonical: "drizzle", docsUrl: "https://orm.drizzle.team/docs" },
39
+ "Prisma": { canonical: "prisma", docsUrl: "https://www.prisma.io/docs" },
40
+ "TypeORM": { canonical: "typeorm", docsUrl: "https://typeorm.io" },
41
+ "Sequelize": { canonical: "sequelize", docsUrl: "https://sequelize.org/docs" },
42
+ "Kysely": { canonical: "kysely", docsUrl: "https://kysely.dev/docs/intro" },
43
+ "Mongoose": { canonical: "mongoose", docsUrl: "https://mongoosejs.com/docs" },
44
+ "Knex": { canonical: "knex", docsUrl: "https://knexjs.org/guide" },
45
+ "MikroORM": { canonical: "mikro-orm", docsUrl: "https://mikro-orm.io/docs" },
46
+ "Supabase": { canonical: "supabase", docsUrl: "https://supabase.com/docs" },
47
+ "Firebase": { canonical: "firebase", docsUrl: "https://firebase.google.com/docs" },
48
+ // Styling
49
+ "Tailwind CSS": { canonical: "tailwind", docsUrl: "https://tailwindcss.com/docs" },
50
+ "Styled Components": { canonical: "styled-components", docsUrl: "https://styled-components.com/docs" },
51
+ "Emotion": { canonical: "emotion", docsUrl: "https://emotion.sh/docs/introduction" },
52
+ "Sass": { canonical: "sass", docsUrl: "https://sass-lang.com/documentation" },
53
+ "Vanilla Extract": { canonical: "vanilla-extract", docsUrl: "https://vanilla-extract.style/documentation" },
54
+ // Auth
55
+ "NextAuth.js": { canonical: "next-auth", docsUrl: "https://next-auth.js.org/getting-started" },
56
+ "Auth.js": { canonical: "auth-js", docsUrl: "https://authjs.dev/getting-started" },
57
+ "Clerk": { canonical: "clerk", docsUrl: "https://clerk.com/docs" },
58
+ "Passport.js": { canonical: "passport", docsUrl: "https://www.passportjs.org/docs" },
59
+ "Lucia": { canonical: "lucia", docsUrl: "https://lucia-auth.com" },
60
+ "Better Auth": { canonical: "better-auth", docsUrl: "https://www.better-auth.com/docs" },
61
+ // Testing
62
+ "Vitest": { canonical: "vitest", docsUrl: "https://vitest.dev/guide" },
63
+ "Jest": { canonical: "jest", docsUrl: "https://jestjs.io/docs/getting-started" },
64
+ "Testing Library": { canonical: "testing-library", docsUrl: "https://testing-library.com/docs" },
65
+ "Playwright": { canonical: "playwright", docsUrl: "https://playwright.dev/docs/intro" },
66
+ "Cypress": { canonical: "cypress", docsUrl: "https://docs.cypress.io" },
67
+ "Mocha": { canonical: "mocha", docsUrl: "https://mochajs.org" },
68
+ // State / Validation
69
+ "zustand": { canonical: "zustand", docsUrl: "https://docs.pmnd.rs/zustand/getting-started/introduction" },
70
+ "TanStack Query": { canonical: "tanstack-query", docsUrl: "https://tanstack.com/query/latest/docs" },
71
+ "SWR": { canonical: "swr", docsUrl: "https://swr.vercel.app/docs/getting-started" },
72
+ "Zod": { canonical: "zod", docsUrl: "https://zod.dev" },
73
+
74
+ // ── Python ─────────────────────────────────────────────────
75
+ "Django": { canonical: "django", docsUrl: "https://docs.djangoproject.com" },
76
+ "FastAPI": { canonical: "fastapi", docsUrl: "https://fastapi.tiangolo.com" },
77
+ "Flask": { canonical: "flask", docsUrl: "https://flask.palletsprojects.com" },
78
+ "Starlette": { canonical: "starlette", docsUrl: "https://www.starlette.io" },
79
+ "Litestar": { canonical: "litestar", docsUrl: "https://docs.litestar.dev" },
80
+ "Tornado": { canonical: "tornado", docsUrl: "https://www.tornadoweb.org/en/stable" },
81
+ "Pyramid": { canonical: "pyramid", docsUrl: "https://docs.pylonsproject.org/projects/pyramid" },
82
+ "aiohttp": { canonical: "aiohttp", docsUrl: "https://docs.aiohttp.org" },
83
+ "Sanic": { canonical: "sanic", docsUrl: "https://sanic.readthedocs.io" },
84
+ "SQLAlchemy": { canonical: "sqlalchemy", docsUrl: "https://docs.sqlalchemy.org" },
85
+ "Tortoise ORM": { canonical: "tortoise-orm", docsUrl: "https://tortoise.github.io" },
86
+ "Peewee": { canonical: "peewee", docsUrl: "https://docs.peewee-orm.com" },
87
+ "SQLModel": { canonical: "sqlmodel", docsUrl: "https://sqlmodel.tiangolo.com" },
88
+ "Beanie": { canonical: "beanie", docsUrl: "https://beanie-odm.dev" },
89
+ "MongoEngine": { canonical: "mongoengine", docsUrl: "https://docs.mongoengine.org" },
90
+ "pytest": { canonical: "pytest", docsUrl: "https://docs.pytest.org" },
91
+ "Hypothesis": { canonical: "hypothesis", docsUrl: "https://hypothesis.readthedocs.io" },
92
+ "TensorFlow": { canonical: "tensorflow", docsUrl: "https://www.tensorflow.org/guide" },
93
+ "PyTorch": { canonical: "pytorch", docsUrl: "https://pytorch.org/docs/stable" },
94
+ "scikit-learn": { canonical: "scikit-learn", docsUrl: "https://scikit-learn.org/stable/user_guide" },
95
+ "LangChain": { canonical: "langchain", docsUrl: "https://python.langchain.com/docs" },
96
+ "Pandas": { canonical: "pandas", docsUrl: "https://pandas.pydata.org/docs" },
97
+ "Hugging Face Transformers": { canonical: "transformers", docsUrl: "https://huggingface.co/docs/transformers" },
98
+ "OpenAI SDK": { canonical: "openai-sdk", docsUrl: "https://platform.openai.com/docs" },
99
+
100
+ // ── Go ─────────────────────────────────────────────────────
101
+ "Gin": { canonical: "gin", docsUrl: "https://gin-gonic.com/docs" },
102
+ "Echo": { canonical: "echo", docsUrl: "https://echo.labstack.com/docs" },
103
+ "Fiber": { canonical: "fiber", docsUrl: "https://docs.gofiber.io" },
104
+ "Chi": { canonical: "chi", docsUrl: "https://go-chi.io" },
105
+ "Gorilla Mux": { canonical: "gorilla-mux", docsUrl: "https://github.com/gorilla/mux" },
106
+ "Beego": { canonical: "beego", docsUrl: "https://beego.wiki" },
107
+ "GORM": { canonical: "gorm", docsUrl: "https://gorm.io/docs" },
108
+ "Ent": { canonical: "ent", docsUrl: "https://entgo.io/docs/getting-started" },
109
+ "SQLx": { canonical: "sqlx-go", docsUrl: "https://github.com/jmoiron/sqlx" },
110
+ "SQLC": { canonical: "sqlc", docsUrl: "https://docs.sqlc.dev" },
111
+ "Testify": { canonical: "testify", docsUrl: "https://github.com/stretchr/testify" },
112
+ "Ginkgo": { canonical: "ginkgo", docsUrl: "https://onsi.github.io/ginkgo" },
113
+ "Cobra CLI": { canonical: "cobra", docsUrl: "https://cobra.dev" },
114
+ "Viper Config": { canonical: "viper", docsUrl: "https://github.com/spf13/viper" },
115
+ "Zap Logger": { canonical: "zap", docsUrl: "https://pkg.go.dev/go.uber.org/zap" },
116
+
117
+ // ── .NET / C# ──────────────────────────────────────────────
118
+ "ASP.NET Core": { canonical: "aspnet-core", docsUrl: "https://learn.microsoft.com/aspnet/core" },
119
+ "ASP.NET Core MVC": { canonical: "aspnet-mvc", docsUrl: "https://learn.microsoft.com/aspnet/core/mvc" },
120
+ "Blazor": { canonical: "blazor", docsUrl: "https://learn.microsoft.com/aspnet/core/blazor" },
121
+ "Blazor WebAssembly": { canonical: "blazor-wasm", docsUrl: "https://learn.microsoft.com/aspnet/core/blazor" },
122
+ ".NET MAUI": { canonical: "maui", docsUrl: "https://learn.microsoft.com/dotnet/maui" },
123
+ "Entity Framework": { canonical: "ef-core", docsUrl: "https://learn.microsoft.com/ef/core" },
124
+ "EF Core + PostgreSQL": { canonical: "ef-postgres", docsUrl: "https://www.npgsql.org/efcore" },
125
+ "EF Core + SQL Server": { canonical: "ef-sqlserver", docsUrl: "https://learn.microsoft.com/ef/core/providers/sql-server" },
126
+ "Dapper": { canonical: "dapper", docsUrl: "https://github.com/DapperLib/Dapper" },
127
+ "NHibernate": { canonical: "nhibernate", docsUrl: "https://nhibernate.info/doc" },
128
+ "xUnit": { canonical: "xunit", docsUrl: "https://xunit.net/docs/getting-started" },
129
+ "NUnit": { canonical: "nunit", docsUrl: "https://docs.nunit.org" },
130
+ "MSTest": { canonical: "mstest", docsUrl: "https://learn.microsoft.com/dotnet/core/testing/unit-testing-with-mstest" },
131
+ "FluentAssertions": { canonical: "fluent-assertions", docsUrl: "https://fluentassertions.com/introduction" },
132
+ "Moq": { canonical: "moq", docsUrl: "https://github.com/devlooped/moq" },
133
+ "ASP.NET Identity": { canonical: "aspnet-identity", docsUrl: "https://learn.microsoft.com/aspnet/core/security/authentication/identity" },
134
+ "Duende IdentityServer":{ canonical: "duende-ids", docsUrl: "https://docs.duendesoftware.com" },
135
+ "JWT Bearer": { canonical: "jwt-bearer-dotnet", docsUrl: "https://learn.microsoft.com/aspnet/core/security/authentication" },
136
+
137
+ // ── Rust ───────────────────────────────────────────────────
138
+ "Actix Web": { canonical: "actix-web", docsUrl: "https://actix.rs/docs" },
139
+ "Axum": { canonical: "axum", docsUrl: "https://docs.rs/axum" },
140
+ "Rocket": { canonical: "rocket", docsUrl: "https://rocket.rs/guide" },
141
+ "Warp": { canonical: "warp", docsUrl: "https://docs.rs/warp" },
142
+ "Tide": { canonical: "tide", docsUrl: "https://docs.rs/tide" },
143
+ "Poem": { canonical: "poem", docsUrl: "https://docs.rs/poem" },
144
+ "Diesel": { canonical: "diesel", docsUrl: "https://diesel.rs/guides" },
145
+ "SeaORM": { canonical: "sea-orm", docsUrl: "https://www.sea-ql.org/SeaORM/docs" },
146
+ "SQLx (Rust)": { canonical: "sqlx-rust", docsUrl: "https://docs.rs/sqlx" },
147
+ "Tokio": { canonical: "tokio", docsUrl: "https://tokio.rs/tokio/tutorial" },
148
+ "Serde": { canonical: "serde", docsUrl: "https://serde.rs" },
149
+
150
+ // ── JVM (Java / Kotlin) ────────────────────────────────────
151
+ "Spring Boot": { canonical: "spring-boot", docsUrl: "https://docs.spring.io/spring-boot/reference" },
152
+ "Spring WebFlux": { canonical: "spring-webflux", docsUrl: "https://docs.spring.io/spring-framework/reference/web/webflux.html" },
153
+ "Quarkus": { canonical: "quarkus", docsUrl: "https://quarkus.io/guides" },
154
+ "Micronaut": { canonical: "micronaut", docsUrl: "https://docs.micronaut.io" },
155
+ "Ktor": { canonical: "ktor", docsUrl: "https://ktor.io/docs" },
156
+ "Vert.x": { canonical: "vertx", docsUrl: "https://vertx.io/docs" },
157
+ "Hibernate": { canonical: "hibernate", docsUrl: "https://hibernate.org/orm/documentation" },
158
+ "Spring Data JPA": { canonical: "spring-data-jpa", docsUrl: "https://docs.spring.io/spring-data/jpa/reference" },
159
+ "MyBatis": { canonical: "mybatis", docsUrl: "https://mybatis.org/mybatis-3" },
160
+ "Exposed": { canonical: "exposed", docsUrl: "https://github.com/JetBrains/Exposed/wiki" },
161
+ "jOOQ": { canonical: "jooq", docsUrl: "https://www.jooq.org/doc" },
162
+ "JUnit": { canonical: "junit", docsUrl: "https://junit.org/junit5/docs/current/user-guide" },
163
+ "TestNG": { canonical: "testng", docsUrl: "https://testng.org/doc" },
164
+ "Kotest": { canonical: "kotest", docsUrl: "https://kotest.io/docs/framework/framework.html" },
165
+ "Mockito": { canonical: "mockito", docsUrl: "https://site.mockito.org" },
166
+ "MockK": { canonical: "mockk", docsUrl: "https://mockk.io" },
167
+ "Spring Security": { canonical: "spring-security", docsUrl: "https://docs.spring.io/spring-security/reference" },
168
+
169
+ // ── Flutter / Dart ─────────────────────────────────────────
170
+ "Flutter": { canonical: "flutter", docsUrl: "https://docs.flutter.dev" },
171
+ "Riverpod": { canonical: "riverpod", docsUrl: "https://riverpod.dev/docs" },
172
+ "Bloc": { canonical: "bloc", docsUrl: "https://bloclibrary.dev" },
173
+ "Provider": { canonical: "provider", docsUrl: "https://pub.dev/packages/provider" },
174
+ "GoRouter": { canonical: "go-router", docsUrl: "https://pub.dev/packages/go_router" },
175
+ "Dio": { canonical: "dio", docsUrl: "https://pub.dev/packages/dio" },
176
+ "Hive": { canonical: "hive", docsUrl: "https://docs.hivedb.dev" },
177
+ "Drift": { canonical: "drift", docsUrl: "https://drift.simonbinder.eu/docs" },
178
+ "Isar": { canonical: "isar", docsUrl: "https://isar.dev/docs" },
179
+ "Mocktail": { canonical: "mocktail", docsUrl: "https://pub.dev/packages/mocktail" },
180
+
181
+ // ── Databases (cross-ecosystem) ────────────────────────────
182
+ "PostgreSQL": { canonical: "postgresql", docsUrl: "https://www.postgresql.org/docs" },
183
+ "MySQL": { canonical: "mysql", docsUrl: "https://dev.mysql.com/doc" },
184
+ "MongoDB": { canonical: "mongodb", docsUrl: "https://www.mongodb.com/docs" },
185
+ "Redis": { canonical: "redis", docsUrl: "https://redis.io/docs" },
186
+ "SQLite": { canonical: "sqlite", docsUrl: "https://www.sqlite.org/docs.html" },
187
+ "Elasticsearch": { canonical: "elasticsearch", docsUrl: "https://www.elastic.co/guide/en/elasticsearch" },
188
+ "SQL Server": { canonical: "sql-server", docsUrl: "https://learn.microsoft.com/sql/sql-server" },
189
+ };
190
+
191
+ // ============================================================
192
+ // UNIVERSAL AGENT-LIB MAPPING
193
+ // Maps agent types to library categories + optional ecosystem filter.
194
+ // If ecosystems is set, only libs from those ecosystems are mapped.
195
+ // ============================================================
196
+
197
+ const UNIVERSAL_AGENT_LIB_CATEGORIES: Record<string, { categories: string[]; ecosystems?: string[] }> = {
198
+ // Generic agents (any ecosystem)
199
+ "frontend": { categories: ["frontend", "styling", "auth"] },
200
+ "backend": { categories: ["backend", "auth"] },
201
+ "database": { categories: ["database", "orm"] },
202
+ "testing": { categories: ["testing"] },
203
+
204
+ // Node.js specific
205
+ "frontend-next": { categories: ["frontend", "styling", "auth", "state", "validation"], ecosystems: ["node"] },
206
+ "frontend-react": { categories: ["frontend", "styling", "state", "validation"], ecosystems: ["node"] },
207
+ "backend-bun": { categories: ["backend", "auth", "validation"], ecosystems: ["node"] },
208
+ "backend-node": { categories: ["backend", "auth", "validation"], ecosystems: ["node"] },
209
+
210
+ // .NET
211
+ "backend-csharp": { categories: ["backend", "auth", "database", "orm"], ecosystems: ["dotnet"] },
212
+
213
+ // Go
214
+ "backend-go": { categories: ["backend", "auth", "database", "orm"], ecosystems: ["go"] },
215
+
216
+ // Flutter
217
+ "frontend-flutter": { categories: ["frontend", "auth", "database"], ecosystems: ["flutter"] },
218
+
219
+ // Ecosystem-agnostic specialists
220
+ "database-postgres": { categories: ["database", "orm"] },
221
+ "testing-unit": { categories: ["testing"] },
222
+ };
223
+
224
+ // ============================================================
225
+ // TYPES
226
+ // ============================================================
227
+
228
+ interface DetectedLib {
229
+ name: string;
230
+ version: string;
231
+ canonical: string;
232
+ docsUrl: string;
233
+ category: string;
234
+ ecosystem: string;
235
+ }
236
+
237
+ // ============================================================
238
+ // UNIVERSAL DETECTION
239
+ // ============================================================
240
+
241
+ async function detectLibsUniversal(): Promise<DetectedLib[]> {
242
+ const detectors = getDetectors();
243
+ const detected: DetectedLib[] = [];
244
+ const seen = new Set<string>();
245
+
246
+ const detectorPromises = detectors.map(async (detector) => {
247
+ try {
248
+ return await detector.detect(process.cwd());
249
+ } catch {
250
+ return null;
251
+ }
252
+ });
253
+
254
+ const results = await Promise.all(detectorPromises);
255
+
256
+ for (const result of results) {
257
+ if (!result || result.technologies.length === 0) continue;
258
+
259
+ for (const tech of result.technologies) {
260
+ // Skip runtime/build categories — those are languages/tools, not libraries
261
+ if (tech.category === "runtime" || tech.category === "build") continue;
262
+
263
+ const registry = UNIVERSAL_DOCS_REGISTRY[tech.name];
264
+ const canonical = registry?.canonical || tech.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
265
+ const docsUrl = registry?.docsUrl || "";
266
+
267
+ // Deduplicate by canonical name
268
+ if (seen.has(canonical)) continue;
269
+ seen.add(canonical);
270
+
271
+ detected.push({
272
+ name: tech.name,
273
+ version: (tech.version || "").replace(/[\^~]/g, ""),
274
+ canonical,
275
+ docsUrl,
276
+ category: tech.category,
277
+ ecosystem: result.ecosystem,
278
+ });
279
+ }
280
+ }
281
+
282
+ return detected;
283
+ }
284
+
285
+ // ============================================================
286
+ // HELPERS
287
+ // ============================================================
288
+
289
+ function ensureLibContextDir(): string {
290
+ const dir = join(process.cwd(), ".codexa", "lib-context");
291
+ if (!existsSync(dir)) {
292
+ mkdirSync(dir, { recursive: true });
293
+ }
294
+ return dir;
295
+ }
296
+
297
+ function generateLibContextTemplate(lib: DetectedLib): string {
298
+ const now = new Date().toISOString().split("T")[0];
299
+ const versionDisplay = lib.version || "latest";
300
+ const docsLine = lib.docsUrl ? `\nOu edite manualmente com base na documentacao:\n${lib.docsUrl}` : "";
301
+
302
+ return `# ${lib.canonical} Context
303
+ <!-- version: ${versionDisplay} -->
304
+ <!-- updated: ${now} -->
305
+ <!-- source: ${lib.docsUrl} -->
306
+ <!-- ecosystem: ${lib.ecosystem} -->
307
+ <!-- status: pending-research -->
308
+
309
+ ## IMPORTANTE
310
+
311
+ Este arquivo foi criado automaticamente e precisa ser preenchido com:
312
+ 1. Padroes obrigatorios para esta versao
313
+ 2. APIs deprecated a evitar
314
+ 3. Melhores praticas atuais
315
+
316
+ ## Como Preencher
317
+
318
+ Use o comando:
319
+ \`\`\`bash
320
+ codexa research fill --lib ${lib.canonical}
321
+ \`\`\`
322
+ ${docsLine}
323
+
324
+ ---
325
+
326
+ ## Padroes Obrigatorios (v${versionDisplay})
327
+
328
+ <!-- Adicione padroes aqui -->
329
+
330
+ ## APIs/Padroes Deprecated
331
+
332
+ <!-- Liste o que evitar -->
333
+
334
+ ## Melhores Praticas
335
+
336
+ <!-- Liste melhores praticas -->
337
+
338
+ ## Exemplos
339
+
340
+ <!-- Adicione exemplos de codigo -->
341
+ `;
342
+ }
343
+
344
+ // ============================================================
345
+ // COMMAND: research start
346
+ // ============================================================
347
+
348
+ export async function researchStart(options: { json?: boolean } = {}): Promise<void> {
349
+ initSchema();
350
+ const db = getDb();
351
+
352
+ const libs = await detectLibsUniversal();
353
+
354
+ if (libs.length === 0) {
355
+ if (options.json) {
356
+ console.log(JSON.stringify({ error: "no_ecosystem_detected", libs: [] }));
357
+ } else {
358
+ console.error("\nNenhum ecossistema ou biblioteca detectada.");
359
+ console.error("Certifique-se de estar na raiz do projeto.");
360
+ console.error("Ecossistemas suportados: Node.js, Python, Go, .NET, Rust, Java/Kotlin, Flutter\n");
361
+ }
362
+ process.exit(1);
363
+ }
364
+
365
+ const libContextDir = ensureLibContextDir();
366
+ const created: string[] = [];
367
+ const existing: string[] = [];
368
+
369
+ for (const lib of libs) {
370
+ const filePath = join(libContextDir, `${lib.canonical}.md`);
371
+
372
+ // Registrar no banco
373
+ const existingRecord = db
374
+ .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
375
+ .get(lib.canonical) as any;
376
+
377
+ if (existingRecord) {
378
+ // Atualizar versao se mudou
379
+ if (existingRecord.version !== lib.version && lib.version) {
380
+ db.run(
381
+ "UPDATE lib_contexts SET version = ?, updated_at = ? WHERE lib_name = ?",
382
+ [lib.version, new Date().toISOString(), lib.canonical]
383
+ );
384
+ }
385
+ existing.push(lib.canonical);
386
+ } else {
387
+ // Criar novo registro
388
+ db.run(
389
+ `INSERT INTO lib_contexts (lib_name, version, context_file, source_url, researched_at)
390
+ VALUES (?, ?, ?, ?, ?)`,
391
+ [lib.canonical, lib.version || "latest", filePath, lib.docsUrl, new Date().toISOString()]
392
+ );
393
+
394
+ // Criar arquivo se nao existe
395
+ if (!existsSync(filePath)) {
396
+ writeFileSync(filePath, generateLibContextTemplate(lib));
397
+ }
398
+
399
+ created.push(lib.canonical);
400
+ }
401
+
402
+ // Registrar mapeamento de agente (com filtro de ecosystem)
403
+ const agentTypes = Object.entries(UNIVERSAL_AGENT_LIB_CATEGORIES)
404
+ .filter(([_, config]) => {
405
+ const categoryMatch = config.categories.includes(lib.category);
406
+ const ecosystemMatch = !config.ecosystems || config.ecosystems.includes(lib.ecosystem);
407
+ return categoryMatch && ecosystemMatch;
408
+ })
409
+ .map(([agent]) => agent);
410
+
411
+ for (const agentType of agentTypes) {
412
+ const existingMapping = db
413
+ .query("SELECT * FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?")
414
+ .get(agentType, lib.canonical);
415
+
416
+ if (!existingMapping) {
417
+ db.run(
418
+ "INSERT INTO agent_lib_mappings (agent_type, lib_name, priority) VALUES (?, ?, ?)",
419
+ [agentType, lib.canonical, 0]
420
+ );
421
+ }
422
+ }
423
+ }
424
+
425
+ if (options.json) {
426
+ // Coletar ecossistemas detectados
427
+ const ecosystems = [...new Set(libs.map(l => l.ecosystem))];
428
+ console.log(JSON.stringify({
429
+ detected: libs.length,
430
+ created: created.length,
431
+ existing: existing.length,
432
+ ecosystems,
433
+ libs: libs.map(l => ({
434
+ name: l.canonical,
435
+ version: l.version || "latest",
436
+ category: l.category,
437
+ ecosystem: l.ecosystem,
438
+ status: created.includes(l.canonical) ? "created" : "existing"
439
+ }))
440
+ }, null, 2));
441
+ return;
442
+ }
443
+
444
+ // Coletar ecossistemas
445
+ const ecosystems = [...new Set(libs.map(l => l.ecosystem))];
446
+
447
+ console.log(`\n${"=".repeat(60)}`);
448
+ console.log(`RESEARCH: Bibliotecas Detectadas`);
449
+ console.log(`${"=".repeat(60)}`);
450
+ console.log(`\nEcossistemas: ${ecosystems.join(", ")}`);
451
+ console.log(`Encontradas ${libs.length} bibliotecas:\n`);
452
+
453
+ // Agrupar por ecossistema > categoria
454
+ const byEcosystem: Record<string, Record<string, DetectedLib[]>> = {};
455
+ for (const lib of libs) {
456
+ if (!byEcosystem[lib.ecosystem]) {
457
+ byEcosystem[lib.ecosystem] = {};
458
+ }
459
+ if (!byEcosystem[lib.ecosystem][lib.category]) {
460
+ byEcosystem[lib.ecosystem][lib.category] = [];
461
+ }
462
+ byEcosystem[lib.ecosystem][lib.category].push(lib);
463
+ }
464
+
465
+ for (const [ecosystem, categories] of Object.entries(byEcosystem)) {
466
+ console.log(` [${ecosystem.toUpperCase()}]`);
467
+ for (const [category, categoryLibs] of Object.entries(categories)) {
468
+ console.log(` ${category}:`);
469
+ for (const lib of categoryLibs) {
470
+ const status = created.includes(lib.canonical) ? "[NOVO]" : "[OK]";
471
+ const version = lib.version ? `@${lib.version}` : "";
472
+ console.log(` ${status} ${lib.canonical}${version}`);
473
+ }
474
+ }
475
+ console.log("");
476
+ }
477
+
478
+ console.log(`${"─".repeat(60)}`);
479
+ console.log(`Arquivos de contexto em: .codexa/lib-context/`);
480
+
481
+ if (created.length > 0) {
482
+ console.log(`\nCriados ${created.length} novos arquivos de contexto.`);
483
+ console.log(`\nProximos passos:`);
484
+ console.log(`1. Preencha os arquivos com padroes atualizados`);
485
+ console.log(`2. Use: research fill --lib <nome> para assistencia`);
486
+ console.log(`3. Ou edite manualmente baseado na documentacao\n`);
487
+ } else {
488
+ console.log(`\nTodos os arquivos de contexto ja existem.`);
489
+ console.log(`Use: research show para ver o status\n`);
490
+ }
491
+ }
492
+
493
+ // ============================================================
494
+ // COMMAND: research show
495
+ // ============================================================
496
+
497
+ export function researchShow(options: { json?: boolean; lib?: string } = {}): void {
498
+ initSchema();
499
+ const db = getDb();
500
+
501
+ let query = "SELECT * FROM lib_contexts";
502
+ let params: any[] = [];
503
+
504
+ if (options.lib) {
505
+ query += " WHERE lib_name = ?";
506
+ params.push(options.lib);
507
+ }
508
+
509
+ query += " ORDER BY lib_name";
510
+
511
+ const libs = db.query(query).all(...params) as any[];
512
+
513
+ if (libs.length === 0) {
514
+ if (options.json) {
515
+ console.log(JSON.stringify({ libs: [] }));
516
+ } else {
517
+ console.error("\nNenhuma biblioteca registrada.");
518
+ console.error("Execute: research start\n");
519
+ }
520
+ process.exit(1);
521
+ }
522
+
523
+ if (options.json) {
524
+ // Adicionar mapeamentos de agentes
525
+ const result = libs.map(lib => {
526
+ const mappings = db
527
+ .query("SELECT agent_type FROM agent_lib_mappings WHERE lib_name = ?")
528
+ .all(lib.lib_name) as any[];
529
+
530
+ return {
531
+ ...lib,
532
+ agents: mappings.map(m => m.agent_type)
533
+ };
534
+ });
535
+
536
+ console.log(JSON.stringify({ libs: result }, null, 2));
537
+ return;
538
+ }
539
+
540
+ console.log(`\n${"=".repeat(60)}`);
541
+ console.log(`RESEARCH: Status das Bibliotecas`);
542
+ console.log(`${"=".repeat(60)}\n`);
543
+
544
+ for (const lib of libs) {
545
+ const mappings = db
546
+ .query("SELECT agent_type FROM agent_lib_mappings WHERE lib_name = ?")
547
+ .all(lib.lib_name) as any[];
548
+
549
+ const fileExists = existsSync(lib.context_file);
550
+ let status = "pendente";
551
+
552
+ if (fileExists) {
553
+ const content = readFileSync(lib.context_file, "utf-8");
554
+ if (content.includes("status: pending-research")) {
555
+ status = "pendente";
556
+ } else {
557
+ status = "preenchido";
558
+ }
559
+ }
560
+
561
+ const statusIcon = status === "preenchido" ? "[x]" : "[ ]";
562
+
563
+ console.log(`${statusIcon} ${lib.lib_name}@${lib.version}`);
564
+ console.log(` Arquivo: ${lib.context_file}`);
565
+ console.log(` Agentes: ${mappings.map(m => m.agent_type).join(", ") || "nenhum"}`);
566
+ console.log(` Docs: ${lib.source_url}`);
567
+ console.log("");
568
+ }
569
+
570
+ console.log(`${"─".repeat(60)}`);
571
+ console.log(`Legenda: [x] preenchido [ ] pendente\n`);
572
+ }
573
+
574
+ // ============================================================
575
+ // COMMAND: research fill
576
+ // ============================================================
577
+
578
+ export function researchFill(libName: string, options: { json?: boolean } = {}): void {
579
+ initSchema();
580
+ const db = getDb();
581
+
582
+ const lib = db
583
+ .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
584
+ .get(libName) as any;
585
+
586
+ if (!lib) {
587
+ if (options.json) {
588
+ console.log(JSON.stringify({ error: "lib_not_found", lib: libName }));
589
+ } else {
590
+ console.error(`\nBiblioteca '${libName}' nao encontrada.`);
591
+ console.error("Execute: research show para ver bibliotecas disponiveis.\n");
592
+ }
593
+ process.exit(1);
594
+ }
595
+
596
+ const versionStr = lib.version && lib.version !== "latest" ? lib.version : "latest";
597
+
598
+ // Retornar instrucoes para o agente preencher
599
+ const instructions = {
600
+ lib_name: lib.lib_name,
601
+ version: lib.version,
602
+ docs_url: lib.source_url,
603
+ context_file: lib.context_file,
604
+ instructions: `
605
+ Voce deve pesquisar e preencher o arquivo de contexto para ${lib.lib_name}@${versionStr}.
606
+
607
+ PASSOS:
608
+ 1. Use WebSearch para buscar: "${lib.lib_name} ${versionStr} best practices migration guide"
609
+ 2. ${lib.source_url ? `Use WebFetch para acessar a documentacao: ${lib.source_url}` : "Busque a documentacao oficial da biblioteca"}
610
+ 3. Identifique:
611
+ - Padroes obrigatorios para esta versao
612
+ - APIs ou padroes deprecated
613
+ - Melhores praticas atuais
614
+ - Exemplos de codigo idiomatico
615
+
616
+ 4. Edite o arquivo: ${lib.context_file}
617
+ 5. Remova o comentario "status: pending-research" quando concluir
618
+
619
+ FORMATO DO ARQUIVO:
620
+ - Secoes claras e objetivas
621
+ - Exemplos de codigo quando relevante
622
+ - Referencias para documentacao
623
+ `.trim()
624
+ };
625
+
626
+ if (options.json) {
627
+ console.log(JSON.stringify(instructions, null, 2));
628
+ } else {
629
+ console.log(`\n${"=".repeat(60)}`);
630
+ console.log(`RESEARCH: Preencher ${lib.lib_name}@${versionStr}`);
631
+ console.log(`${"=".repeat(60)}`);
632
+ console.log(`\n${instructions.instructions}\n`);
633
+ }
634
+ }
635
+
636
+ // ============================================================
637
+ // COMMAND: research map-agent
638
+ // ============================================================
639
+
640
+ export function researchMapAgent(options: {
641
+ agent: string;
642
+ add?: string;
643
+ remove?: string;
644
+ json?: boolean;
645
+ }): void {
646
+ initSchema();
647
+ const db = getDb();
648
+
649
+ if (options.add) {
650
+ const libs = options.add.split(",").map(s => s.trim());
651
+ for (const lib of libs) {
652
+ const existingLib = db
653
+ .query("SELECT * FROM lib_contexts WHERE lib_name = ?")
654
+ .get(lib);
655
+
656
+ if (!existingLib) {
657
+ console.error(`\nBiblioteca '${lib}' nao encontrada. Execute research start primeiro.\n`);
658
+ continue;
659
+ }
660
+
661
+ const existing = db
662
+ .query("SELECT * FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?")
663
+ .get(options.agent, lib);
664
+
665
+ if (!existing) {
666
+ db.run(
667
+ "INSERT INTO agent_lib_mappings (agent_type, lib_name) VALUES (?, ?)",
668
+ [options.agent, lib]
669
+ );
670
+ console.log(`Adicionado: ${lib} -> ${options.agent}`);
671
+ }
672
+ }
673
+ }
674
+
675
+ if (options.remove) {
676
+ const libs = options.remove.split(",").map(s => s.trim());
677
+ for (const lib of libs) {
678
+ db.run(
679
+ "DELETE FROM agent_lib_mappings WHERE agent_type = ? AND lib_name = ?",
680
+ [options.agent, lib]
681
+ );
682
+ console.log(`Removido: ${lib} -> ${options.agent}`);
683
+ }
684
+ }
685
+
686
+ // Mostrar mapeamentos atuais
687
+ const mappings = db
688
+ .query("SELECT lib_name FROM agent_lib_mappings WHERE agent_type = ? ORDER BY lib_name")
689
+ .all(options.agent) as any[];
690
+
691
+ if (options.json) {
692
+ console.log(JSON.stringify({ agent: options.agent, libs: mappings.map(m => m.lib_name) }));
693
+ } else if (!options.add && !options.remove) {
694
+ console.log(`\nMapeamentos para agente '${options.agent}':`);
695
+ if (mappings.length === 0) {
696
+ console.log(" (nenhum)");
697
+ } else {
698
+ for (const m of mappings) {
699
+ console.log(` - ${m.lib_name}`);
700
+ }
701
+ }
702
+ console.log("");
703
+ }
704
+ }
705
+
706
+ // ============================================================
707
+ // COMMAND: research reset
708
+ // ============================================================
709
+
710
+ export function researchReset(): void {
711
+ initSchema();
712
+ const db = getDb();
713
+
714
+ db.run("DELETE FROM agent_lib_mappings");
715
+ db.run("DELETE FROM lib_contexts");
716
+
717
+ console.log("\nResearch resetado. Todos os contextos de biblioteca foram removidos.");
718
+ console.log("Execute: research start para redetectar.\n");
719
+ }
720
+
721
+ // ============================================================
722
+ // INTEGRATION: getLibContextsForAgent
723
+ // Used by utils.ts to inject lib context into subagent prompts.
724
+ // ============================================================
725
+
726
+ export function getLibContextsForAgent(agentType: string): string[] {
727
+ initSchema();
728
+ const db = getDb();
729
+
730
+ // Buscar libs mapeadas para o agente
731
+ const mappings = db
732
+ .query(`
733
+ SELECT lc.context_file
734
+ FROM agent_lib_mappings alm
735
+ JOIN lib_contexts lc ON alm.lib_name = lc.lib_name
736
+ WHERE alm.agent_type = ? OR alm.agent_type = ?
737
+ ORDER BY alm.priority DESC
738
+ `)
739
+ .all(agentType, agentType.split("-")[0]) as any[];
740
+
741
+ const contexts: string[] = [];
742
+
743
+ for (const mapping of mappings) {
744
+ if (existsSync(mapping.context_file)) {
745
+ const content = readFileSync(mapping.context_file, "utf-8");
746
+ // So inclui se nao estiver pendente
747
+ if (!content.includes("status: pending-research")) {
748
+ contexts.push(content);
749
+ }
750
+ }
751
+ }
752
+
753
+ return contexts;
754
+ }