@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.
- package/commands/architect.ts +760 -896
- package/commands/check.ts +131 -131
- package/commands/clear.ts +170 -174
- package/commands/decide.ts +249 -249
- package/commands/discover.ts +82 -10
- package/commands/knowledge.ts +361 -361
- package/commands/patterns.ts +621 -621
- package/commands/plan.ts +376 -376
- package/commands/product.ts +626 -628
- package/commands/research.ts +754 -754
- package/commands/review.ts +463 -463
- package/commands/standards.ts +200 -223
- package/commands/task.ts +2 -2
- package/commands/utils.ts +1021 -1021
- package/db/connection.ts +32 -32
- package/db/schema.ts +719 -788
- package/detectors/loader.ts +0 -12
- package/gates/standards-validator.ts +204 -204
- package/gates/validator.ts +441 -441
- package/package.json +43 -43
- package/protocol/process-return.ts +450 -450
- package/protocol/subagent-protocol.ts +401 -411
- package/workflow.ts +0 -18
package/commands/research.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|