agent-rules-init 0.2.0 → 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/core/llm-bridge.d.ts +1 -1
- package/dist/core/llm-bridge.js +8 -6
- package/dist/core/scanner.js +60 -13
- package/dist/core/types.d.ts +3 -0
- package/dist/packs/cpp.js +15 -1
- package/dist/packs/elixir.js +5 -1
- package/dist/packs/go.js +13 -1
- package/dist/packs/java.js +5 -1
- package/dist/packs/js-ts.js +22 -7
- package/dist/packs/kotlin.js +4 -1
- package/dist/packs/python.js +22 -2
- package/dist/packs/scala.js +2 -0
- package/dist/packs/swift.js +6 -0
- package/package.json +1 -1
|
@@ -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
|
@@ -56,8 +56,43 @@ function shallowest(matches) {
|
|
|
56
56
|
function findFirst(files, fileName) {
|
|
57
57
|
return shallowest(files.filter((f) => path.basename(f) === fileName));
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
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;
|
|
61
96
|
}
|
|
62
97
|
function pickShallowest(paths) {
|
|
63
98
|
return shallowest(paths.filter((p) => p !== undefined));
|
|
@@ -75,6 +110,7 @@ export function scanRepo(rootPath) {
|
|
|
75
110
|
dependencies: rawPackageJson.dependencies ?? {},
|
|
76
111
|
devDependencies: rawPackageJson.devDependencies ?? {},
|
|
77
112
|
scripts: rawPackageJson.scripts ?? {},
|
|
113
|
+
moduleType: rawPackageJson.type === "module" ? "module" : "commonjs",
|
|
78
114
|
}
|
|
79
115
|
: undefined;
|
|
80
116
|
const composerJsonPath = findFirst(files, "composer.json");
|
|
@@ -87,9 +123,10 @@ export function scanRepo(rootPath) {
|
|
|
87
123
|
requireDev: rawComposerJson["require-dev"] ?? {},
|
|
88
124
|
}
|
|
89
125
|
: undefined;
|
|
90
|
-
const pyprojectCandidate =
|
|
91
|
-
const requirementsCandidate =
|
|
92
|
-
const environmentYmlCandidate =
|
|
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");
|
|
93
130
|
// Only the shallowest of the three "wins" as the project's Python manifest — otherwise an
|
|
94
131
|
// unrelated pyproject.toml nested inside a vendored/data subdirectory (e.g. a bundled dataset
|
|
95
132
|
// package) would always outrank a real root-level environment.yml just by file type.
|
|
@@ -101,14 +138,24 @@ export function scanRepo(rootPath) {
|
|
|
101
138
|
const pyprojectPath = primaryPythonManifest === pyprojectCandidate ? pyprojectCandidate : undefined;
|
|
102
139
|
const requirementsPath = primaryPythonManifest === requirementsCandidate ? requirementsCandidate : undefined;
|
|
103
140
|
const environmentYmlPath = primaryPythonManifest === environmentYmlCandidate ? environmentYmlCandidate : undefined;
|
|
104
|
-
|
|
105
|
-
|
|
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"]);
|
|
106
146
|
const gemfilePath = findFirst(files, "Gemfile");
|
|
107
147
|
const goModPath = findFirst(files, "go.mod");
|
|
108
148
|
const cargoTomlPath = findFirst(files, "Cargo.toml");
|
|
109
|
-
|
|
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");
|
|
110
154
|
const packageSwiftPath = findFirst(files, "Package.swift");
|
|
111
|
-
|
|
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"]);
|
|
112
159
|
const cmakeListsPath = findFirst(files, "CMakeLists.txt");
|
|
113
160
|
const makefilePath = findFirst(files, "Makefile") ?? findFirst(files, "makefile");
|
|
114
161
|
const mixExsPath = findFirst(files, "mix.exs");
|
|
@@ -129,15 +176,15 @@ export function scanRepo(rootPath) {
|
|
|
129
176
|
environmentYml: environmentYmlPath
|
|
130
177
|
? readTextIfExists(path.join(rootPath, environmentYmlPath))
|
|
131
178
|
: undefined,
|
|
132
|
-
pomXml:
|
|
133
|
-
buildGradle:
|
|
179
|
+
pomXml: readAllConcatenated(rootPath, pomPaths),
|
|
180
|
+
buildGradle: readAllConcatenated(rootPath, buildGradlePaths),
|
|
134
181
|
composerJson,
|
|
135
182
|
gemfile: gemfilePath ? readTextIfExists(path.join(rootPath, gemfilePath)) : undefined,
|
|
136
183
|
goMod: goModPath ? readTextIfExists(path.join(rootPath, goModPath)) : undefined,
|
|
137
184
|
cargoToml: cargoTomlPath ? readTextIfExists(path.join(rootPath, cargoTomlPath)) : undefined,
|
|
138
|
-
csproj:
|
|
185
|
+
csproj: readAllConcatenated(rootPath, csprojPaths),
|
|
139
186
|
packageSwift: packageSwiftPath ? readTextIfExists(path.join(rootPath, packageSwiftPath)) : undefined,
|
|
140
|
-
pubspecYaml:
|
|
187
|
+
pubspecYaml: readAllConcatenated(rootPath, pubspecYamlPaths),
|
|
141
188
|
cmakeLists: cmakeListsPath ? readTextIfExists(path.join(rootPath, cmakeListsPath)) : undefined,
|
|
142
189
|
makefile: makefilePath ? readTextIfExists(path.join(rootPath, makefilePath)) : undefined,
|
|
143
190
|
mixExs: mixExsPath ? readTextIfExists(path.join(rootPath, mixExsPath)) : undefined,
|
package/dist/core/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface PackageJsonManifest {
|
|
|
3
3
|
dependencies: Record<string, string>;
|
|
4
4
|
devDependencies: Record<string, string>;
|
|
5
5
|
scripts: Record<string, string>;
|
|
6
|
+
moduleType: "module" | "commonjs";
|
|
6
7
|
}
|
|
7
8
|
export interface ComposerJsonManifest {
|
|
8
9
|
require: Record<string, string>;
|
|
@@ -45,6 +46,8 @@ export interface DetectionResult {
|
|
|
45
46
|
packageManager?: DetectionField<string>;
|
|
46
47
|
testRunner?: DetectionField<string>;
|
|
47
48
|
linter?: DetectionField<string>;
|
|
49
|
+
usesTypeScript?: boolean;
|
|
50
|
+
moduleFormat?: "module" | "commonjs";
|
|
48
51
|
}
|
|
49
52
|
export interface RuleSet {
|
|
50
53
|
summary: string;
|
package/dist/packs/cpp.js
CHANGED
|
@@ -8,8 +8,22 @@ const TEST_RUNNERS = [
|
|
|
8
8
|
["catch2", "catch2"],
|
|
9
9
|
["doctest", "doctest"],
|
|
10
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
|
+
}
|
|
11
21
|
function detect(signals) {
|
|
12
|
-
|
|
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;
|
|
13
27
|
if (!source)
|
|
14
28
|
return null;
|
|
15
29
|
const lower = source.toLowerCase();
|
package/dist/packs/elixir.js
CHANGED
|
@@ -3,7 +3,11 @@ function detect(signals) {
|
|
|
3
3
|
if (!source)
|
|
4
4
|
return null;
|
|
5
5
|
const lower = source.toLowerCase();
|
|
6
|
-
|
|
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)
|
|
7
11
|
? { value: "phoenix", confidence: "high" }
|
|
8
12
|
: { value: "none", confidence: "low" };
|
|
9
13
|
return {
|
package/dist/packs/go.js
CHANGED
|
@@ -4,11 +4,23 @@ const FRAMEWORKS = [
|
|
|
4
4
|
["gofiber/fiber", "fiber"],
|
|
5
5
|
["go-chi/chi", "chi"],
|
|
6
6
|
];
|
|
7
|
+
// The `module` line of go.mod is the project's own import path, not a dependency —
|
|
8
|
+
// searching the whole file would false-positive on a framework whose own repo module
|
|
9
|
+
// path happens to match a known framework name (e.g. gofiber/fiber's own go.mod
|
|
10
|
+
// declares `module github.com/gofiber/fiber/v3`). Scope the search to `require` entries.
|
|
11
|
+
function extractGoRequireSection(goMod) {
|
|
12
|
+
const sections = [];
|
|
13
|
+
for (const m of goMod.matchAll(/require\s*\(([\s\S]*?)\)/g))
|
|
14
|
+
sections.push(m[1]);
|
|
15
|
+
for (const m of goMod.matchAll(/^require\s+(?!\()(.+)$/gm))
|
|
16
|
+
sections.push(m[1]);
|
|
17
|
+
return sections.length > 0 ? sections.join("\n") : goMod;
|
|
18
|
+
}
|
|
7
19
|
function detect(signals) {
|
|
8
20
|
const source = signals.goMod;
|
|
9
21
|
if (!source)
|
|
10
22
|
return null;
|
|
11
|
-
const lower = source.toLowerCase();
|
|
23
|
+
const lower = extractGoRequireSection(source).toLowerCase();
|
|
12
24
|
let framework = { value: "none", confidence: "low" };
|
|
13
25
|
for (const [needle, label] of FRAMEWORKS) {
|
|
14
26
|
if (lower.includes(needle)) {
|
package/dist/packs/java.js
CHANGED
|
@@ -4,7 +4,11 @@ function detect(signals) {
|
|
|
4
4
|
return null;
|
|
5
5
|
// A Gradle build that applies the Kotlin plugin is a Kotlin project, not Java —
|
|
6
6
|
// defer entirely to the Kotlin pack instead of also reporting a spurious Java match.
|
|
7
|
-
|
|
7
|
+
// Modern Gradle projects often declare plugins via version catalogs
|
|
8
|
+
// (`alias(libs.plugins.kotlin.android)`) rather than the literal `kotlin("jvm")` or
|
|
9
|
+
// `org.jetbrains.kotlin` plugin id, so a plain word-boundary check on "kotlin" is
|
|
10
|
+
// more robust than trying to match every DSL style.
|
|
11
|
+
if (/\bkotlin\b/i.test(source))
|
|
8
12
|
return null;
|
|
9
13
|
const framework = /spring/i.test(source)
|
|
10
14
|
? { value: "spring", confidence: "high" }
|
package/dist/packs/js-ts.js
CHANGED
|
@@ -45,18 +45,33 @@ function detect(signals) {
|
|
|
45
45
|
: signals.hasFile("package-lock.json")
|
|
46
46
|
? { value: "npm", confidence: "high" }
|
|
47
47
|
: { value: "npm", confidence: "low" };
|
|
48
|
-
|
|
48
|
+
const usesTypeScript = Boolean(allDeps.typescript) || signals.hasFile("tsconfig.json");
|
|
49
|
+
const moduleFormat = signals.packageJson.moduleType;
|
|
50
|
+
return {
|
|
51
|
+
packId: "js-ts",
|
|
52
|
+
language: usesTypeScript ? "TypeScript" : "JavaScript",
|
|
53
|
+
framework,
|
|
54
|
+
testRunner,
|
|
55
|
+
linter,
|
|
56
|
+
packageManager,
|
|
57
|
+
usesTypeScript,
|
|
58
|
+
moduleFormat,
|
|
59
|
+
};
|
|
49
60
|
}
|
|
50
61
|
function rules(detection) {
|
|
51
62
|
const framework = detection.framework?.value ?? "none";
|
|
52
63
|
const testRunner = detection.testRunner?.value ?? "unknown";
|
|
64
|
+
const conventions = [];
|
|
65
|
+
if (detection.usesTypeScript) {
|
|
66
|
+
conventions.push("Usa TypeScript estricto; evita `any` salvo justificación explícita.");
|
|
67
|
+
}
|
|
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.");
|
|
53
72
|
return {
|
|
54
|
-
summary: `Proyecto JavaScript
|
|
55
|
-
conventions
|
|
56
|
-
"Usa TypeScript estricto; evita `any` salvo justificación explícita.",
|
|
57
|
-
`Ejecuta los tests con ${testRunner === "unknown" ? "el test runner del proyecto" : testRunner} antes de dar por terminada una tarea.`,
|
|
58
|
-
"Sigue el estilo de módulos ES existente (import/export), no mezcles con require().",
|
|
59
|
-
],
|
|
73
|
+
summary: `Proyecto ${detection.usesTypeScript ? "TypeScript" : "JavaScript"}${framework !== "none" ? ` con ${framework}` : ""}.`,
|
|
74
|
+
conventions,
|
|
60
75
|
architectureNotes: [
|
|
61
76
|
"Mantén los componentes/módulos pequeños y con una responsabilidad clara.",
|
|
62
77
|
"Coloca los tests junto al código que prueban o en un directorio `test/` espejo, según lo que ya use el repo.",
|
package/dist/packs/kotlin.js
CHANGED
|
@@ -2,7 +2,10 @@ function detect(signals) {
|
|
|
2
2
|
const source = signals.buildGradle;
|
|
3
3
|
if (!source)
|
|
4
4
|
return null;
|
|
5
|
-
|
|
5
|
+
// Modern Gradle projects often declare the Kotlin plugin via a version catalog alias
|
|
6
|
+
// (`alias(libs.plugins.kotlin.android)`) rather than the literal `kotlin("jvm")` or
|
|
7
|
+
// `org.jetbrains.kotlin` plugin id, so a plain word-boundary check is more robust.
|
|
8
|
+
if (!/\bkotlin\b/i.test(source))
|
|
6
9
|
return null;
|
|
7
10
|
const framework = /ktor/i.test(source)
|
|
8
11
|
? { value: "ktor", confidence: "high" }
|
package/dist/packs/python.js
CHANGED
|
@@ -15,12 +15,32 @@ function findIn(haystack, table) {
|
|
|
15
15
|
}
|
|
16
16
|
return undefined;
|
|
17
17
|
}
|
|
18
|
+
// Searching the whole pyproject.toml text (project name, URLs, script entry points, etc.)
|
|
19
|
+
// produces false positives — e.g. Flask's own pyproject.toml has `name = "flask"`, which
|
|
20
|
+
// isn't a dependency at all. Scope the search to the actual dependency declarations.
|
|
21
|
+
function extractPyprojectDependencySections(pyproject) {
|
|
22
|
+
const sections = [];
|
|
23
|
+
const mainDeps = pyproject.match(/(?:^|\n)dependencies\s*=\s*\[([\s\S]*?)\]/);
|
|
24
|
+
if (mainDeps)
|
|
25
|
+
sections.push(mainDeps[1]);
|
|
26
|
+
const optionalDeps = pyproject.match(/\[project\.optional-dependencies\]([\s\S]*?)(?:\n\[|$)/);
|
|
27
|
+
if (optionalDeps)
|
|
28
|
+
sections.push(optionalDeps[1]);
|
|
29
|
+
const dependencyGroups = pyproject.match(/\[dependency-groups\]([\s\S]*?)(?:\n\[|$)/);
|
|
30
|
+
if (dependencyGroups)
|
|
31
|
+
sections.push(dependencyGroups[1]);
|
|
32
|
+
const poetryDepsBlocks = pyproject.match(/\[tool\.poetry(?:\.group\.\w+)?\.dependencies\]([\s\S]*?)(?:\n\[|$)/g);
|
|
33
|
+
if (poetryDepsBlocks)
|
|
34
|
+
sections.push(...poetryDepsBlocks);
|
|
35
|
+
return sections.length > 0 ? sections.join("\n") : pyproject;
|
|
36
|
+
}
|
|
18
37
|
function detect(signals) {
|
|
19
38
|
const source = signals.pyprojectToml ?? signals.requirementsTxt ?? signals.environmentYml;
|
|
20
39
|
if (!source)
|
|
21
40
|
return null;
|
|
22
|
-
const
|
|
23
|
-
const
|
|
41
|
+
const searchText = signals.pyprojectToml ? extractPyprojectDependencySections(signals.pyprojectToml) : source;
|
|
42
|
+
const framework = findIn(searchText, FRAMEWORKS) ?? { value: "none", confidence: "low" };
|
|
43
|
+
const testRunner = findIn(searchText, TEST_RUNNERS) ?? { value: "unknown", confidence: "low" };
|
|
24
44
|
const packageManager = signals.environmentYml
|
|
25
45
|
? { value: "conda", confidence: "high" }
|
|
26
46
|
: signals.hasFile("poetry.lock")
|
package/dist/packs/scala.js
CHANGED
|
@@ -2,10 +2,12 @@ const FRAMEWORKS = [
|
|
|
2
2
|
["playframework", "play"],
|
|
3
3
|
["akka-http", "akka"],
|
|
4
4
|
["akka.actor", "akka"],
|
|
5
|
+
["scalatra", "scalatra"],
|
|
5
6
|
];
|
|
6
7
|
const TEST_RUNNERS = [
|
|
7
8
|
["scalatest", "scalatest"],
|
|
8
9
|
["munit", "munit"],
|
|
10
|
+
["specs2", "specs2"],
|
|
9
11
|
];
|
|
10
12
|
function detect(signals) {
|
|
11
13
|
const source = signals.buildSbt;
|
package/dist/packs/swift.js
CHANGED
|
@@ -2,6 +2,12 @@ function detect(signals) {
|
|
|
2
2
|
const source = signals.packageSwift;
|
|
3
3
|
if (!source)
|
|
4
4
|
return null;
|
|
5
|
+
// A Package.swift alone doesn't guarantee a Swift project: some C/C++ libraries (e.g.
|
|
6
|
+
// nlohmann/json) ship one purely so Swift Package Manager users can link the library,
|
|
7
|
+
// with zero actual Swift source. Require at least one other .swift file to be present.
|
|
8
|
+
const hasSwiftSource = signals.files.some((f) => f.toLowerCase().endsWith(".swift") && !f.endsWith("Package.swift"));
|
|
9
|
+
if (!hasSwiftSource)
|
|
10
|
+
return null;
|
|
5
11
|
const lower = source.toLowerCase();
|
|
6
12
|
const framework = lower.includes("vapor")
|
|
7
13
|
? { value: "vapor", confidence: "high" }
|
package/package.json
CHANGED