agent-rules-init 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,12 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, unknownRunnerLabel, } from "../core/i18n.js";
1
2
  function detect(signals) {
2
3
  const source = signals.buildGradle;
3
4
  if (!source)
4
5
  return null;
5
- if (!/kotlin\(|org\.jetbrains\.kotlin/i.test(source))
6
+ // Modern Gradle projects often declare the Kotlin plugin via a version catalog alias
7
+ // (`alias(libs.plugins.kotlin.android)`) rather than the literal `kotlin("jvm")` or
8
+ // `org.jetbrains.kotlin` plugin id, so a plain word-boundary check is more robust.
9
+ if (!/\bkotlin\b/i.test(source))
6
10
  return null;
7
11
  const framework = /ktor/i.test(source)
8
12
  ? { value: "ktor", confidence: "high" }
@@ -24,39 +28,43 @@ function detect(signals) {
24
28
  packageManager: { value: "gradle", confidence: "high" },
25
29
  };
26
30
  }
27
- function rules(detection) {
28
- const framework = detection.framework?.value ?? "none";
29
- return {
30
- summary: `Proyecto Kotlin${framework !== "none" ? ` con ${framework}` : ""} (gradle).`,
31
- conventions: [
32
- "Sigue las convenciones de estilo oficiales de Kotlin (kotlinlang.org/docs/coding-conventions.html).",
33
- "Ejecuta los tests con `gradle test` antes de terminar una tarea.",
34
- "Prefiere tipos no-nulos y `val` sobre `var` salvo que la mutabilidad sea necesaria.",
35
- ],
36
- architectureNotes: [
31
+ const TEXTS = {
32
+ es: {
33
+ style: "Sigue las convenciones de estilo oficiales de Kotlin (kotlinlang.org/docs/coding-conventions.html).",
34
+ nullsafety: "Prefiere tipos no-nulos y `val` sobre `var` salvo que la mutabilidad sea necesaria.",
35
+ arch: [
37
36
  "Respeta la separación en capas (controller/service/repository) si el proyecto ya la usa.",
38
37
  "Prefiere inyección de dependencias sobre instanciación manual cuando el framework ya la ofrezca.",
39
38
  ],
39
+ reviewFocus: "uso innecesario de `!!`",
40
+ },
41
+ en: {
42
+ style: "Follow the official Kotlin style conventions (kotlinlang.org/docs/coding-conventions.html).",
43
+ nullsafety: "Prefer non-null types and `val` over `var` unless mutability is required.",
44
+ arch: [
45
+ "Respect the layered separation (controller/service/repository) if the project already uses it.",
46
+ "Prefer dependency injection over manual instantiation when the framework already provides it.",
47
+ ],
48
+ reviewFocus: "unnecessary `!!` usage",
49
+ },
50
+ };
51
+ function rules(detection, lang) {
52
+ const t = TEXTS[lang];
53
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
54
+ return {
55
+ summary: summarySentence(lang, "Kotlin", framework, "gradle"),
56
+ conventions: [t.style, runTestsConvention(lang, "`gradle test`"), t.nullsafety],
57
+ architectureNotes: t.arch,
40
58
  };
41
59
  }
42
- function promptTemplates(detection) {
43
- const framework = detection.framework?.value ?? "el framework del proyecto";
60
+ function promptTemplates(detection, lang) {
61
+ const t = TEXTS[lang];
62
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
63
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner.value : unknownRunnerLabel(lang);
44
64
  return [
45
- {
46
- id: "review",
47
- title: "Code Review (Kotlin)",
48
- 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.`,
49
- },
50
- {
51
- id: "refactor",
52
- title: "Refactor (Kotlin)",
53
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
54
- },
55
- {
56
- id: "testing",
57
- title: "Testing (Kotlin)",
58
- 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.`,
59
- },
65
+ { id: "review", title: "Code Review (Kotlin)", body: reviewBody(lang, t.reviewFocus, framework) },
66
+ { id: "refactor", title: "Refactor (Kotlin)", body: refactorBody(lang) },
67
+ { id: "testing", title: "Testing (Kotlin)", body: testingBody(lang, runner) },
60
68
  ];
61
69
  }
62
70
  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, unknownFrameworkLabel, unknownRunnerLabel, } 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
- function rules(detection) {
29
- const framework = detection.framework?.value ?? "none";
30
- return {
31
- summary: `Proyecto PHP${framework !== "none" ? ` con ${framework}` : ""} (composer).`,
32
- conventions: [
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" : unknownRunnerLabel(lang);
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 ?? "el framework del proyecto";
57
+ function promptTemplates(detection, lang) {
58
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
59
+ const runner = detection.testRunner?.value === "phpunit" ? "PHPUnit" : unknownRunnerLabel(lang);
45
60
  return [
46
- {
47
- id: "review",
48
- title: "Code Review (PHP)",
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 };
@@ -1,3 +1,4 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, unknownRunnerLabel, } from "../core/i18n.js";
1
2
  const FRAMEWORKS = [
2
3
  ["fastapi", "fastapi"],
3
4
  ["django", "django"],
@@ -15,12 +16,32 @@ function findIn(haystack, table) {
15
16
  }
16
17
  return undefined;
17
18
  }
19
+ // Searching the whole pyproject.toml text (project name, URLs, script entry points, etc.)
20
+ // produces false positives — e.g. Flask's own pyproject.toml has `name = "flask"`, which
21
+ // isn't a dependency at all. Scope the search to the actual dependency declarations.
22
+ function extractPyprojectDependencySections(pyproject) {
23
+ const sections = [];
24
+ const mainDeps = pyproject.match(/(?:^|\n)dependencies\s*=\s*\[([\s\S]*?)\]/);
25
+ if (mainDeps)
26
+ sections.push(mainDeps[1]);
27
+ const optionalDeps = pyproject.match(/\[project\.optional-dependencies\]([\s\S]*?)(?:\n\[|$)/);
28
+ if (optionalDeps)
29
+ sections.push(optionalDeps[1]);
30
+ const dependencyGroups = pyproject.match(/\[dependency-groups\]([\s\S]*?)(?:\n\[|$)/);
31
+ if (dependencyGroups)
32
+ sections.push(dependencyGroups[1]);
33
+ const poetryDepsBlocks = pyproject.match(/\[tool\.poetry(?:\.group\.\w+)?\.dependencies\]([\s\S]*?)(?:\n\[|$)/g);
34
+ if (poetryDepsBlocks)
35
+ sections.push(...poetryDepsBlocks);
36
+ return sections.length > 0 ? sections.join("\n") : pyproject;
37
+ }
18
38
  function detect(signals) {
19
39
  const source = signals.pyprojectToml ?? signals.requirementsTxt ?? signals.environmentYml;
20
40
  if (!source)
21
41
  return null;
22
- const framework = findIn(source, FRAMEWORKS) ?? { value: "none", confidence: "low" };
23
- const testRunner = findIn(source, TEST_RUNNERS) ?? { value: "unknown", confidence: "low" };
42
+ const searchText = signals.pyprojectToml ? extractPyprojectDependencySections(signals.pyprojectToml) : source;
43
+ const framework = findIn(searchText, FRAMEWORKS) ?? { value: "none", confidence: "low" };
44
+ const testRunner = findIn(searchText, TEST_RUNNERS) ?? { value: "unknown", confidence: "low" };
24
45
  const packageManager = signals.environmentYml
25
46
  ? { value: "conda", confidence: "high" }
26
47
  : signals.hasFile("poetry.lock")
@@ -30,39 +51,46 @@ function detect(signals) {
30
51
  : { value: "pip", confidence: "low" };
31
52
  return { packId: "python", language: "Python", framework, testRunner, packageManager };
32
53
  }
33
- function rules(detection) {
34
- const framework = detection.framework?.value ?? "none";
35
- return {
36
- summary: `Proyecto Python${framework !== "none" ? ` con ${framework}` : ""}.`,
37
- conventions: [
38
- "Sigue PEP 8; usa type hints en funciones públicas.",
39
- `Ejecuta los tests con ${detection.testRunner?.value ?? "el test runner del proyecto"} antes de terminar una tarea.`,
40
- "No introduzcas dependencias nuevas sin añadirlas al manifiesto de dependencias existente.",
41
- ],
42
- architectureNotes: [
54
+ const TEXTS = {
55
+ es: {
56
+ style: "Sigue PEP 8; usa type hints en funciones públicas.",
57
+ deps: "No introduzcas dependencias nuevas sin añadirlas al manifiesto de dependencias existente.",
58
+ arch: [
43
59
  "Mantén la lógica de negocio separada de la capa de framework cuando el proyecto ya siga ese patrón.",
44
60
  "Usa entornos virtuales; no instales paquetes globalmente.",
45
61
  ],
62
+ reviewFocus: "manejo de excepciones incorrecto",
63
+ refactorExtra: "Respeta los type hints existentes.",
64
+ },
65
+ en: {
66
+ style: "Follow PEP 8; use type hints on public functions.",
67
+ deps: "Do not introduce new dependencies without adding them to the existing dependency manifest.",
68
+ arch: [
69
+ "Keep business logic separate from the framework layer when the project already follows that pattern.",
70
+ "Use virtual environments; never install packages globally.",
71
+ ],
72
+ reviewFocus: "incorrect exception handling",
73
+ refactorExtra: "Respect the existing type hints.",
74
+ },
75
+ };
76
+ function rules(detection, lang) {
77
+ const t = TEXTS[lang];
78
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
79
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : undefined;
80
+ return {
81
+ summary: summarySentence(lang, "Python", framework),
82
+ conventions: [t.style, runTestsConvention(lang, runner ?? unknownRunnerLabel(lang)), t.deps],
83
+ architectureNotes: t.arch,
46
84
  };
47
85
  }
48
- function promptTemplates(detection) {
49
- const framework = detection.framework?.value ?? "el framework del proyecto";
86
+ function promptTemplates(detection, lang) {
87
+ const t = TEXTS[lang];
88
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
89
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner.value : unknownRunnerLabel(lang);
50
90
  return [
51
- {
52
- id: "review",
53
- title: "Code Review (Python)",
54
- 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.`,
55
- },
56
- {
57
- id: "refactor",
58
- title: "Refactor (Python)",
59
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable. Respeta los type hints existentes.`,
60
- },
61
- {
62
- id: "testing",
63
- title: "Testing (Python)",
64
- 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.`,
65
- },
91
+ { id: "review", title: "Code Review (Python)", body: reviewBody(lang, t.reviewFocus, framework) },
92
+ { id: "refactor", title: "Refactor (Python)", body: refactorBody(lang, t.refactorExtra) },
93
+ { id: "testing", title: "Testing (Python)", body: testingBody(lang, runner) },
66
94
  ];
67
95
  }
68
96
  export const pythonPack = { id: "python", detect, rules, promptTemplates };
package/dist/packs/r.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, unknownRunnerLabel, } from "../core/i18n.js";
1
2
  const FRAMEWORKS = [
2
3
  ["shiny", "shiny"],
3
4
  ["plumber", "plumber"],
@@ -22,39 +23,41 @@ function detect(signals) {
22
23
  : { value: "CRAN", confidence: "low" };
23
24
  return { packId: "r", language: "R", framework, testRunner, packageManager };
24
25
  }
25
- function rules(detection) {
26
- const framework = detection.framework?.value ?? "none";
27
- return {
28
- summary: `Proyecto R${framework !== "none" ? ` con ${framework}` : ""} (${detection.packageManager?.value}).`,
29
- conventions: [
30
- "Sigue la guía de estilo tidyverse (style.tidyverse.org) salvo que el proyecto ya use otra.",
31
- `Ejecuta los tests con ${detection.testRunner?.value === "testthat" ? "testthat::test_dir(\"tests\")" : "el test runner del proyecto"} antes de terminar una tarea.`,
32
- "Declara toda dependencia nueva en DESCRIPTION y actualiza renv.lock si el proyecto usa renv.",
33
- ],
34
- architectureNotes: [
26
+ const TEXTS = {
27
+ es: {
28
+ style: "Sigue la guía de estilo tidyverse (style.tidyverse.org) salvo que el proyecto ya use otra.",
29
+ deps: "Declara toda dependencia nueva en DESCRIPTION y actualiza renv.lock si el proyecto usa renv.",
30
+ arch: [
35
31
  "Mantén las funciones puras y con una responsabilidad clara; evita modificar el entorno global.",
36
32
  "Separa el análisis/scripts de la lógica reutilizable empaquetada en funciones.",
37
33
  ],
34
+ },
35
+ en: {
36
+ style: "Follow the tidyverse style guide (style.tidyverse.org) unless the project already uses another one.",
37
+ deps: "Declare every new dependency in DESCRIPTION and update renv.lock if the project uses renv.",
38
+ arch: [
39
+ "Keep functions pure and single-purpose; avoid mutating the global environment.",
40
+ "Separate analysis/scripts from reusable logic packaged as functions.",
41
+ ],
42
+ },
43
+ };
44
+ function rules(detection, lang) {
45
+ const t = TEXTS[lang];
46
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
47
+ const testCmd = detection.testRunner?.value === "testthat" ? 'testthat::test_dir("tests")' : unknownRunnerLabel(lang);
48
+ return {
49
+ summary: summarySentence(lang, "R", framework, detection.packageManager?.value),
50
+ conventions: [t.style, runTestsConvention(lang, testCmd), t.deps],
51
+ architectureNotes: t.arch,
38
52
  };
39
53
  }
40
- function promptTemplates(detection) {
41
- const framework = detection.framework?.value ?? "el framework del proyecto";
54
+ function promptTemplates(detection, lang) {
55
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
56
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner.value : unknownRunnerLabel(lang);
42
57
  return [
43
- {
44
- id: "review",
45
- title: "Code Review (R)",
46
- body: `Revisa el diff actual buscando bugs y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
47
- },
48
- {
49
- id: "refactor",
50
- title: "Refactor (R)",
51
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
52
- },
53
- {
54
- id: "testing",
55
- title: "Testing (R)",
56
- 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.`,
57
- },
58
+ { id: "review", title: "Code Review (R)", body: reviewBody(lang, "", framework) },
59
+ { id: "refactor", title: "Refactor (R)", body: refactorBody(lang) },
60
+ { id: "testing", title: "Testing (R)", body: testingBody(lang, runner) },
58
61
  ];
59
62
  }
60
63
  export const rPack = { id: "r", detect, rules, promptTemplates };
@@ -1,3 +1,4 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, unknownRunnerLabel, } from "../core/i18n.js";
1
2
  function detect(signals) {
2
3
  const source = signals.gemfile;
3
4
  if (!source)
@@ -21,39 +22,47 @@ function detect(signals) {
21
22
  packageManager: { value: "bundler", confidence: "high" },
22
23
  };
23
24
  }
24
- function rules(detection) {
25
- const framework = detection.framework?.value ?? "none";
26
- return {
27
- summary: `Proyecto Ruby${framework !== "none" ? ` con ${framework}` : ""} (bundler).`,
28
- conventions: [
29
- "Sigue la guía de estilo Ruby estándar (snake_case para métodos/variables, CamelCase para clases).",
30
- `Ejecuta los tests con ${detection.testRunner?.value === "rspec" ? "bundle exec rspec" : detection.testRunner?.value === "minitest" ? "bundle exec rake test" : "el test runner del proyecto"} antes de terminar una tarea.`,
31
- "Declara toda dependencia nueva en el Gemfile, nunca instales una gema sin registrarla.",
32
- ],
33
- architectureNotes: [
25
+ const TEXTS = {
26
+ es: {
27
+ style: "Sigue la guía de estilo Ruby estándar (snake_case para métodos/variables, CamelCase para clases).",
28
+ deps: "Declara toda dependencia nueva en el Gemfile, nunca instales una gema sin registrarla.",
29
+ arch: [
34
30
  "Respeta la estructura MVC del framework si el proyecto ya la sigue.",
35
31
  "Evita lógica de negocio en los controladores cuando el proyecto ya use capas de servicio.",
36
32
  ],
33
+ },
34
+ en: {
35
+ style: "Follow the standard Ruby style guide (snake_case for methods/variables, CamelCase for classes).",
36
+ deps: "Declare every new dependency in the Gemfile; never install a gem without registering it.",
37
+ arch: [
38
+ "Respect the framework's MVC structure if the project already follows it.",
39
+ "Avoid business logic in controllers when the project already uses service layers.",
40
+ ],
41
+ },
42
+ };
43
+ function testCommand(detection, lang) {
44
+ if (detection.testRunner?.value === "rspec")
45
+ return "bundle exec rspec";
46
+ if (detection.testRunner?.value === "minitest")
47
+ return "bundle exec rake test";
48
+ return unknownRunnerLabel(lang);
49
+ }
50
+ function rules(detection, lang) {
51
+ const t = TEXTS[lang];
52
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
53
+ return {
54
+ summary: summarySentence(lang, "Ruby", framework, "bundler"),
55
+ conventions: [t.style, runTestsConvention(lang, testCommand(detection, lang)), t.deps],
56
+ architectureNotes: t.arch,
37
57
  };
38
58
  }
39
- function promptTemplates(detection) {
40
- const framework = detection.framework?.value ?? "el framework del proyecto";
59
+ function promptTemplates(detection, lang) {
60
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
61
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner.value : unknownRunnerLabel(lang);
41
62
  return [
42
- {
43
- id: "review",
44
- title: "Code Review (Ruby)",
45
- body: `Revisa el diff actual buscando bugs y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
46
- },
47
- {
48
- id: "refactor",
49
- title: "Refactor (Ruby)",
50
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
51
- },
52
- {
53
- id: "testing",
54
- title: "Testing (Ruby)",
55
- 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.`,
56
- },
63
+ { id: "review", title: "Code Review (Ruby)", body: reviewBody(lang, "", framework) },
64
+ { id: "refactor", title: "Refactor (Ruby)", body: refactorBody(lang) },
65
+ { id: "testing", title: "Testing (Ruby)", body: testingBody(lang, runner) },
57
66
  ];
58
67
  }
59
68
  export const rubyPack = { id: "ruby", detect, rules, promptTemplates };
@@ -1,3 +1,4 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, } from "../core/i18n.js";
1
2
  const FRAMEWORKS = [
2
3
  ["actix-web", "actix-web"],
3
4
  ["axum", "axum"],
@@ -23,39 +24,44 @@ function detect(signals) {
23
24
  packageManager: { value: "cargo", confidence: "high" },
24
25
  };
25
26
  }
26
- function rules(detection) {
27
- const framework = detection.framework?.value ?? "none";
28
- return {
29
- summary: `Proyecto Rust${framework !== "none" ? ` con ${framework}` : ""} (cargo).`,
30
- conventions: [
31
- "Sigue el formato estándar de `rustfmt`; corre `cargo clippy` para detectar problemas idiomáticos.",
32
- "Ejecuta los tests con `cargo test` antes de terminar una tarea.",
33
- "Evita `unwrap()`/`expect()` en código de producción salvo que la invariante esté justificada.",
34
- ],
35
- architectureNotes: [
27
+ const TEXTS = {
28
+ es: {
29
+ style: "Sigue el formato estándar de `rustfmt`; corre `cargo clippy` para detectar problemas idiomáticos.",
30
+ unwrap: "Evita `unwrap()`/`expect()` en código de producción salvo que la invariante esté justificada.",
31
+ arch: [
36
32
  "Mantén los módulos con una responsabilidad clara; usa `mod.rs` o archivos de módulo con nombre explícito según lo que ya use el repo.",
37
33
  "Declara toda dependencia nueva en Cargo.toml y mantén Cargo.lock sincronizado.",
38
34
  ],
35
+ reviewFocus: "usos innecesarios de `unwrap()`/`clone()`",
36
+ refactorExtra: "Respeta el sistema de ownership existente.",
37
+ },
38
+ en: {
39
+ style: "Follow the standard `rustfmt` formatting; run `cargo clippy` to catch idiomatic issues.",
40
+ unwrap: "Avoid `unwrap()`/`expect()` in production code unless the invariant is justified.",
41
+ arch: [
42
+ "Keep modules single-purpose; use `mod.rs` or explicitly named module files following what the repo already uses.",
43
+ "Declare every new dependency in Cargo.toml and keep Cargo.lock in sync.",
44
+ ],
45
+ reviewFocus: "unnecessary uses of `unwrap()`/`clone()`",
46
+ refactorExtra: "Respect the existing ownership model.",
47
+ },
48
+ };
49
+ function rules(detection, lang) {
50
+ const t = TEXTS[lang];
51
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
52
+ return {
53
+ summary: summarySentence(lang, "Rust", framework, "cargo"),
54
+ conventions: [t.style, runTestsConvention(lang, "`cargo test`"), t.unwrap],
55
+ architectureNotes: t.arch,
39
56
  };
40
57
  }
41
- function promptTemplates(detection) {
42
- const framework = detection.framework?.value ?? "el framework del proyecto";
58
+ function promptTemplates(detection, lang) {
59
+ const t = TEXTS[lang];
60
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
43
61
  return [
44
- {
45
- id: "review",
46
- title: "Code Review (Rust)",
47
- body: `Revisa el diff actual buscando bugs, usos innecesarios de \`unwrap()\`/\`clone()\` y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
48
- },
49
- {
50
- id: "refactor",
51
- title: "Refactor (Rust)",
52
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable. Respeta el sistema de ownership existente.`,
53
- },
54
- {
55
- id: "testing",
56
- title: "Testing (Rust)",
57
- body: `Escribe tests con \`cargo test\` para el código señalado. Cubre el camino feliz y al menos un caso límite.`,
58
- },
62
+ { id: "review", title: "Code Review (Rust)", body: reviewBody(lang, t.reviewFocus, framework) },
63
+ { id: "refactor", title: "Refactor (Rust)", body: refactorBody(lang, t.refactorExtra) },
64
+ { id: "testing", title: "Testing (Rust)", body: testingBody(lang, "`cargo test`") },
59
65
  ];
60
66
  }
61
67
  export const rustPack = { id: "rust", detect, rules, promptTemplates };
@@ -1,11 +1,14 @@
1
+ import { refactorBody, reviewBody, runTestsConvention, summarySentence, testingBody, unknownFrameworkLabel, unknownRunnerLabel, } from "../core/i18n.js";
1
2
  const FRAMEWORKS = [
2
3
  ["playframework", "play"],
3
4
  ["akka-http", "akka"],
4
5
  ["akka.actor", "akka"],
6
+ ["scalatra", "scalatra"],
5
7
  ];
6
8
  const TEST_RUNNERS = [
7
9
  ["scalatest", "scalatest"],
8
10
  ["munit", "munit"],
11
+ ["specs2", "specs2"],
9
12
  ];
10
13
  function detect(signals) {
11
14
  const source = signals.buildSbt;
@@ -34,39 +37,43 @@ function detect(signals) {
34
37
  packageManager: { value: "sbt", confidence: "high" },
35
38
  };
36
39
  }
37
- function rules(detection) {
38
- const framework = detection.framework?.value ?? "none";
39
- return {
40
- summary: `Proyecto Scala${framework !== "none" ? ` con ${framework}` : ""} (sbt).`,
41
- conventions: [
42
- "Sigue la guía de estilo oficial de Scala; ejecuta `scalafmt` si el proyecto ya lo usa.",
43
- `Ejecuta los tests con \`sbt test\` antes de terminar una tarea.`,
44
- "Prefiere estructuras inmutables y funciones puras cuando el proyecto ya siga ese estilo.",
45
- ],
46
- architectureNotes: [
40
+ const TEXTS = {
41
+ es: {
42
+ style: "Sigue la guía de estilo oficial de Scala; ejecuta `scalafmt` si el proyecto ya lo usa.",
43
+ immutability: "Prefiere estructuras inmutables y funciones puras cuando el proyecto ya siga ese estilo.",
44
+ arch: [
47
45
  "Mantén los módulos con una responsabilidad clara; usa `case class` para modelos de datos.",
48
46
  "Declara toda dependencia nueva en `build.sbt`.",
49
47
  ],
48
+ reviewFocus: "usos innecesarios de mutabilidad",
49
+ },
50
+ en: {
51
+ style: "Follow the official Scala style guide; run `scalafmt` if the project already uses it.",
52
+ immutability: "Prefer immutable structures and pure functions when the project already follows that style.",
53
+ arch: [
54
+ "Keep modules single-purpose; use `case class` for data models.",
55
+ "Declare every new dependency in `build.sbt`.",
56
+ ],
57
+ reviewFocus: "unnecessary mutability",
58
+ },
59
+ };
60
+ function rules(detection, lang) {
61
+ const t = TEXTS[lang];
62
+ const framework = detection.framework?.value !== "none" ? detection.framework?.value : undefined;
63
+ return {
64
+ summary: summarySentence(lang, "Scala", framework, "sbt"),
65
+ conventions: [t.style, runTestsConvention(lang, "`sbt test`"), t.immutability],
66
+ architectureNotes: t.arch,
50
67
  };
51
68
  }
52
- function promptTemplates(detection) {
53
- const framework = detection.framework?.value ?? "el framework del proyecto";
69
+ function promptTemplates(detection, lang) {
70
+ const t = TEXTS[lang];
71
+ const framework = detection.framework?.value !== "none" ? detection.framework.value : unknownFrameworkLabel(lang);
72
+ const runner = detection.testRunner?.value !== "unknown" ? detection.testRunner.value : unknownRunnerLabel(lang);
54
73
  return [
55
- {
56
- id: "review",
57
- title: "Code Review (Scala)",
58
- body: `Revisa el diff actual buscando bugs, usos innecesarios de mutabilidad y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
59
- },
60
- {
61
- id: "refactor",
62
- title: "Refactor (Scala)",
63
- body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
64
- },
65
- {
66
- id: "testing",
67
- title: "Testing (Scala)",
68
- 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.`,
69
- },
74
+ { id: "review", title: "Code Review (Scala)", body: reviewBody(lang, t.reviewFocus, framework) },
75
+ { id: "refactor", title: "Refactor (Scala)", body: refactorBody(lang) },
76
+ { id: "testing", title: "Testing (Scala)", body: testingBody(lang, runner) },
70
77
  ];
71
78
  }
72
79
  export const scalaPack = { id: "scala", detect, rules, promptTemplates };