agent-rules-init 0.1.3 → 0.2.1
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/dist/bin.d.ts +2 -0
- package/dist/bin.js +3 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +33 -12
- package/dist/core/llm-bridge.d.ts +1 -1
- package/dist/core/llm-bridge.js +8 -6
- package/dist/core/scanner.js +103 -9
- package/dist/core/types.d.ts +67 -1
- package/dist/core/types.js +1 -1
- package/dist/packs/cpp.d.ts +2 -0
- package/dist/packs/cpp.js +84 -0
- package/dist/packs/csharp.d.ts +2 -0
- package/dist/packs/csharp.js +59 -0
- package/dist/packs/dart.d.ts +2 -0
- package/dist/packs/dart.js +61 -0
- package/dist/packs/elixir.d.ts +2 -0
- package/dist/packs/elixir.js +56 -0
- package/dist/packs/go.d.ts +2 -0
- package/dist/packs/go.js +74 -0
- package/dist/packs/java.d.ts +2 -0
- package/dist/packs/java.js +59 -0
- package/dist/packs/js-ts.d.ts +2 -0
- package/dist/packs/js-ts.js +101 -0
- package/dist/packs/kotlin.d.ts +2 -0
- package/dist/packs/kotlin.js +65 -0
- package/dist/packs/php.d.ts +2 -0
- package/dist/packs/php.js +63 -0
- package/dist/packs/python.d.ts +2 -0
- package/dist/packs/python.js +88 -0
- package/dist/packs/r.d.ts +2 -0
- package/dist/packs/r.js +60 -0
- package/dist/packs/ruby.d.ts +2 -0
- package/dist/packs/ruby.js +59 -0
- package/dist/packs/rust.d.ts +2 -0
- package/dist/packs/rust.js +61 -0
- package/dist/packs/scala.d.ts +2 -0
- package/dist/packs/scala.js +74 -0
- package/dist/packs/swift.d.ts +2 -0
- package/dist/packs/swift.js +58 -0
- package/package.json +3 -8
- package/dist/core/greeting.d.ts +0 -1
- package/dist/core/greeting.js +0 -9
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import { type WriteResult } from "./core/writer.js";
|
|
3
2
|
import { type PromptFn } from "./core/prompt-engine.js";
|
|
4
3
|
import { type ExecFn } from "./core/llm-bridge.js";
|
|
@@ -8,3 +7,4 @@ export interface RunCliOptions {
|
|
|
8
7
|
skipLlm?: boolean;
|
|
9
8
|
}
|
|
10
9
|
export declare function runCli(rootPath: string, options?: RunCliOptions): Promise<WriteResult[]>;
|
|
10
|
+
export declare function main(): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import * as clack from "@clack/prompts";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
2
|
import { scanRepo } from "./core/scanner.js";
|
|
5
3
|
import { writeGeneratedFiles } from "./core/writer.js";
|
|
6
4
|
import { renderClaudeMd, renderAgentsMd, renderCopilotInstructions, renderPromptFiles, } from "./core/templates.js";
|
|
7
5
|
import { collectLowConfidenceQuestions, askQuestions, applyAnswers, defaultPromptFn, hasInteractiveTty, } from "./core/prompt-engine.js";
|
|
8
6
|
import { detectAvailableAssistants, polishWithAssistant, defaultExecFn } from "./core/llm-bridge.js";
|
|
9
|
-
import { jsTsPack } from "
|
|
10
|
-
import { pythonPack } from "
|
|
11
|
-
import { javaPack } from "
|
|
12
|
-
import { phpPack } from "
|
|
13
|
-
|
|
7
|
+
import { jsTsPack } from "./packs/js-ts.js";
|
|
8
|
+
import { pythonPack } from "./packs/python.js";
|
|
9
|
+
import { javaPack } from "./packs/java.js";
|
|
10
|
+
import { phpPack } from "./packs/php.js";
|
|
11
|
+
import { rubyPack } from "./packs/ruby.js";
|
|
12
|
+
import { goPack } from "./packs/go.js";
|
|
13
|
+
import { rustPack } from "./packs/rust.js";
|
|
14
|
+
import { csharpPack } from "./packs/csharp.js";
|
|
15
|
+
import { kotlinPack } from "./packs/kotlin.js";
|
|
16
|
+
import { swiftPack } from "./packs/swift.js";
|
|
17
|
+
import { dartPack } from "./packs/dart.js";
|
|
18
|
+
import { cppPack } from "./packs/cpp.js";
|
|
19
|
+
import { elixirPack } from "./packs/elixir.js";
|
|
20
|
+
import { scalaPack } from "./packs/scala.js";
|
|
21
|
+
import { rPack } from "./packs/r.js";
|
|
22
|
+
const ALL_PACKS = [
|
|
23
|
+
jsTsPack,
|
|
24
|
+
pythonPack,
|
|
25
|
+
javaPack,
|
|
26
|
+
phpPack,
|
|
27
|
+
rubyPack,
|
|
28
|
+
goPack,
|
|
29
|
+
rustPack,
|
|
30
|
+
csharpPack,
|
|
31
|
+
kotlinPack,
|
|
32
|
+
swiftPack,
|
|
33
|
+
dartPack,
|
|
34
|
+
cppPack,
|
|
35
|
+
elixirPack,
|
|
36
|
+
scalaPack,
|
|
37
|
+
rPack,
|
|
38
|
+
];
|
|
14
39
|
export async function runCli(rootPath, options = {}) {
|
|
15
40
|
const promptFn = options.promptFn ?? defaultPromptFn;
|
|
16
41
|
const execFn = options.execFn ?? defaultExecFn;
|
|
@@ -61,7 +86,7 @@ export async function runCli(rootPath, options = {}) {
|
|
|
61
86
|
}
|
|
62
87
|
return writeGeneratedFiles(rootPath, files);
|
|
63
88
|
}
|
|
64
|
-
async function main() {
|
|
89
|
+
export async function main() {
|
|
65
90
|
clack.intro("agent-rules-init");
|
|
66
91
|
if (!hasInteractiveTty()) {
|
|
67
92
|
console.warn("No se detectó una terminal interactiva (esto pasa a veces en Git Bash en Windows). " +
|
|
@@ -96,7 +121,3 @@ async function main() {
|
|
|
96
121
|
process.exitCode = 1;
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
|
-
const isMainModule = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
100
|
-
if (isMainModule) {
|
|
101
|
-
main();
|
|
102
|
-
}
|
|
@@ -3,7 +3,7 @@ export interface ExecResult {
|
|
|
3
3
|
stdout: string;
|
|
4
4
|
exitCode: number;
|
|
5
5
|
}
|
|
6
|
-
export type ExecFn = (command: string, args: string[]) => Promise<ExecResult>;
|
|
6
|
+
export type ExecFn = (command: string, args: string[], stdin?: string) => Promise<ExecResult>;
|
|
7
7
|
export declare const defaultExecFn: ExecFn;
|
|
8
8
|
export declare function detectAvailableAssistants(execFn?: ExecFn): Promise<AssistantId[]>;
|
|
9
9
|
export declare function polishWithAssistant(assistant: AssistantId, content: string, execFn?: ExecFn): Promise<string>;
|
package/dist/core/llm-bridge.js
CHANGED
|
@@ -3,11 +3,8 @@ const VERSION_ARGS = {
|
|
|
3
3
|
claude: ["--version"],
|
|
4
4
|
codex: ["--version"],
|
|
5
5
|
};
|
|
6
|
-
export const defaultExecFn = (command, args) => new Promise((resolve, reject) => {
|
|
6
|
+
export const defaultExecFn = (command, args, stdin) => new Promise((resolve, reject) => {
|
|
7
7
|
// shell:true is only needed on Windows, to resolve npm-installed .cmd/.ps1 shims.
|
|
8
|
-
// On POSIX, spawn(command, args) execs the binary directly with argv, so passing
|
|
9
|
-
// large generated content as an argument (see polishWithAssistant) can never be
|
|
10
|
-
// reinterpreted by a shell there.
|
|
11
8
|
const child = spawn(command, args, { shell: process.platform === "win32" });
|
|
12
9
|
let stdout = "";
|
|
13
10
|
child.stdout?.on("data", (chunk) => (stdout += chunk.toString()));
|
|
@@ -18,6 +15,11 @@ export const defaultExecFn = (command, args) => new Promise((resolve, reject) =>
|
|
|
18
15
|
else
|
|
19
16
|
reject(new Error(`${command} exited with code ${exitCode}`));
|
|
20
17
|
});
|
|
18
|
+
// Content always goes through stdin, never as a CLI argument: on Windows, spawn's
|
|
19
|
+
// shell:true routes the command through cmd.exe, which cannot reliably carry a
|
|
20
|
+
// multi-line, multi-KB argument (embedded newlines truncate/corrupt it). Piping
|
|
21
|
+
// via stdin has no such limit and needs no shell-quoting at all.
|
|
22
|
+
child.stdin?.end(stdin ?? "");
|
|
21
23
|
});
|
|
22
24
|
export async function detectAvailableAssistants(execFn = defaultExecFn) {
|
|
23
25
|
const candidates = ["claude", "codex"];
|
|
@@ -33,9 +35,9 @@ export async function detectAvailableAssistants(execFn = defaultExecFn) {
|
|
|
33
35
|
return results.filter((id) => id !== null);
|
|
34
36
|
}
|
|
35
37
|
export async function polishWithAssistant(assistant, content, execFn = defaultExecFn) {
|
|
36
|
-
const prompt = `Pule la redacción del siguiente documento de instrucciones para un agente de IA, sin cambiar su significado ni estructura:\n\n${content}`;
|
|
38
|
+
const prompt = `Pule la redacción del siguiente documento de instrucciones para un agente de IA, sin cambiar su significado ni estructura. Devuelve únicamente el documento pulido, sin comentarios ni explicaciones adicionales:\n\n${content}`;
|
|
37
39
|
try {
|
|
38
|
-
const result = await execFn(assistant, ["-p", prompt
|
|
40
|
+
const result = await execFn(assistant, ["-p"], prompt);
|
|
39
41
|
return result.stdout.trim() || content;
|
|
40
42
|
}
|
|
41
43
|
catch (err) {
|
package/dist/core/scanner.js
CHANGED
|
@@ -48,11 +48,54 @@ function readTextIfExists(filePath) {
|
|
|
48
48
|
return undefined;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
function
|
|
52
|
-
const matches = files.filter((f) => path.basename(f) === fileName);
|
|
51
|
+
function shallowest(matches) {
|
|
53
52
|
if (matches.length === 0)
|
|
54
53
|
return undefined;
|
|
55
|
-
return matches.reduce((
|
|
54
|
+
return matches.reduce((shallowestSoFar, candidate) => candidate.split(path.sep).length < shallowestSoFar.split(path.sep).length ? candidate : shallowestSoFar);
|
|
55
|
+
}
|
|
56
|
+
function findFirst(files, fileName) {
|
|
57
|
+
return shallowest(files.filter((f) => path.basename(f) === fileName));
|
|
58
|
+
}
|
|
59
|
+
// Directories that routinely carry their own throwaway Python tooling manifest (a
|
|
60
|
+
// mkdocs/sphinx docs build, a one-off script, a benchmark harness) in projects whose
|
|
61
|
+
// primary language is something else entirely (e.g. nlohmann/json, a C++ library, ships
|
|
62
|
+
// docs/mkdocs/requirements.txt). Prefer a candidate outside these dirs when one exists.
|
|
63
|
+
const NON_PROJECT_DIRS = new Set([
|
|
64
|
+
"docs",
|
|
65
|
+
"doc",
|
|
66
|
+
"tools",
|
|
67
|
+
"tool",
|
|
68
|
+
"scripts",
|
|
69
|
+
"script",
|
|
70
|
+
"examples",
|
|
71
|
+
"example",
|
|
72
|
+
"benchmark",
|
|
73
|
+
"benchmarks",
|
|
74
|
+
]);
|
|
75
|
+
function isUnderNonProjectDir(relativePath) {
|
|
76
|
+
const segments = relativePath.split(path.sep).slice(0, -1);
|
|
77
|
+
return segments.some((segment) => NON_PROJECT_DIRS.has(segment.toLowerCase()));
|
|
78
|
+
}
|
|
79
|
+
function findFirstPreferringRealProjectDirs(files, fileName) {
|
|
80
|
+
const matches = files.filter((f) => path.basename(f) === fileName && !isUnderNonProjectDir(f));
|
|
81
|
+
return shallowest(matches);
|
|
82
|
+
}
|
|
83
|
+
function findAllByExtension(files, extension) {
|
|
84
|
+
return files.filter((f) => f.toLowerCase().endsWith(extension));
|
|
85
|
+
}
|
|
86
|
+
function findAllByNames(files, names) {
|
|
87
|
+
return files.filter((f) => names.includes(path.basename(f)));
|
|
88
|
+
}
|
|
89
|
+
function readAllConcatenated(rootPath, relativePaths) {
|
|
90
|
+
if (relativePaths.length === 0)
|
|
91
|
+
return undefined;
|
|
92
|
+
const contents = relativePaths
|
|
93
|
+
.map((p) => readTextIfExists(path.join(rootPath, p)))
|
|
94
|
+
.filter((content) => content !== undefined);
|
|
95
|
+
return contents.length > 0 ? contents.join("\n") : undefined;
|
|
96
|
+
}
|
|
97
|
+
function pickShallowest(paths) {
|
|
98
|
+
return shallowest(paths.filter((p) => p !== undefined));
|
|
56
99
|
}
|
|
57
100
|
export function scanRepo(rootPath) {
|
|
58
101
|
const files = walk(rootPath);
|
|
@@ -67,6 +110,7 @@ export function scanRepo(rootPath) {
|
|
|
67
110
|
dependencies: rawPackageJson.dependencies ?? {},
|
|
68
111
|
devDependencies: rawPackageJson.devDependencies ?? {},
|
|
69
112
|
scripts: rawPackageJson.scripts ?? {},
|
|
113
|
+
moduleType: rawPackageJson.type === "module" ? "module" : "commonjs",
|
|
70
114
|
}
|
|
71
115
|
: undefined;
|
|
72
116
|
const composerJsonPath = findFirst(files, "composer.json");
|
|
@@ -79,10 +123,45 @@ export function scanRepo(rootPath) {
|
|
|
79
123
|
requireDev: rawComposerJson["require-dev"] ?? {},
|
|
80
124
|
}
|
|
81
125
|
: undefined;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
126
|
+
const pyprojectCandidate = findFirstPreferringRealProjectDirs(files, "pyproject.toml");
|
|
127
|
+
const requirementsCandidate = findFirstPreferringRealProjectDirs(files, "requirements.txt");
|
|
128
|
+
const environmentYmlCandidate = findFirstPreferringRealProjectDirs(files, "environment.yml") ??
|
|
129
|
+
findFirstPreferringRealProjectDirs(files, "environment.yaml");
|
|
130
|
+
// Only the shallowest of the three "wins" as the project's Python manifest — otherwise an
|
|
131
|
+
// unrelated pyproject.toml nested inside a vendored/data subdirectory (e.g. a bundled dataset
|
|
132
|
+
// package) would always outrank a real root-level environment.yml just by file type.
|
|
133
|
+
const primaryPythonManifest = pickShallowest([
|
|
134
|
+
pyprojectCandidate,
|
|
135
|
+
requirementsCandidate,
|
|
136
|
+
environmentYmlCandidate,
|
|
137
|
+
]);
|
|
138
|
+
const pyprojectPath = primaryPythonManifest === pyprojectCandidate ? pyprojectCandidate : undefined;
|
|
139
|
+
const requirementsPath = primaryPythonManifest === requirementsCandidate ? requirementsCandidate : undefined;
|
|
140
|
+
const environmentYmlPath = primaryPythonManifest === environmentYmlCandidate ? environmentYmlCandidate : undefined;
|
|
141
|
+
// Multi-module Maven/Gradle projects (parent + child modules) each have their own
|
|
142
|
+
// pom.xml/build.gradle, and the module that actually declares the framework/Kotlin
|
|
143
|
+
// plugin/test runner isn't necessarily the shallowest one. Aggregate all of them.
|
|
144
|
+
const pomPaths = findAllByNames(files, ["pom.xml"]);
|
|
145
|
+
const buildGradlePaths = findAllByNames(files, ["build.gradle", "build.gradle.kts"]);
|
|
146
|
+
const gemfilePath = findFirst(files, "Gemfile");
|
|
147
|
+
const goModPath = findFirst(files, "go.mod");
|
|
148
|
+
const cargoTomlPath = findFirst(files, "Cargo.toml");
|
|
149
|
+
// .NET solutions routinely split into several .csproj files (domain/infra/web/tests);
|
|
150
|
+
// picking just the "shallowest" one is close to arbitrary and often lands on a plain
|
|
151
|
+
// class library that has neither the web framework nor the test runner reference.
|
|
152
|
+
// Concatenate all of them so detection can find those references wherever they live.
|
|
153
|
+
const csprojPaths = findAllByExtension(files, ".csproj");
|
|
154
|
+
const packageSwiftPath = findFirst(files, "Package.swift");
|
|
155
|
+
// Melos/pub workspaces have a root pubspec.yaml that's just workspace glue (no
|
|
156
|
+
// `flutter`/`flutter_test` dependency) plus one real pubspec.yaml per package under
|
|
157
|
+
// packages/*. Aggregate all of them so the actual framework/test runner is found.
|
|
158
|
+
const pubspecYamlPaths = findAllByNames(files, ["pubspec.yaml"]);
|
|
159
|
+
const cmakeListsPath = findFirst(files, "CMakeLists.txt");
|
|
160
|
+
const makefilePath = findFirst(files, "Makefile") ?? findFirst(files, "makefile");
|
|
161
|
+
const mixExsPath = findFirst(files, "mix.exs");
|
|
162
|
+
const buildSbtPath = findFirst(files, "build.sbt");
|
|
163
|
+
const rDescriptionPath = findFirst(files, "DESCRIPTION");
|
|
164
|
+
const renvLockPath = findFirst(files, "renv.lock");
|
|
86
165
|
return {
|
|
87
166
|
rootPath,
|
|
88
167
|
files,
|
|
@@ -94,8 +173,23 @@ export function scanRepo(rootPath) {
|
|
|
94
173
|
requirementsTxt: requirementsPath
|
|
95
174
|
? readTextIfExists(path.join(rootPath, requirementsPath))
|
|
96
175
|
: undefined,
|
|
97
|
-
|
|
98
|
-
|
|
176
|
+
environmentYml: environmentYmlPath
|
|
177
|
+
? readTextIfExists(path.join(rootPath, environmentYmlPath))
|
|
178
|
+
: undefined,
|
|
179
|
+
pomXml: readAllConcatenated(rootPath, pomPaths),
|
|
180
|
+
buildGradle: readAllConcatenated(rootPath, buildGradlePaths),
|
|
99
181
|
composerJson,
|
|
182
|
+
gemfile: gemfilePath ? readTextIfExists(path.join(rootPath, gemfilePath)) : undefined,
|
|
183
|
+
goMod: goModPath ? readTextIfExists(path.join(rootPath, goModPath)) : undefined,
|
|
184
|
+
cargoToml: cargoTomlPath ? readTextIfExists(path.join(rootPath, cargoTomlPath)) : undefined,
|
|
185
|
+
csproj: readAllConcatenated(rootPath, csprojPaths),
|
|
186
|
+
packageSwift: packageSwiftPath ? readTextIfExists(path.join(rootPath, packageSwiftPath)) : undefined,
|
|
187
|
+
pubspecYaml: readAllConcatenated(rootPath, pubspecYamlPaths),
|
|
188
|
+
cmakeLists: cmakeListsPath ? readTextIfExists(path.join(rootPath, cmakeListsPath)) : undefined,
|
|
189
|
+
makefile: makefilePath ? readTextIfExists(path.join(rootPath, makefilePath)) : undefined,
|
|
190
|
+
mixExs: mixExsPath ? readTextIfExists(path.join(rootPath, mixExsPath)) : undefined,
|
|
191
|
+
buildSbt: buildSbtPath ? readTextIfExists(path.join(rootPath, buildSbtPath)) : undefined,
|
|
192
|
+
rDescription: rDescriptionPath ? readTextIfExists(path.join(rootPath, rDescriptionPath)) : undefined,
|
|
193
|
+
renvLock: renvLockPath ? readTextIfExists(path.join(rootPath, renvLockPath)) : undefined,
|
|
100
194
|
};
|
|
101
195
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1 +1,67 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface PackageJsonManifest {
|
|
2
|
+
name?: string;
|
|
3
|
+
dependencies: Record<string, string>;
|
|
4
|
+
devDependencies: Record<string, string>;
|
|
5
|
+
scripts: Record<string, string>;
|
|
6
|
+
moduleType: "module" | "commonjs";
|
|
7
|
+
}
|
|
8
|
+
export interface ComposerJsonManifest {
|
|
9
|
+
require: Record<string, string>;
|
|
10
|
+
requireDev: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface RepoSignals {
|
|
13
|
+
rootPath: string;
|
|
14
|
+
files: string[];
|
|
15
|
+
hasFile: (relativePath: string) => boolean;
|
|
16
|
+
hasDir: (relativeDir: string) => boolean;
|
|
17
|
+
packageJson?: PackageJsonManifest;
|
|
18
|
+
pyprojectToml?: string;
|
|
19
|
+
requirementsTxt?: string;
|
|
20
|
+
environmentYml?: string;
|
|
21
|
+
pomXml?: string;
|
|
22
|
+
buildGradle?: string;
|
|
23
|
+
composerJson?: ComposerJsonManifest;
|
|
24
|
+
gemfile?: string;
|
|
25
|
+
goMod?: string;
|
|
26
|
+
cargoToml?: string;
|
|
27
|
+
csproj?: string;
|
|
28
|
+
packageSwift?: string;
|
|
29
|
+
pubspecYaml?: string;
|
|
30
|
+
cmakeLists?: string;
|
|
31
|
+
makefile?: string;
|
|
32
|
+
mixExs?: string;
|
|
33
|
+
buildSbt?: string;
|
|
34
|
+
rDescription?: string;
|
|
35
|
+
renvLock?: string;
|
|
36
|
+
}
|
|
37
|
+
export type Confidence = "high" | "low";
|
|
38
|
+
export interface DetectionField<T> {
|
|
39
|
+
value: T;
|
|
40
|
+
confidence: Confidence;
|
|
41
|
+
}
|
|
42
|
+
export interface DetectionResult {
|
|
43
|
+
packId: string;
|
|
44
|
+
language: string;
|
|
45
|
+
framework?: DetectionField<string>;
|
|
46
|
+
packageManager?: DetectionField<string>;
|
|
47
|
+
testRunner?: DetectionField<string>;
|
|
48
|
+
linter?: DetectionField<string>;
|
|
49
|
+
usesTypeScript?: boolean;
|
|
50
|
+
moduleFormat?: "module" | "commonjs";
|
|
51
|
+
}
|
|
52
|
+
export interface RuleSet {
|
|
53
|
+
summary: string;
|
|
54
|
+
conventions: string[];
|
|
55
|
+
architectureNotes: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface PromptTemplate {
|
|
58
|
+
id: "review" | "refactor" | "testing";
|
|
59
|
+
title: string;
|
|
60
|
+
body: string;
|
|
61
|
+
}
|
|
62
|
+
export interface Pack {
|
|
63
|
+
id: string;
|
|
64
|
+
detect(signals: RepoSignals): DetectionResult | null;
|
|
65
|
+
rules(detection: DetectionResult): RuleSet;
|
|
66
|
+
promptTemplates(detection: DetectionResult): PromptTemplate[];
|
|
67
|
+
}
|
package/dist/core/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const FRAMEWORKS = [
|
|
2
|
+
["find_package(qt", "qt"],
|
|
3
|
+
["find_package(boost", "boost"],
|
|
4
|
+
["find_package(sdl2", "sdl2"],
|
|
5
|
+
];
|
|
6
|
+
const TEST_RUNNERS = [
|
|
7
|
+
["gtest", "gtest"],
|
|
8
|
+
["catch2", "catch2"],
|
|
9
|
+
["doctest", "doctest"],
|
|
10
|
+
];
|
|
11
|
+
const CPP_COMPILER_TOKENS = ["$(cc)", "$(cxx)", "gcc", "g++", "clang"];
|
|
12
|
+
// Source-file extensions need a stricter boundary check than a plain substring:
|
|
13
|
+
// Go module paths hosted on the ".cc" ccTLD (e.g. "mvdan.cc/gofumpt") also contain
|
|
14
|
+
// ".cc", but it's followed by "/", never by whitespace/quote/paren/end-of-line the
|
|
15
|
+
// way a real compiled source file reference in a Makefile would be.
|
|
16
|
+
const CPP_SOURCE_EXTENSION = /\.(cpp|cxx|cc)(?=[\s:"')]|$)/im;
|
|
17
|
+
function looksLikeCppMakefile(makefile) {
|
|
18
|
+
const lower = makefile.toLowerCase();
|
|
19
|
+
return CPP_COMPILER_TOKENS.some((needle) => lower.includes(needle)) || CPP_SOURCE_EXTENSION.test(makefile) || /\.o:/.test(makefile);
|
|
20
|
+
}
|
|
21
|
+
function detect(signals) {
|
|
22
|
+
// A bare Makefile isn't C/C++-specific — plenty of Python/JS/etc. projects ship
|
|
23
|
+
// one as a generic task runner (or a Sphinx docs Makefile, like Flask's docs/Makefile).
|
|
24
|
+
// Only trust it once it actually references a C/C++ compiler or source file.
|
|
25
|
+
const makefileSource = signals.makefile && looksLikeCppMakefile(signals.makefile) ? signals.makefile : undefined;
|
|
26
|
+
const source = signals.cmakeLists ?? makefileSource;
|
|
27
|
+
if (!source)
|
|
28
|
+
return null;
|
|
29
|
+
const lower = source.toLowerCase();
|
|
30
|
+
let framework = { value: "none", confidence: "low" };
|
|
31
|
+
for (const [needle, label] of FRAMEWORKS) {
|
|
32
|
+
if (lower.includes(needle)) {
|
|
33
|
+
framework = { value: label, confidence: "high" };
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
let testRunner = { value: "unknown", confidence: "low" };
|
|
38
|
+
for (const [needle, label] of TEST_RUNNERS) {
|
|
39
|
+
if (lower.includes(needle)) {
|
|
40
|
+
testRunner = { value: label, confidence: "high" };
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const packageManager = signals.cmakeLists
|
|
45
|
+
? { value: "cmake", confidence: "high" }
|
|
46
|
+
: { value: "make", confidence: "high" };
|
|
47
|
+
return { packId: "cpp", language: "C/C++", framework, testRunner, packageManager };
|
|
48
|
+
}
|
|
49
|
+
function rules(detection) {
|
|
50
|
+
const framework = detection.framework?.value ?? "none";
|
|
51
|
+
return {
|
|
52
|
+
summary: `Proyecto C/C++${framework !== "none" ? ` con ${framework}` : ""} (${detection.packageManager?.value}).`,
|
|
53
|
+
conventions: [
|
|
54
|
+
"Sigue el estilo de formato ya usado en el proyecto (revisa si hay un `.clang-format`).",
|
|
55
|
+
`Compila y ejecuta los tests con ${detection.packageManager?.value === "cmake" ? "cmake --build . && ctest" : "make test"} antes de terminar una tarea.`,
|
|
56
|
+
"Gestiona la memoria con cuidado: prefiere RAII/smart pointers sobre `new`/`delete` manuales cuando el proyecto ya lo haga.",
|
|
57
|
+
],
|
|
58
|
+
architectureNotes: [
|
|
59
|
+
"Mantén los headers con guardas de inclusión (`#pragma once` o include guards) consistentes con el resto del proyecto.",
|
|
60
|
+
"Declara toda dependencia nueva en el sistema de build existente (CMakeLists.txt o Makefile).",
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function promptTemplates(detection) {
|
|
65
|
+
const framework = detection.framework?.value ?? "el framework del proyecto";
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
id: "review",
|
|
69
|
+
title: "Code Review (C/C++)",
|
|
70
|
+
body: `Revisa el diff actual buscando fugas de memoria, punteros colgantes y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "refactor",
|
|
74
|
+
title: "Refactor (C/C++)",
|
|
75
|
+
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "testing",
|
|
79
|
+
title: "Testing (C/C++)",
|
|
80
|
+
body: `Escribe tests con ${detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : "el framework de tests del proyecto"} para el código señalado. Cubre el camino feliz y al menos un caso límite.`,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
export const cppPack = { id: "cpp", detect, rules, promptTemplates };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function detect(signals) {
|
|
2
|
+
const source = signals.csproj;
|
|
3
|
+
if (!source)
|
|
4
|
+
return null;
|
|
5
|
+
const lower = source.toLowerCase();
|
|
6
|
+
const framework = lower.includes("microsoft.aspnetcore")
|
|
7
|
+
? { value: "aspnet-core", confidence: "high" }
|
|
8
|
+
: { value: "none", confidence: "low" };
|
|
9
|
+
const testRunner = lower.includes("xunit")
|
|
10
|
+
? { value: "xunit", confidence: "high" }
|
|
11
|
+
: lower.includes("nunit")
|
|
12
|
+
? { value: "nunit", confidence: "high" }
|
|
13
|
+
: lower.includes("mstest")
|
|
14
|
+
? { value: "mstest", confidence: "high" }
|
|
15
|
+
: { value: "unknown", confidence: "low" };
|
|
16
|
+
return {
|
|
17
|
+
packId: "csharp",
|
|
18
|
+
language: "C#",
|
|
19
|
+
framework,
|
|
20
|
+
testRunner,
|
|
21
|
+
packageManager: { value: "nuget", confidence: "high" },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function rules(detection) {
|
|
25
|
+
const framework = detection.framework?.value ?? "none";
|
|
26
|
+
return {
|
|
27
|
+
summary: `Proyecto C#/.NET${framework !== "none" ? ` con ${framework}` : ""} (NuGet).`,
|
|
28
|
+
conventions: [
|
|
29
|
+
"Sigue las convenciones de nombrado de .NET (PascalCase para clases/métodos públicos, camelCase para variables locales).",
|
|
30
|
+
`Ejecuta los tests con \`dotnet test\` antes de terminar una tarea.`,
|
|
31
|
+
"Declara toda dependencia nueva vía NuGet en el .csproj, nunca la añadas manualmente sin registrarla.",
|
|
32
|
+
],
|
|
33
|
+
architectureNotes: [
|
|
34
|
+
"Respeta la separación en capas (controller/service/repository) si el proyecto ya la usa.",
|
|
35
|
+
"Prefiere inyección de dependencias sobre instanciación manual cuando el framework ya la ofrezca.",
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function promptTemplates(detection) {
|
|
40
|
+
const framework = detection.framework?.value ?? "el framework del proyecto";
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
id: "review",
|
|
44
|
+
title: "Code Review (C#/.NET)",
|
|
45
|
+
body: `Revisa el diff actual buscando bugs, null-safety 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 (C#/.NET)",
|
|
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 (C#/.NET)",
|
|
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
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
export const csharpPack = { id: "csharp", detect, rules, promptTemplates };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
function detect(signals) {
|
|
2
|
+
const source = signals.pubspecYaml;
|
|
3
|
+
if (!source)
|
|
4
|
+
return null;
|
|
5
|
+
const lower = source.toLowerCase();
|
|
6
|
+
const framework = /^\s*flutter\s*:/m.test(lower)
|
|
7
|
+
? { value: "flutter", confidence: "high" }
|
|
8
|
+
: lower.includes("shelf")
|
|
9
|
+
? { value: "shelf", confidence: "high" }
|
|
10
|
+
: { value: "none", confidence: "low" };
|
|
11
|
+
const testRunner = lower.includes("flutter_test")
|
|
12
|
+
? { value: "flutter test", confidence: "high" }
|
|
13
|
+
: /^\s*test\s*:/m.test(lower)
|
|
14
|
+
? { value: "dart test", confidence: "high" }
|
|
15
|
+
: { value: "unknown", confidence: "low" };
|
|
16
|
+
return {
|
|
17
|
+
packId: "dart",
|
|
18
|
+
language: "Dart",
|
|
19
|
+
framework,
|
|
20
|
+
testRunner,
|
|
21
|
+
packageManager: { value: "pub", confidence: "high" },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function rules(detection) {
|
|
25
|
+
const framework = detection.framework?.value ?? "none";
|
|
26
|
+
return {
|
|
27
|
+
summary: `Proyecto Dart${framework !== "none" ? ` con ${framework}` : ""} (pub).`,
|
|
28
|
+
conventions: [
|
|
29
|
+
"Sigue la guía de estilo oficial de Dart (dart.dev/effective-dart/style).",
|
|
30
|
+
`Ejecuta los tests con ${detection.testRunner?.value !== "unknown" ? detection.testRunner?.value : "el test runner del proyecto"} antes de terminar una tarea.`,
|
|
31
|
+
"Declara toda dependencia nueva en pubspec.yaml, nunca la instales sin registrarla.",
|
|
32
|
+
],
|
|
33
|
+
architectureNotes: [
|
|
34
|
+
framework === "flutter"
|
|
35
|
+
? "Separa la lógica de negocio de los widgets cuando el proyecto ya siga ese patrón (p. ej. BLoC/Provider/Riverpod)."
|
|
36
|
+
: "Mantén los módulos con una responsabilidad clara.",
|
|
37
|
+
"Prefiere `final`/`const` sobre variables mutables cuando sea posible.",
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function promptTemplates(detection) {
|
|
42
|
+
const framework = detection.framework?.value ?? "el framework del proyecto";
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
id: "review",
|
|
46
|
+
title: "Code Review (Dart)",
|
|
47
|
+
body: `Revisa el diff actual buscando bugs 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 (Dart)",
|
|
52
|
+
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "testing",
|
|
56
|
+
title: "Testing (Dart)",
|
|
57
|
+
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.`,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
export const dartPack = { id: "dart", detect, rules, promptTemplates };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function detect(signals) {
|
|
2
|
+
const source = signals.mixExs;
|
|
3
|
+
if (!source)
|
|
4
|
+
return null;
|
|
5
|
+
const lower = source.toLowerCase();
|
|
6
|
+
// A plain `.includes(":phoenix")` also matches sibling hex packages like `:phoenix_pubsub`
|
|
7
|
+
// or `:phoenix_html` (substring prefix), and Phoenix's own mix.exs (`app: :phoenix`) —
|
|
8
|
+
// neither means the project actually depends on the `phoenix` package. Match the
|
|
9
|
+
// `{:phoenix, ...}` dependency-tuple syntax instead, which only the real dependency uses.
|
|
10
|
+
const framework = /\{\s*:phoenix\s*,/.test(lower)
|
|
11
|
+
? { value: "phoenix", confidence: "high" }
|
|
12
|
+
: { value: "none", confidence: "low" };
|
|
13
|
+
return {
|
|
14
|
+
packId: "elixir",
|
|
15
|
+
language: "Elixir",
|
|
16
|
+
framework,
|
|
17
|
+
testRunner: { value: "mix test", confidence: "high" },
|
|
18
|
+
packageManager: { value: "mix", confidence: "high" },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function rules(detection) {
|
|
22
|
+
const framework = detection.framework?.value ?? "none";
|
|
23
|
+
return {
|
|
24
|
+
summary: `Proyecto Elixir${framework !== "none" ? ` con ${framework}` : ""} (mix).`,
|
|
25
|
+
conventions: [
|
|
26
|
+
"Sigue la guía de estilo oficial de Elixir; ejecuta `mix format` antes de terminar una tarea.",
|
|
27
|
+
"Ejecuta los tests con `mix test` antes de terminar una tarea.",
|
|
28
|
+
"Prefiere pattern matching y pipelines (`|>`) sobre anidar llamadas de función.",
|
|
29
|
+
],
|
|
30
|
+
architectureNotes: [
|
|
31
|
+
"Respeta la separación entre contextos (lógica de negocio) y la capa web si el proyecto ya usa Phoenix.",
|
|
32
|
+
"Declara toda dependencia nueva en `mix.exs` y mantén `mix.lock` sincronizado.",
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function promptTemplates(detection) {
|
|
37
|
+
const framework = detection.framework?.value ?? "el framework del proyecto";
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
id: "review",
|
|
41
|
+
title: "Code Review (Elixir)",
|
|
42
|
+
body: `Revisa el diff actual buscando bugs, procesos sin supervisar correctamente y desviaciones de las convenciones de ${framework}. Señala solo problemas concretos con línea de archivo.`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "refactor",
|
|
46
|
+
title: "Refactor (Elixir)",
|
|
47
|
+
body: `Propón refactors que reduzcan duplicación y mejoren la legibilidad sin cambiar comportamiento observable.`,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "testing",
|
|
51
|
+
title: "Testing (Elixir)",
|
|
52
|
+
body: `Escribe tests con \`mix test\` (ExUnit) para el código señalado. Cubre el camino feliz y al menos un caso límite.`,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
export const elixirPack = { id: "elixir", detect, rules, promptTemplates };
|