agent-rules-init 0.2.1 → 0.4.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/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/cli.d.ts +38 -1
- package/dist/cli.js +203 -40
- package/dist/core/canonical-commands.d.ts +4 -0
- package/dist/core/canonical-commands.js +169 -0
- package/dist/core/config.d.ts +25 -0
- package/dist/core/config.js +125 -0
- package/dist/core/i18n.d.ts +47 -0
- package/dist/core/i18n.js +207 -0
- package/dist/core/llm-bridge.d.ts +5 -1
- package/dist/core/llm-bridge.js +56 -4
- package/dist/core/project-unit-output.d.ts +13 -0
- package/dist/core/project-unit-output.js +25 -0
- package/dist/core/project-units.d.ts +16 -0
- package/dist/core/project-units.js +95 -0
- package/dist/core/prompt-engine.d.ts +3 -1
- package/dist/core/prompt-engine.js +19 -14
- package/dist/core/repo-facts.d.ts +30 -0
- package/dist/core/repo-facts.js +420 -0
- package/dist/core/scanner.js +153 -27
- package/dist/core/templates.d.ts +6 -4
- package/dist/core/templates.js +127 -29
- package/dist/core/types.d.ts +84 -2
- package/dist/core/writer.d.ts +1 -1
- package/dist/core/writer.js +6 -4
- package/dist/packs/cpp.js +33 -27
- package/dist/packs/csharp.js +32 -27
- package/dist/packs/dart.js +30 -31
- package/dist/packs/elixir.js +31 -27
- package/dist/packs/go.js +31 -27
- package/dist/packs/java.js +82 -30
- package/dist/packs/js-ts.js +150 -37
- package/dist/packs/kotlin.js +56 -31
- package/dist/packs/php.js +30 -27
- package/dist/packs/python.js +107 -34
- package/dist/packs/r.js +30 -27
- package/dist/packs/ruby.js +36 -27
- package/dist/packs/rust.js +33 -27
- package/dist/packs/scala.js +32 -27
- package/dist/packs/swift.js +31 -27
- package/package.json +7 -4
package/dist/packs/js-ts.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, } from "../core/i18n.js";
|
|
2
|
+
import { canonicalOf } from "../core/canonical-commands.js";
|
|
1
3
|
const FRAMEWORKS = {
|
|
2
4
|
next: "next",
|
|
3
5
|
"@nestjs/core": "nestjs",
|
|
@@ -25,77 +27,188 @@ function detectFromDeps(deps, table) {
|
|
|
25
27
|
}
|
|
26
28
|
return undefined;
|
|
27
29
|
}
|
|
30
|
+
function detectPackageManagerFromCi(signals) {
|
|
31
|
+
const managers = new Set();
|
|
32
|
+
for (const workflow of signals.githubWorkflows ?? []) {
|
|
33
|
+
for (const rawLine of workflow.content.split(/\r?\n/)) {
|
|
34
|
+
const command = rawLine.trim().replace(/^(?:-\s*)?run:\s*(?:[|>]\s*)?/, "").trim();
|
|
35
|
+
const manager = /^(npm|pnpm|yarn|bun)\b/.exec(command)?.[1];
|
|
36
|
+
if (manager)
|
|
37
|
+
managers.add(manager);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return managers.size === 1 ? { value: [...managers][0], confidence: "high" } : undefined;
|
|
41
|
+
}
|
|
28
42
|
function detect(signals) {
|
|
29
43
|
if (!signals.packageJson)
|
|
30
44
|
return null;
|
|
31
45
|
const allDeps = { ...signals.packageJson.dependencies, ...signals.packageJson.devDependencies };
|
|
46
|
+
const hasAnyFileNamed = (name) => signals.files.some((file) => file.split(/[\\/]/).pop() === name);
|
|
47
|
+
const frameworkSource = signals.packageJson.name
|
|
48
|
+
? FRAMEWORKS[signals.packageJson.name]
|
|
49
|
+
: undefined;
|
|
32
50
|
const framework = detectFromDeps(allDeps, FRAMEWORKS) ?? {
|
|
33
51
|
value: "none",
|
|
34
|
-
confidence: "low",
|
|
52
|
+
confidence: frameworkSource ? "high" : "low",
|
|
35
53
|
};
|
|
36
54
|
const testRunner = detectFromDeps(allDeps, TEST_RUNNERS) ?? {
|
|
37
55
|
value: "unknown",
|
|
38
56
|
confidence: "low",
|
|
39
57
|
};
|
|
40
58
|
const linter = detectFromDeps(allDeps, LINTERS) ?? { value: "unknown", confidence: "low" };
|
|
41
|
-
const packageManager = signals.
|
|
42
|
-
? { value:
|
|
43
|
-
: signals.hasFile("
|
|
44
|
-
? { value: "
|
|
45
|
-
: signals.hasFile("
|
|
46
|
-
? { value: "
|
|
47
|
-
:
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
const packageManager = signals.packageJson.packageManager
|
|
60
|
+
? { value: signals.packageJson.packageManager, confidence: "high" }
|
|
61
|
+
: signals.hasFile("bun.lock") || signals.hasFile("bun.lockb")
|
|
62
|
+
? { value: "bun", confidence: "high" }
|
|
63
|
+
: signals.hasFile("pnpm-lock.yaml")
|
|
64
|
+
? { value: "pnpm", confidence: "high" }
|
|
65
|
+
: signals.hasFile("yarn.lock")
|
|
66
|
+
? { value: "yarn", confidence: "high" }
|
|
67
|
+
: signals.hasFile("package-lock.json") || signals.hasFile("npm-shrinkwrap.json")
|
|
68
|
+
? { value: "npm", confidence: "high" }
|
|
69
|
+
: hasAnyFileNamed("bun.lock") || hasAnyFileNamed("bun.lockb")
|
|
70
|
+
? { value: "bun", confidence: "high" }
|
|
71
|
+
: hasAnyFileNamed("pnpm-lock.yaml")
|
|
72
|
+
? { value: "pnpm", confidence: "high" }
|
|
73
|
+
: hasAnyFileNamed("yarn.lock")
|
|
74
|
+
? { value: "yarn", confidence: "high" }
|
|
75
|
+
: hasAnyFileNamed("package-lock.json") || hasAnyFileNamed("npm-shrinkwrap.json")
|
|
76
|
+
? { value: "npm", confidence: "high" }
|
|
77
|
+
: detectPackageManagerFromCi(signals) ?? { value: "npm", confidence: "low" };
|
|
78
|
+
const usesTypeScript = Boolean(allDeps.typescript) || signals.hasFile("tsconfig.json") || hasAnyFileNamed("tsconfig.json");
|
|
79
|
+
const moduleTypes = new Set((signals.packageJsons?.length ? signals.packageJsons : [signals.packageJson]).map((p) => p.moduleType));
|
|
80
|
+
// Mixed ESM/CommonJS workspaces should not receive a repo-wide rule that is wrong
|
|
81
|
+
// for half of the packages.
|
|
82
|
+
const moduleFormat = moduleTypes.size === 1 ? [...moduleTypes][0] : undefined;
|
|
50
83
|
return {
|
|
51
84
|
packId: "js-ts",
|
|
52
85
|
language: usesTypeScript ? "TypeScript" : "JavaScript",
|
|
53
86
|
framework,
|
|
54
87
|
testRunner,
|
|
55
88
|
linter,
|
|
89
|
+
frameworkSource,
|
|
56
90
|
packageManager,
|
|
57
91
|
usesTypeScript,
|
|
58
92
|
moduleFormat,
|
|
59
93
|
};
|
|
60
94
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
const TEXTS = {
|
|
96
|
+
es: {
|
|
97
|
+
tsStrict: "Usa TypeScript estricto; evita `any` salvo justificación explícita.",
|
|
98
|
+
esModules: "Sigue el estilo de módulos ES existente (import/export), no mezcles con require().",
|
|
99
|
+
commonJs: "Sigue el estilo CommonJS existente (require()/module.exports), no mezcles con import/export.",
|
|
100
|
+
arch: [
|
|
101
|
+
"Mantén los componentes/módulos pequeños y con una responsabilidad clara.",
|
|
102
|
+
"Coloca los tests junto al código que prueban o en un directorio `test/` espejo, según lo que ya use el repo.",
|
|
103
|
+
],
|
|
104
|
+
reviewFocusTs: "errores de tipado, condiciones de carrera en async/await",
|
|
105
|
+
reviewFocusJs: "condiciones de carrera en async/await",
|
|
106
|
+
refactorExtra: "Respeta los tipos existentes.",
|
|
107
|
+
},
|
|
108
|
+
en: {
|
|
109
|
+
tsStrict: "Use strict TypeScript; avoid `any` unless explicitly justified.",
|
|
110
|
+
esModules: "Follow the existing ES modules style (import/export); do not mix in require().",
|
|
111
|
+
commonJs: "Follow the existing CommonJS style (require()/module.exports); do not mix in import/export.",
|
|
112
|
+
arch: [
|
|
113
|
+
"Keep components/modules small and single-purpose.",
|
|
114
|
+
"Place tests next to the code they cover or in a mirrored `test/` directory, following what the repo already uses.",
|
|
115
|
+
],
|
|
116
|
+
reviewFocusTs: "typing errors, async/await race conditions",
|
|
117
|
+
reviewFocusJs: "async/await race conditions",
|
|
118
|
+
refactorExtra: "Respect the existing types.",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const FRAMEWORK_RISKS = {
|
|
122
|
+
express: {
|
|
123
|
+
es: "Presta especial atención al flujo de middleware, la propagación de errores con `next(err)` y el ciclo de vida de la respuesta.",
|
|
124
|
+
en: "Pay special attention to middleware control flow, error propagation through `next(err)` and response lifecycle handling.",
|
|
125
|
+
},
|
|
126
|
+
react: {
|
|
127
|
+
es: "Presta especial atención a las dependencias de hooks (`useEffect`), renders innecesarios y estado derivado.",
|
|
128
|
+
en: "Pay special attention to hook dependencies (`useEffect`), unnecessary re-renders and derived state.",
|
|
129
|
+
},
|
|
130
|
+
next: {
|
|
131
|
+
es: "Presta especial atención a la frontera servidor/cliente (`use client`), el data fetching y el caché de rutas.",
|
|
132
|
+
en: "Pay special attention to the server/client boundary (`use client`), data fetching and route caching.",
|
|
133
|
+
},
|
|
134
|
+
nestjs: {
|
|
135
|
+
es: "Presta especial atención a los scopes de providers, la inyección de dependencias y los pipes de validación.",
|
|
136
|
+
en: "Pay special attention to provider scopes, dependency injection and validation pipes.",
|
|
137
|
+
},
|
|
138
|
+
fastify: {
|
|
139
|
+
es: "Presta especial atención al ciclo de vida de plugins, la encapsulación y los schemas de validación.",
|
|
140
|
+
en: "Pay special attention to plugin lifecycle, encapsulation and validation schemas.",
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
function rules(detection, lang, ctx) {
|
|
144
|
+
const t = TEXTS[lang];
|
|
145
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
146
|
+
const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
|
|
147
|
+
const testCmd = canonicalOf(ctx, "test", "js-ts")?.command ?? runner;
|
|
64
148
|
const conventions = [];
|
|
65
|
-
if (detection.usesTypeScript)
|
|
66
|
-
conventions.push(
|
|
149
|
+
if (detection.usesTypeScript)
|
|
150
|
+
conventions.push(t.tsStrict);
|
|
151
|
+
conventions.push(runTestsConvention(lang, testCmd));
|
|
152
|
+
if (detection.moduleFormat) {
|
|
153
|
+
conventions.push(detection.moduleFormat === "module" ? t.esModules : t.commonJs);
|
|
67
154
|
}
|
|
68
|
-
conventions.push(`Ejecuta los tests con ${testRunner === "unknown" ? "el test runner del proyecto" : testRunner} antes de dar por terminada una tarea.`);
|
|
69
|
-
conventions.push(detection.moduleFormat === "module"
|
|
70
|
-
? "Sigue el estilo de módulos ES existente (import/export), no mezcles con require()."
|
|
71
|
-
: "Sigue el estilo CommonJS existente (require()/module.exports), no mezcles con import/export.");
|
|
72
155
|
return {
|
|
73
|
-
summary:
|
|
156
|
+
summary: detection.frameworkSource
|
|
157
|
+
? lang === "es"
|
|
158
|
+
? `Repositorio ${detection.usesTypeScript ? "TypeScript" : "JavaScript"} que implementa ${detection.frameworkSource}.`
|
|
159
|
+
: `${detection.usesTypeScript ? "TypeScript" : "JavaScript"} repository implementing ${detection.frameworkSource}.`
|
|
160
|
+
: summarySentence(lang, detection.usesTypeScript ? "TypeScript" : "JavaScript", framework),
|
|
74
161
|
conventions,
|
|
75
|
-
architectureNotes:
|
|
76
|
-
"Mantén los componentes/módulos pequeños y con una responsabilidad clara.",
|
|
77
|
-
"Coloca los tests junto al código que prueban o en un directorio `test/` espejo, según lo que ya use el repo.",
|
|
78
|
-
],
|
|
162
|
+
architectureNotes: t.arch,
|
|
79
163
|
};
|
|
80
164
|
}
|
|
81
|
-
function promptTemplates(detection) {
|
|
82
|
-
const
|
|
165
|
+
function promptTemplates(detection, lang, ctx) {
|
|
166
|
+
const t = TEXTS[lang];
|
|
167
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
168
|
+
const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
|
|
169
|
+
const focus = detection.usesTypeScript ? t.reviewFocusTs : t.reviewFocusJs;
|
|
170
|
+
const test = canonicalOf(ctx, "test", "js-ts");
|
|
171
|
+
const lint = canonicalOf(ctx, "lint", "js-ts");
|
|
172
|
+
const testDirs = ctx?.facts.testDirs ?? [];
|
|
173
|
+
const es = lang === "es";
|
|
174
|
+
const reviewParts = [];
|
|
175
|
+
const moduleLabel = detection.moduleFormat === "commonjs" ? "CommonJS" : detection.moduleFormat === "module" ? "ESM" : undefined;
|
|
176
|
+
reviewParts.push(es
|
|
177
|
+
? `Revisa el diff actual contra las convenciones${moduleLabel ? ` ${moduleLabel}` : ""} de este repositorio${framework ? ` (${framework})` : ""}.`
|
|
178
|
+
: `Review the current diff against this repository's${moduleLabel ? ` ${moduleLabel}` : ""} conventions${framework ? ` (${framework})` : ""}.`);
|
|
179
|
+
if (test || lint) {
|
|
180
|
+
const commands = [test, lint].filter((c) => c !== undefined);
|
|
181
|
+
const list = commands.map((c) => `\`${c.command}\``).join(es ? " y " : " and ");
|
|
182
|
+
reviewParts.push(es ? `Ejecuta ${list} antes de dar por buena la revisión.` : `Run ${list} before approving the review.`);
|
|
183
|
+
}
|
|
184
|
+
const risk = FRAMEWORK_RISKS[framework ?? detection.frameworkSource ?? ""]?.[lang];
|
|
185
|
+
if (risk)
|
|
186
|
+
reviewParts.push(risk);
|
|
187
|
+
reviewParts.push(es ? `Busca también bugs: ${focus}.` : `Also look for bugs: ${focus}.`);
|
|
188
|
+
if (testDirs.length > 0) {
|
|
189
|
+
const dirs = testDirs.map((d) => `\`${d}\``).join(", ");
|
|
190
|
+
reviewParts.push(es ? `Los tests viven en ${dirs}.` : `Tests live under ${dirs}.`);
|
|
191
|
+
}
|
|
192
|
+
reviewParts.push(es
|
|
193
|
+
? "Señala solo hallazgos concretos con archivo y línea."
|
|
194
|
+
: "Report only concrete findings with file and line references.");
|
|
195
|
+
const testingParts = [];
|
|
196
|
+
testingParts.push(testingBody(lang, runner));
|
|
197
|
+
if (test) {
|
|
198
|
+
testingParts.push(es ? `Verifica la suite con \`${test.command}\` antes de terminar.` : `Verify the suite with \`${test.command}\` before finishing.`);
|
|
199
|
+
}
|
|
200
|
+
if (testDirs.length > 0) {
|
|
201
|
+
const dirs = testDirs.map((d) => `\`${d}\``).join(", ");
|
|
202
|
+
testingParts.push(es ? `Coloca los tests nuevos en ${dirs}.` : `Place new tests under ${dirs}.`);
|
|
203
|
+
}
|
|
83
204
|
return [
|
|
84
205
|
{
|
|
85
206
|
id: "review",
|
|
86
207
|
title: "Code Review (JS/TS)",
|
|
87
|
-
body:
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: "refactor",
|
|
91
|
-
title: "Refactor (JS/TS)",
|
|
92
|
-
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable. Respeta los tipos existentes.`,
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: "testing",
|
|
96
|
-
title: "Testing (JS/TS)",
|
|
97
|
-
body: `Escribe tests para el código señalado usando el test runner detectado (${detection.testRunner?.value ?? "el del proyecto"}). Cubre el camino feliz y al menos un caso límite.`,
|
|
208
|
+
body: ctx ? reviewParts.join(" ") : reviewBody(lang, focus, framework),
|
|
98
209
|
},
|
|
210
|
+
{ id: "refactor", title: "Refactor (JS/TS)", body: refactorBody(lang, detection.usesTypeScript ? t.refactorExtra : undefined) },
|
|
211
|
+
{ id: "testing", title: "Testing (JS/TS)", body: ctx ? testingParts.join(" ") : testingBody(lang, runner) },
|
|
99
212
|
];
|
|
100
213
|
}
|
|
101
214
|
export const jsTsPack = { id: "js-ts", detect, rules, promptTemplates };
|
package/dist/packs/kotlin.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, } from "../core/i18n.js";
|
|
1
2
|
function detect(signals) {
|
|
2
|
-
const
|
|
3
|
+
const gradleSource = signals.buildGradle && /\bkotlin\b/i.test(signals.buildGradle)
|
|
4
|
+
? signals.buildGradle
|
|
5
|
+
: undefined;
|
|
6
|
+
const mavenSource = signals.pomXml && /\bkotlin\b/i.test(signals.pomXml)
|
|
7
|
+
? signals.pomXml
|
|
8
|
+
: undefined;
|
|
9
|
+
const source = gradleSource ?? mavenSource;
|
|
3
10
|
if (!source)
|
|
4
11
|
return null;
|
|
5
12
|
// Modern Gradle projects often declare the Kotlin plugin via a version catalog alias
|
|
6
13
|
// (`alias(libs.plugins.kotlin.android)`) rather than the literal `kotlin("jvm")` or
|
|
7
14
|
// `org.jetbrains.kotlin` plugin id, so a plain word-boundary check is more robust.
|
|
8
|
-
if (!/\bkotlin\b/i.test(source))
|
|
9
|
-
return null;
|
|
10
15
|
const framework = /ktor/i.test(source)
|
|
11
16
|
? { value: "ktor", confidence: "high" }
|
|
12
17
|
: /com\.android\.application|com\.android\.library/i.test(source)
|
|
@@ -24,42 +29,62 @@ function detect(signals) {
|
|
|
24
29
|
language: "Kotlin",
|
|
25
30
|
framework,
|
|
26
31
|
testRunner,
|
|
27
|
-
packageManager:
|
|
32
|
+
packageManager: mavenSource && !gradleSource
|
|
33
|
+
? signals.hasFile("mvnw") || signals.hasFile("mvnw.cmd")
|
|
34
|
+
? { value: "maven wrapper", confidence: "high" }
|
|
35
|
+
: { value: "maven", confidence: "high" }
|
|
36
|
+
: signals.hasFile("gradlew") || signals.hasFile("gradlew.bat")
|
|
37
|
+
? { value: "gradle wrapper", confidence: "high" }
|
|
38
|
+
: { value: "gradle", confidence: "high" },
|
|
28
39
|
};
|
|
29
40
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"Sigue las convenciones de estilo oficiales de Kotlin (kotlinlang.org/docs/coding-conventions.html).",
|
|
36
|
-
"Ejecuta los tests con `gradle test` antes de terminar una tarea.",
|
|
37
|
-
"Prefiere tipos no-nulos y `val` sobre `var` salvo que la mutabilidad sea necesaria.",
|
|
38
|
-
],
|
|
39
|
-
architectureNotes: [
|
|
41
|
+
const TEXTS = {
|
|
42
|
+
es: {
|
|
43
|
+
style: "Sigue las convenciones de estilo oficiales de Kotlin (kotlinlang.org/docs/coding-conventions.html).",
|
|
44
|
+
nullsafety: "Prefiere tipos no-nulos y `val` sobre `var` salvo que la mutabilidad sea necesaria.",
|
|
45
|
+
arch: [
|
|
40
46
|
"Respeta la separación en capas (controller/service/repository) si el proyecto ya la usa.",
|
|
41
47
|
"Prefiere inyección de dependencias sobre instanciación manual cuando el framework ya la ofrezca.",
|
|
42
48
|
],
|
|
49
|
+
reviewFocus: "uso innecesario de `!!`",
|
|
50
|
+
},
|
|
51
|
+
en: {
|
|
52
|
+
style: "Follow the official Kotlin style conventions (kotlinlang.org/docs/coding-conventions.html).",
|
|
53
|
+
nullsafety: "Prefer non-null types and `val` over `var` unless mutability is required.",
|
|
54
|
+
arch: [
|
|
55
|
+
"Respect the layered separation (controller/service/repository) if the project already uses it.",
|
|
56
|
+
"Prefer dependency injection over manual instantiation when the framework already provides it.",
|
|
57
|
+
],
|
|
58
|
+
reviewFocus: "unnecessary `!!` usage",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
function rules(detection, lang) {
|
|
62
|
+
const t = TEXTS[lang];
|
|
63
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
64
|
+
return {
|
|
65
|
+
summary: summarySentence(lang, "Kotlin", framework, detection.packageManager?.value),
|
|
66
|
+
conventions: [
|
|
67
|
+
t.style,
|
|
68
|
+
runTestsConvention(lang, detection.packageManager?.value === "maven wrapper"
|
|
69
|
+
? "`./mvnw test`"
|
|
70
|
+
: detection.packageManager?.value === "maven"
|
|
71
|
+
? "`mvn test`"
|
|
72
|
+
: detection.packageManager?.value === "gradle wrapper"
|
|
73
|
+
? "`./gradlew test`"
|
|
74
|
+
: "`gradle test`"),
|
|
75
|
+
t.nullsafety,
|
|
76
|
+
],
|
|
77
|
+
architectureNotes: t.arch,
|
|
43
78
|
};
|
|
44
79
|
}
|
|
45
|
-
function promptTemplates(detection) {
|
|
46
|
-
const
|
|
80
|
+
function promptTemplates(detection, lang) {
|
|
81
|
+
const t = TEXTS[lang];
|
|
82
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
83
|
+
const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
|
|
47
84
|
return [
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
body: `Revisa el diff actual buscando bugs, uso innecesario de \`!!\` y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: "refactor",
|
|
55
|
-
title: "Refactor (Kotlin)",
|
|
56
|
-
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
id: "testing",
|
|
60
|
-
title: "Testing (Kotlin)",
|
|
61
|
-
body: `Escribe tests con ${detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : "el test runner del proyecto"} para el código señalado. Cubre el camino feliz y al menos un caso límite.`,
|
|
62
|
-
},
|
|
85
|
+
{ id: "review", title: "Code Review (Kotlin)", body: reviewBody(lang, t.reviewFocus, framework) },
|
|
86
|
+
{ id: "refactor", title: "Refactor (Kotlin)", body: refactorBody(lang) },
|
|
87
|
+
{ id: "testing", title: "Testing (Kotlin)", body: testingBody(lang, runner) },
|
|
63
88
|
];
|
|
64
89
|
}
|
|
65
90
|
export const kotlinPack = { id: "kotlin", detect, rules, promptTemplates };
|
package/dist/packs/php.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, } from "../core/i18n.js";
|
|
1
2
|
const FRAMEWORKS = {
|
|
2
3
|
"laravel/framework": "laravel",
|
|
3
4
|
"symfony/symfony": "symfony",
|
|
@@ -25,39 +26,41 @@ function detect(signals) {
|
|
|
25
26
|
packageManager: { value: "composer", confidence: "high" },
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"Sigue PSR-12 para el estilo de código.",
|
|
34
|
-
`Ejecuta los tests con ${detection.testRunner?.value === "phpunit" ? "vendor/bin/phpunit" : "el test runner del proyecto"} antes de terminar una tarea.`,
|
|
35
|
-
"Declara toda dependencia nueva en composer.json, nunca la instales sin registrarla.",
|
|
36
|
-
],
|
|
37
|
-
architectureNotes: [
|
|
29
|
+
const TEXTS = {
|
|
30
|
+
es: {
|
|
31
|
+
style: "Sigue PSR-12 para el estilo de código.",
|
|
32
|
+
deps: "Declara toda dependencia nueva en composer.json, nunca la instales sin registrarla.",
|
|
33
|
+
arch: [
|
|
38
34
|
"Respeta la estructura MVC del framework si el proyecto ya la sigue.",
|
|
39
35
|
"Evita lógica de negocio en los controladores cuando el proyecto ya use capas de servicio.",
|
|
40
36
|
],
|
|
37
|
+
},
|
|
38
|
+
en: {
|
|
39
|
+
style: "Follow PSR-12 for code style.",
|
|
40
|
+
deps: "Declare every new dependency in composer.json; never install one without registering it.",
|
|
41
|
+
arch: [
|
|
42
|
+
"Respect the framework's MVC structure if the project already follows it.",
|
|
43
|
+
"Avoid business logic in controllers when the project already uses service layers.",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
function rules(detection, lang) {
|
|
48
|
+
const t = TEXTS[lang];
|
|
49
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
50
|
+
const testCmd = detection.testRunner?.value === "phpunit" ? "vendor/bin/phpunit" : undefined;
|
|
51
|
+
return {
|
|
52
|
+
summary: summarySentence(lang, "PHP", framework, "composer"),
|
|
53
|
+
conventions: [t.style, runTestsConvention(lang, testCmd), t.deps],
|
|
54
|
+
architectureNotes: t.arch,
|
|
41
55
|
};
|
|
42
56
|
}
|
|
43
|
-
function promptTemplates(detection) {
|
|
44
|
-
const framework = detection.framework?.value
|
|
57
|
+
function promptTemplates(detection, lang) {
|
|
58
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
59
|
+
const runner = detection.testRunner?.value === "phpunit" ? "PHPUnit" : undefined;
|
|
45
60
|
return [
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
body: `Revisa el diff actual buscando bugs y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: "refactor",
|
|
53
|
-
title: "Refactor (PHP)",
|
|
54
|
-
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "testing",
|
|
58
|
-
title: "Testing (PHP)",
|
|
59
|
-
body: `Escribe tests con ${detection.testRunner?.value === "phpunit" ? "PHPUnit" : "el test runner del proyecto"} para el código señalado. Cubre el camino feliz y al menos un caso límite.`,
|
|
60
|
-
},
|
|
61
|
+
{ id: "review", title: "Code Review (PHP)", body: reviewBody(lang, "", framework) },
|
|
62
|
+
{ id: "refactor", title: "Refactor (PHP)", body: refactorBody(lang) },
|
|
63
|
+
{ id: "testing", title: "Testing (PHP)", body: testingBody(lang, runner) },
|
|
61
64
|
];
|
|
62
65
|
}
|
|
63
66
|
export const phpPack = { id: "php", detect, rules, promptTemplates };
|
package/dist/packs/python.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, } from "../core/i18n.js";
|
|
2
|
+
import { canonicalOf } from "../core/canonical-commands.js";
|
|
1
3
|
const FRAMEWORKS = [
|
|
2
4
|
["fastapi", "fastapi"],
|
|
3
5
|
["django", "django"],
|
|
@@ -34,55 +36,126 @@ function extractPyprojectDependencySections(pyproject) {
|
|
|
34
36
|
sections.push(...poetryDepsBlocks);
|
|
35
37
|
return sections.length > 0 ? sections.join("\n") : pyproject;
|
|
36
38
|
}
|
|
39
|
+
function frameworkSourceProject(pyproject) {
|
|
40
|
+
const projectBlock = pyproject.match(/(?:^|\n)\[project\]([\s\S]*?)(?:\n\[|$)/)?.[1];
|
|
41
|
+
const name = projectBlock?.match(/(?:^|\n)\s*name\s*=\s*["']([^"']+)["']/i)?.[1].toLowerCase();
|
|
42
|
+
return FRAMEWORKS.find(([framework]) => framework === name)?.[1];
|
|
43
|
+
}
|
|
37
44
|
function detect(signals) {
|
|
38
45
|
const source = signals.pyprojectToml ?? signals.requirementsTxt ?? signals.environmentYml;
|
|
39
46
|
if (!source)
|
|
40
47
|
return null;
|
|
41
48
|
const searchText = signals.pyprojectToml ? extractPyprojectDependencySections(signals.pyprojectToml) : source;
|
|
42
|
-
const
|
|
49
|
+
const frameworkSource = signals.pyprojectToml
|
|
50
|
+
? frameworkSourceProject(signals.pyprojectToml)
|
|
51
|
+
: undefined;
|
|
52
|
+
const framework = findIn(searchText, FRAMEWORKS) ?? {
|
|
53
|
+
value: "none",
|
|
54
|
+
confidence: frameworkSource
|
|
55
|
+
? "high"
|
|
56
|
+
: "low",
|
|
57
|
+
};
|
|
43
58
|
const testRunner = findIn(searchText, TEST_RUNNERS) ?? { value: "unknown", confidence: "low" };
|
|
44
59
|
const packageManager = signals.environmentYml
|
|
45
60
|
? { value: "conda", confidence: "high" }
|
|
46
|
-
: signals.hasFile("
|
|
47
|
-
? { value: "
|
|
48
|
-
: signals.
|
|
49
|
-
? { value: "
|
|
50
|
-
:
|
|
51
|
-
|
|
61
|
+
: signals.hasFile("uv.lock")
|
|
62
|
+
? { value: "uv", confidence: "high" }
|
|
63
|
+
: signals.hasFile("poetry.lock")
|
|
64
|
+
? { value: "poetry", confidence: "high" }
|
|
65
|
+
: signals.pyprojectToml
|
|
66
|
+
? { value: "pip (pyproject.toml)", confidence: "low" }
|
|
67
|
+
: { value: "pip", confidence: "low" };
|
|
68
|
+
return { packId: "python", language: "Python", framework, frameworkSource, testRunner, packageManager };
|
|
52
69
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"Sigue PEP 8; usa type hints en funciones públicas.",
|
|
59
|
-
`Ejecuta los tests con ${detection.testRunner?.value ?? "el test runner del proyecto"} antes de terminar una tarea.`,
|
|
60
|
-
"No introduzcas dependencias nuevas sin añadirlas al manifiesto de dependencias existente.",
|
|
61
|
-
],
|
|
62
|
-
architectureNotes: [
|
|
70
|
+
const TEXTS = {
|
|
71
|
+
es: {
|
|
72
|
+
style: "Sigue PEP 8; usa type hints en funciones públicas.",
|
|
73
|
+
deps: "No introduzcas dependencias nuevas sin añadirlas al manifiesto de dependencias existente.",
|
|
74
|
+
arch: [
|
|
63
75
|
"Mantén la lógica de negocio separada de la capa de framework cuando el proyecto ya siga ese patrón.",
|
|
64
76
|
"Usa entornos virtuales; no instales paquetes globalmente.",
|
|
65
77
|
],
|
|
78
|
+
reviewFocus: "manejo de excepciones incorrecto",
|
|
79
|
+
refactorExtra: "Respeta los type hints existentes.",
|
|
80
|
+
},
|
|
81
|
+
en: {
|
|
82
|
+
style: "Follow PEP 8; use type hints on public functions.",
|
|
83
|
+
deps: "Do not introduce new dependencies without adding them to the existing dependency manifest.",
|
|
84
|
+
arch: [
|
|
85
|
+
"Keep business logic separate from the framework layer when the project already follows that pattern.",
|
|
86
|
+
"Use virtual environments; never install packages globally.",
|
|
87
|
+
],
|
|
88
|
+
reviewFocus: "incorrect exception handling",
|
|
89
|
+
refactorExtra: "Respect the existing type hints.",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
const FRAMEWORK_RISKS = {
|
|
93
|
+
flask: {
|
|
94
|
+
es: "Presta especial atención al contexto de aplicación/petición, los blueprints y el manejo de errores HTTP.",
|
|
95
|
+
en: "Pay special attention to application/request context, blueprints and HTTP error handling.",
|
|
96
|
+
},
|
|
97
|
+
django: {
|
|
98
|
+
es: "Presta especial atención a migraciones pendientes, consultas N+1 del ORM y validación en forms/serializers.",
|
|
99
|
+
en: "Pay special attention to pending migrations, ORM N+1 queries and validation in forms/serializers.",
|
|
100
|
+
},
|
|
101
|
+
fastapi: {
|
|
102
|
+
es: "Presta especial atención a los modelos Pydantic, las dependencias async y los códigos de respuesta declarados.",
|
|
103
|
+
en: "Pay special attention to Pydantic models, async dependencies and declared response codes.",
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
function rules(detection, lang, ctx) {
|
|
107
|
+
const t = TEXTS[lang];
|
|
108
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
109
|
+
const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
|
|
110
|
+
const testCmd = canonicalOf(ctx, "test", "python")?.command ?? runner;
|
|
111
|
+
return {
|
|
112
|
+
summary: detection.frameworkSource
|
|
113
|
+
? lang === "es"
|
|
114
|
+
? `Repositorio Python que implementa ${detection.frameworkSource}.`
|
|
115
|
+
: `Python repository implementing ${detection.frameworkSource}.`
|
|
116
|
+
: summarySentence(lang, "Python", framework),
|
|
117
|
+
conventions: [t.style, runTestsConvention(lang, testCmd), t.deps],
|
|
118
|
+
architectureNotes: t.arch,
|
|
66
119
|
};
|
|
67
120
|
}
|
|
68
|
-
function promptTemplates(detection) {
|
|
69
|
-
const
|
|
121
|
+
function promptTemplates(detection, lang, ctx) {
|
|
122
|
+
const t = TEXTS[lang];
|
|
123
|
+
const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
|
|
124
|
+
const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
|
|
125
|
+
const test = canonicalOf(ctx, "test", "python");
|
|
126
|
+
const testDirs = ctx?.facts.testDirs ?? [];
|
|
127
|
+
const hasTox = ctx?.facts.commands.some((c) => c.source === "tox") ?? false;
|
|
128
|
+
const es = lang === "es";
|
|
129
|
+
const reviewParts = [];
|
|
130
|
+
reviewParts.push(es
|
|
131
|
+
? `Revisa el diff actual contra las convenciones Python de este repositorio${framework ? ` (${framework})` : ""}.`
|
|
132
|
+
: `Review the current diff against this repository's Python conventions${framework ? ` (${framework})` : ""}.`);
|
|
133
|
+
if (test) {
|
|
134
|
+
reviewParts.push(es ? `Ejecuta \`${test.command}\` antes de dar por buena la revisión.` : `Run \`${test.command}\` before approving the review.`);
|
|
135
|
+
}
|
|
136
|
+
if (hasTox) {
|
|
137
|
+
reviewParts.push(es ? "La matriz completa de entornos se ejecuta con tox (`tox.ini`)." : "The full environment matrix runs through tox (`tox.ini`).");
|
|
138
|
+
}
|
|
139
|
+
const risk = FRAMEWORK_RISKS[framework ?? detection.frameworkSource ?? ""]?.[lang];
|
|
140
|
+
if (risk)
|
|
141
|
+
reviewParts.push(risk);
|
|
142
|
+
reviewParts.push(es ? `Busca también bugs: ${t.reviewFocus}.` : `Also look for bugs: ${t.reviewFocus}.`);
|
|
143
|
+
if (testDirs.length > 0) {
|
|
144
|
+
const dirs = testDirs.map((d) => `\`${d}\``).join(", ");
|
|
145
|
+
reviewParts.push(es ? `Los tests viven en ${dirs}.` : `Tests live under ${dirs}.`);
|
|
146
|
+
}
|
|
147
|
+
reviewParts.push(es ? "Señala solo hallazgos concretos con archivo y línea." : "Report only concrete findings with file and line references.");
|
|
148
|
+
const testingParts = [testingBody(lang, runner)];
|
|
149
|
+
if (test)
|
|
150
|
+
testingParts.push(es ? `Verifica la suite con \`${test.command}\` antes de terminar.` : `Verify the suite with \`${test.command}\` before finishing.`);
|
|
151
|
+
if (testDirs.length > 0) {
|
|
152
|
+
const dirs = testDirs.map((d) => `\`${d}\``).join(", ");
|
|
153
|
+
testingParts.push(es ? `Coloca los tests nuevos en ${dirs}.` : `Place new tests under ${dirs}.`);
|
|
154
|
+
}
|
|
70
155
|
return [
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
body: `Revisa el diff actual buscando bugs, manejo de excepciones incorrecto y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: "refactor",
|
|
78
|
-
title: "Refactor (Python)",
|
|
79
|
-
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable. Respeta los type hints existentes.`,
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: "testing",
|
|
83
|
-
title: "Testing (Python)",
|
|
84
|
-
body: `Escribe tests para el código señalado usando ${detection.testRunner?.value ?? "el test runner del proyecto"}. Cubre el camino feliz y al menos un caso límite.`,
|
|
85
|
-
},
|
|
156
|
+
{ id: "review", title: "Code Review (Python)", body: ctx ? reviewParts.join(" ") : reviewBody(lang, t.reviewFocus, framework) },
|
|
157
|
+
{ id: "refactor", title: "Refactor (Python)", body: refactorBody(lang, t.refactorExtra) },
|
|
158
|
+
{ id: "testing", title: "Testing (Python)", body: ctx ? testingParts.join(" ") : testingBody(lang, runner) },
|
|
86
159
|
];
|
|
87
160
|
}
|
|
88
161
|
export const pythonPack = { id: "python", detect, rules, promptTemplates };
|