@thelogicatelier/sylva 1.0.4 → 1.0.10
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/README.md +20 -0
- package/dist/awareness/braveSearch.d.ts +18 -0
- package/dist/awareness/braveSearch.js +134 -0
- package/dist/awareness/detector.d.ts +18 -0
- package/dist/awareness/detector.js +176 -0
- package/dist/awareness/index.d.ts +11 -0
- package/dist/awareness/index.js +259 -0
- package/dist/awareness/manifestParsers/dotnetParsers.d.ts +13 -0
- package/dist/awareness/manifestParsers/dotnetParsers.js +151 -0
- package/dist/awareness/manifestParsers/goParsers.d.ts +9 -0
- package/dist/awareness/manifestParsers/goParsers.js +137 -0
- package/dist/awareness/manifestParsers/index.d.ts +13 -0
- package/dist/awareness/manifestParsers/index.js +182 -0
- package/dist/awareness/manifestParsers/javaParsers.d.ts +13 -0
- package/dist/awareness/manifestParsers/javaParsers.js +243 -0
- package/dist/awareness/manifestParsers/openclawParser.d.ts +6 -0
- package/dist/awareness/manifestParsers/openclawParser.js +103 -0
- package/dist/awareness/manifestParsers/packageJsonParser.d.ts +7 -0
- package/dist/awareness/manifestParsers/packageJsonParser.js +209 -0
- package/dist/awareness/manifestParsers/pythonParsers.d.ts +21 -0
- package/dist/awareness/manifestParsers/pythonParsers.js +344 -0
- package/dist/awareness/manifestParsers/rustParsers.d.ts +9 -0
- package/dist/awareness/manifestParsers/rustParsers.js +153 -0
- package/dist/awareness/manifestScanner.d.ts +11 -0
- package/dist/awareness/manifestScanner.js +182 -0
- package/dist/awareness/types.d.ts +105 -0
- package/dist/awareness/types.js +6 -0
- package/dist/awareness/versionResolver.d.ts +17 -0
- package/dist/awareness/versionResolver.js +62 -0
- package/dist/awareness/webGrounding.d.ts +20 -0
- package/dist/awareness/webGrounding.js +102 -0
- package/dist/cli.js +19 -4
- package/dist/constants.js +11 -0
- package/dist/modules.d.ts +5 -2
- package/dist/modules.js +17 -4
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.js +5 -2
- package/dist/utils.js +12 -6
- package/package.json +2 -2
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Python manifest parsers.
|
|
4
|
+
* Handles requirements.txt, pyproject.toml, poetry.lock, Pipfile, Pipfile.lock, setup.cfg.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.parseRequirementsTxt = parseRequirementsTxt;
|
|
41
|
+
exports.parsePyprojectToml = parsePyprojectToml;
|
|
42
|
+
exports.parsePipfile = parsePipfile;
|
|
43
|
+
exports.parseSetupCfg = parseSetupCfg;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
/** Known Python frameworks and their detection patterns */
|
|
47
|
+
const PYTHON_FRAMEWORKS = [
|
|
48
|
+
{ pkg: "django", frameworkId: "django", frameworkName: "Django" },
|
|
49
|
+
{ pkg: "flask", frameworkId: "flask", frameworkName: "Flask" },
|
|
50
|
+
{ pkg: "fastapi", frameworkId: "fastapi", frameworkName: "FastAPI" },
|
|
51
|
+
{ pkg: "uvicorn", frameworkId: "uvicorn", frameworkName: "Uvicorn" },
|
|
52
|
+
{ pkg: "starlette", frameworkId: "starlette", frameworkName: "Starlette" },
|
|
53
|
+
{ pkg: "celery", frameworkId: "celery", frameworkName: "Celery" },
|
|
54
|
+
{ pkg: "sqlalchemy", frameworkId: "sqlalchemy", frameworkName: "SQLAlchemy" },
|
|
55
|
+
{ pkg: "pandas", frameworkId: "pandas", frameworkName: "Pandas" },
|
|
56
|
+
{ pkg: "numpy", frameworkId: "numpy", frameworkName: "NumPy" },
|
|
57
|
+
{ pkg: "tensorflow", frameworkId: "tensorflow", frameworkName: "TensorFlow" },
|
|
58
|
+
{ pkg: "torch", frameworkId: "pytorch", frameworkName: "PyTorch" },
|
|
59
|
+
{ pkg: "scikit-learn", frameworkId: "scikit-learn", frameworkName: "scikit-learn" },
|
|
60
|
+
{ pkg: "pytest", frameworkId: "pytest", frameworkName: "pytest" },
|
|
61
|
+
{ pkg: "gunicorn", frameworkId: "gunicorn", frameworkName: "Gunicorn" },
|
|
62
|
+
];
|
|
63
|
+
function parsePythonVersion(versionSpec, sourceFile, pkgName) {
|
|
64
|
+
if (!versionSpec || versionSpec.trim() === "") {
|
|
65
|
+
return { certainty: "unknown", sourceFile, notes: `No version for ${pkgName}` };
|
|
66
|
+
}
|
|
67
|
+
// Exact pin: ==X.Y.Z
|
|
68
|
+
const exactMatch = versionSpec.match(/^==\s*(\S+)/);
|
|
69
|
+
if (exactMatch) {
|
|
70
|
+
return { value: exactMatch[1], certainty: "exact", sourceFile };
|
|
71
|
+
}
|
|
72
|
+
// Range-based (>=, <=, ~=, !=, etc.)
|
|
73
|
+
const rangeMatch = versionSpec.match(/[><=~!]+\s*(\S+)/);
|
|
74
|
+
if (rangeMatch) {
|
|
75
|
+
return {
|
|
76
|
+
value: rangeMatch[1],
|
|
77
|
+
certainty: "ambiguous",
|
|
78
|
+
sourceFile,
|
|
79
|
+
notes: `Version constraint '${versionSpec.trim()}' is a range`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { certainty: "unknown", sourceFile, notes: `Unparseable version: ${versionSpec}` };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse requirements.txt
|
|
86
|
+
*/
|
|
87
|
+
function parseRequirementsTxt(manifest) {
|
|
88
|
+
const signals = [];
|
|
89
|
+
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
90
|
+
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
91
|
+
// Python signal
|
|
92
|
+
signals.push({
|
|
93
|
+
kind: "framework",
|
|
94
|
+
frameworkId: "python",
|
|
95
|
+
frameworkName: "Python",
|
|
96
|
+
evidence: {
|
|
97
|
+
file: manifest.relativePath,
|
|
98
|
+
reason: "requirements.txt found",
|
|
99
|
+
},
|
|
100
|
+
scope: { pathRoot: rootPath },
|
|
101
|
+
});
|
|
102
|
+
const lines = content.split("\n");
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
const trimmed = line.trim();
|
|
105
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-"))
|
|
106
|
+
continue;
|
|
107
|
+
// Parse package==version or package>=version etc.
|
|
108
|
+
const match = trimmed.match(/^([a-zA-Z0-9_.-]+)\s*([><=~!]+.*)?$/);
|
|
109
|
+
if (!match)
|
|
110
|
+
continue;
|
|
111
|
+
const pkgName = match[1].toLowerCase();
|
|
112
|
+
const versionSpec = match[2] || "";
|
|
113
|
+
const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
|
|
114
|
+
if (framework) {
|
|
115
|
+
const versionInfo = parsePythonVersion(versionSpec, manifest.relativePath, pkgName);
|
|
116
|
+
signals.push({
|
|
117
|
+
kind: "framework",
|
|
118
|
+
frameworkId: framework.frameworkId,
|
|
119
|
+
frameworkName: framework.frameworkName,
|
|
120
|
+
version: versionInfo,
|
|
121
|
+
evidence: {
|
|
122
|
+
file: manifest.relativePath,
|
|
123
|
+
reason: `Python package '${pkgName}' found in requirements.txt`,
|
|
124
|
+
excerpt: trimmed,
|
|
125
|
+
},
|
|
126
|
+
scope: { pathRoot: rootPath },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return signals;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Parse pyproject.toml (basic TOML parsing with regex — no external dependency)
|
|
134
|
+
*/
|
|
135
|
+
function parsePyprojectToml(manifest) {
|
|
136
|
+
const signals = [];
|
|
137
|
+
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
138
|
+
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
139
|
+
signals.push({
|
|
140
|
+
kind: "framework",
|
|
141
|
+
frameworkId: "python",
|
|
142
|
+
frameworkName: "Python",
|
|
143
|
+
evidence: {
|
|
144
|
+
file: manifest.relativePath,
|
|
145
|
+
reason: "pyproject.toml found",
|
|
146
|
+
},
|
|
147
|
+
scope: { pathRoot: rootPath },
|
|
148
|
+
});
|
|
149
|
+
// Detect build system
|
|
150
|
+
if (content.includes("[tool.poetry]")) {
|
|
151
|
+
signals.push({
|
|
152
|
+
kind: "tooling",
|
|
153
|
+
frameworkId: "poetry",
|
|
154
|
+
frameworkName: "Poetry",
|
|
155
|
+
evidence: {
|
|
156
|
+
file: manifest.relativePath,
|
|
157
|
+
reason: "[tool.poetry] section found in pyproject.toml",
|
|
158
|
+
},
|
|
159
|
+
scope: { pathRoot: rootPath },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Extract dependencies from [project.dependencies] or [tool.poetry.dependencies]
|
|
163
|
+
// Simple regex-based extraction
|
|
164
|
+
const depPatterns = [
|
|
165
|
+
/\[(?:project\.dependencies|tool\.poetry\.dependencies)\]([\s\S]*?)(?:\n\[|$)/,
|
|
166
|
+
];
|
|
167
|
+
for (const pattern of depPatterns) {
|
|
168
|
+
const match = content.match(pattern);
|
|
169
|
+
if (!match)
|
|
170
|
+
continue;
|
|
171
|
+
const depsSection = match[1];
|
|
172
|
+
const depLines = depsSection.split("\n");
|
|
173
|
+
for (const depLine of depLines) {
|
|
174
|
+
const trimmed = depLine.trim();
|
|
175
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("["))
|
|
176
|
+
continue;
|
|
177
|
+
// Match: package = "version" or package = {version = "..."}
|
|
178
|
+
const simpleMatch = trimmed.match(/^([a-zA-Z0-9_.-]+)\s*=\s*"([^"]*)"/);
|
|
179
|
+
if (simpleMatch) {
|
|
180
|
+
const pkgName = simpleMatch[1].toLowerCase();
|
|
181
|
+
const versionSpec = simpleMatch[2];
|
|
182
|
+
const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
|
|
183
|
+
if (framework) {
|
|
184
|
+
const isPinned = /^\d+\.\d+/.test(versionSpec) && !/[><=~^]/.test(versionSpec);
|
|
185
|
+
const versionInfo = isPinned
|
|
186
|
+
? { value: versionSpec, certainty: "exact", sourceFile: manifest.relativePath }
|
|
187
|
+
: {
|
|
188
|
+
value: versionSpec.replace(/^[><=~^]+/, ""),
|
|
189
|
+
certainty: "ambiguous",
|
|
190
|
+
sourceFile: manifest.relativePath,
|
|
191
|
+
notes: `Constraint '${versionSpec}' in pyproject.toml`,
|
|
192
|
+
};
|
|
193
|
+
signals.push({
|
|
194
|
+
kind: "framework",
|
|
195
|
+
frameworkId: framework.frameworkId,
|
|
196
|
+
frameworkName: framework.frameworkName,
|
|
197
|
+
version: versionInfo,
|
|
198
|
+
evidence: {
|
|
199
|
+
file: manifest.relativePath,
|
|
200
|
+
reason: `Python package '${pkgName}' found in pyproject.toml`,
|
|
201
|
+
excerpt: trimmed,
|
|
202
|
+
},
|
|
203
|
+
scope: { pathRoot: rootPath },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Check for python version requirement
|
|
210
|
+
const pythonVersionMatch = content.match(/requires-python\s*=\s*"([^"]+)"/);
|
|
211
|
+
if (pythonVersionMatch) {
|
|
212
|
+
const spec = pythonVersionMatch[1];
|
|
213
|
+
const isExact = /^\d+\.\d+/.test(spec) && !/[><=~^]/.test(spec);
|
|
214
|
+
signals.push({
|
|
215
|
+
kind: "version",
|
|
216
|
+
frameworkId: "python",
|
|
217
|
+
frameworkName: "Python",
|
|
218
|
+
version: {
|
|
219
|
+
value: spec.replace(/^[><=~^]+/, ""),
|
|
220
|
+
certainty: isExact ? "exact" : "ambiguous",
|
|
221
|
+
sourceFile: manifest.relativePath,
|
|
222
|
+
notes: isExact ? undefined : `Constraint '${spec}'`,
|
|
223
|
+
},
|
|
224
|
+
evidence: {
|
|
225
|
+
file: manifest.relativePath,
|
|
226
|
+
reason: "Python version requirement in pyproject.toml",
|
|
227
|
+
excerpt: `requires-python = "${spec}"`,
|
|
228
|
+
},
|
|
229
|
+
scope: { pathRoot: rootPath },
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return signals;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Parse Pipfile (basic TOML-like parsing)
|
|
236
|
+
*/
|
|
237
|
+
function parsePipfile(manifest) {
|
|
238
|
+
const signals = [];
|
|
239
|
+
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
240
|
+
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
241
|
+
signals.push({
|
|
242
|
+
kind: "framework",
|
|
243
|
+
frameworkId: "python",
|
|
244
|
+
frameworkName: "Python",
|
|
245
|
+
evidence: {
|
|
246
|
+
file: manifest.relativePath,
|
|
247
|
+
reason: "Pipfile found (Pipenv project)",
|
|
248
|
+
},
|
|
249
|
+
scope: { pathRoot: rootPath },
|
|
250
|
+
});
|
|
251
|
+
signals.push({
|
|
252
|
+
kind: "tooling",
|
|
253
|
+
frameworkId: "pipenv",
|
|
254
|
+
frameworkName: "Pipenv",
|
|
255
|
+
evidence: {
|
|
256
|
+
file: manifest.relativePath,
|
|
257
|
+
reason: "Pipfile found",
|
|
258
|
+
},
|
|
259
|
+
scope: { pathRoot: rootPath },
|
|
260
|
+
});
|
|
261
|
+
// Extract packages section
|
|
262
|
+
const packagesMatch = content.match(/\[packages\]([\s\S]*?)(?:\n\[|$)/);
|
|
263
|
+
if (packagesMatch) {
|
|
264
|
+
const lines = packagesMatch[1].split("\n");
|
|
265
|
+
for (const line of lines) {
|
|
266
|
+
const match = line.trim().match(/^([a-zA-Z0-9_.-]+)\s*=\s*"([^"]*)"/);
|
|
267
|
+
if (match) {
|
|
268
|
+
const pkgName = match[1].toLowerCase();
|
|
269
|
+
const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
|
|
270
|
+
if (framework) {
|
|
271
|
+
const versionSpec = match[2];
|
|
272
|
+
const isExact = versionSpec === "*" ? false : /^\d+\.\d+/.test(versionSpec);
|
|
273
|
+
signals.push({
|
|
274
|
+
kind: "framework",
|
|
275
|
+
frameworkId: framework.frameworkId,
|
|
276
|
+
frameworkName: framework.frameworkName,
|
|
277
|
+
version: isExact
|
|
278
|
+
? { value: versionSpec, certainty: "exact", sourceFile: manifest.relativePath }
|
|
279
|
+
: {
|
|
280
|
+
certainty: versionSpec === "*" ? "unknown" : "ambiguous",
|
|
281
|
+
sourceFile: manifest.relativePath,
|
|
282
|
+
},
|
|
283
|
+
evidence: {
|
|
284
|
+
file: manifest.relativePath,
|
|
285
|
+
reason: `Python package '${pkgName}' found in Pipfile`,
|
|
286
|
+
excerpt: line.trim(),
|
|
287
|
+
},
|
|
288
|
+
scope: { pathRoot: rootPath },
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return signals;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Parse setup.cfg (ini-like)
|
|
298
|
+
*/
|
|
299
|
+
function parseSetupCfg(manifest) {
|
|
300
|
+
const signals = [];
|
|
301
|
+
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
302
|
+
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
303
|
+
signals.push({
|
|
304
|
+
kind: "framework",
|
|
305
|
+
frameworkId: "python",
|
|
306
|
+
frameworkName: "Python",
|
|
307
|
+
evidence: {
|
|
308
|
+
file: manifest.relativePath,
|
|
309
|
+
reason: "setup.cfg found (Python setuptools project)",
|
|
310
|
+
},
|
|
311
|
+
scope: { pathRoot: rootPath },
|
|
312
|
+
});
|
|
313
|
+
// Extract install_requires
|
|
314
|
+
const installRequiresMatch = content.match(/install_requires\s*=\s*([\s\S]*?)(?:\n\S|\n\[|$)/);
|
|
315
|
+
if (installRequiresMatch) {
|
|
316
|
+
const lines = installRequiresMatch[1].split("\n");
|
|
317
|
+
for (const line of lines) {
|
|
318
|
+
const trimmed = line.trim();
|
|
319
|
+
if (!trimmed)
|
|
320
|
+
continue;
|
|
321
|
+
const match = trimmed.match(/^([a-zA-Z0-9_.-]+)\s*([><=~!]+.*)?$/);
|
|
322
|
+
if (match) {
|
|
323
|
+
const pkgName = match[1].toLowerCase();
|
|
324
|
+
const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
|
|
325
|
+
if (framework) {
|
|
326
|
+
const versionInfo = parsePythonVersion(match[2] || "", manifest.relativePath, pkgName);
|
|
327
|
+
signals.push({
|
|
328
|
+
kind: "framework",
|
|
329
|
+
frameworkId: framework.frameworkId,
|
|
330
|
+
frameworkName: framework.frameworkName,
|
|
331
|
+
version: versionInfo,
|
|
332
|
+
evidence: {
|
|
333
|
+
file: manifest.relativePath,
|
|
334
|
+
reason: `Python package '${pkgName}' found in setup.cfg`,
|
|
335
|
+
excerpt: trimmed,
|
|
336
|
+
},
|
|
337
|
+
scope: { pathRoot: rootPath },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return signals;
|
|
344
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rust manifest parser.
|
|
4
|
+
* Handles Cargo.toml files.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.parseCargoToml = parseCargoToml;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/** Known Rust frameworks/libraries */
|
|
44
|
+
const RUST_FRAMEWORKS = [
|
|
45
|
+
{ crate: "actix-web", frameworkId: "actix-web", frameworkName: "Actix Web" },
|
|
46
|
+
{ crate: "rocket", frameworkId: "rocket", frameworkName: "Rocket" },
|
|
47
|
+
{ crate: "axum", frameworkId: "axum", frameworkName: "Axum" },
|
|
48
|
+
{ crate: "tokio", frameworkId: "tokio", frameworkName: "Tokio" },
|
|
49
|
+
{ crate: "serde", frameworkId: "serde", frameworkName: "Serde" },
|
|
50
|
+
{ crate: "diesel", frameworkId: "diesel", frameworkName: "Diesel" },
|
|
51
|
+
{ crate: "sqlx", frameworkId: "sqlx", frameworkName: "SQLx" },
|
|
52
|
+
{ crate: "warp", frameworkId: "warp", frameworkName: "Warp" },
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Parse Cargo.toml
|
|
56
|
+
*/
|
|
57
|
+
function parseCargoToml(manifest) {
|
|
58
|
+
const signals = [];
|
|
59
|
+
const content = fs.readFileSync(manifest.absolutePath, "utf-8");
|
|
60
|
+
const rootPath = path.dirname(manifest.relativePath) || ".";
|
|
61
|
+
signals.push({
|
|
62
|
+
kind: "framework",
|
|
63
|
+
frameworkId: "rust",
|
|
64
|
+
frameworkName: "Rust",
|
|
65
|
+
evidence: {
|
|
66
|
+
file: manifest.relativePath,
|
|
67
|
+
reason: "Cargo.toml found",
|
|
68
|
+
},
|
|
69
|
+
scope: { pathRoot: rootPath },
|
|
70
|
+
});
|
|
71
|
+
// Extract Rust edition
|
|
72
|
+
const editionMatch = content.match(/edition\s*=\s*"(\d{4})"/);
|
|
73
|
+
if (editionMatch) {
|
|
74
|
+
signals.push({
|
|
75
|
+
kind: "version",
|
|
76
|
+
frameworkId: "rust",
|
|
77
|
+
frameworkName: "Rust",
|
|
78
|
+
version: {
|
|
79
|
+
value: `edition ${editionMatch[1]}`,
|
|
80
|
+
certainty: "exact",
|
|
81
|
+
sourceFile: manifest.relativePath,
|
|
82
|
+
},
|
|
83
|
+
evidence: {
|
|
84
|
+
file: manifest.relativePath,
|
|
85
|
+
reason: "Rust edition specified in Cargo.toml",
|
|
86
|
+
excerpt: `edition = "${editionMatch[1]}"`,
|
|
87
|
+
},
|
|
88
|
+
scope: { pathRoot: rootPath },
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Extract dependencies
|
|
92
|
+
const depSections = [
|
|
93
|
+
content.match(/\[dependencies\]([\s\S]*?)(?:\n\[|$)/),
|
|
94
|
+
content.match(/\[dev-dependencies\]([\s\S]*?)(?:\n\[|$)/),
|
|
95
|
+
];
|
|
96
|
+
for (const sectionMatch of depSections) {
|
|
97
|
+
if (!sectionMatch)
|
|
98
|
+
continue;
|
|
99
|
+
const section = sectionMatch[1];
|
|
100
|
+
const lines = section.split("\n");
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("["))
|
|
104
|
+
continue;
|
|
105
|
+
// Match: crate = "version" or crate = { version = "..." }
|
|
106
|
+
let crateName;
|
|
107
|
+
let versionStr;
|
|
108
|
+
const simpleMatch = trimmed.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]*)"/);
|
|
109
|
+
if (simpleMatch) {
|
|
110
|
+
crateName = simpleMatch[1];
|
|
111
|
+
versionStr = simpleMatch[2];
|
|
112
|
+
}
|
|
113
|
+
const tableMatch = trimmed.match(/^([a-zA-Z0-9_-]+)\s*=\s*\{.*version\s*=\s*"([^"]*)"/);
|
|
114
|
+
if (tableMatch) {
|
|
115
|
+
crateName = tableMatch[1];
|
|
116
|
+
versionStr = tableMatch[2];
|
|
117
|
+
}
|
|
118
|
+
if (!crateName)
|
|
119
|
+
continue;
|
|
120
|
+
const framework = RUST_FRAMEWORKS.find((f) => f.crate === crateName);
|
|
121
|
+
if (framework) {
|
|
122
|
+
let versionInfo;
|
|
123
|
+
if (versionStr) {
|
|
124
|
+
const isPinned = /^\d+\.\d+\.\d+$/.test(versionStr);
|
|
125
|
+
versionInfo = isPinned
|
|
126
|
+
? { value: versionStr, certainty: "exact", sourceFile: manifest.relativePath }
|
|
127
|
+
: {
|
|
128
|
+
value: versionStr,
|
|
129
|
+
certainty: "ambiguous",
|
|
130
|
+
sourceFile: manifest.relativePath,
|
|
131
|
+
notes: `Semver range: ${versionStr}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
versionInfo = { certainty: "unknown", sourceFile: manifest.relativePath };
|
|
136
|
+
}
|
|
137
|
+
signals.push({
|
|
138
|
+
kind: "framework",
|
|
139
|
+
frameworkId: framework.frameworkId,
|
|
140
|
+
frameworkName: framework.frameworkName,
|
|
141
|
+
version: versionInfo,
|
|
142
|
+
evidence: {
|
|
143
|
+
file: manifest.relativePath,
|
|
144
|
+
reason: `Rust crate '${crateName}' found in Cargo.toml`,
|
|
145
|
+
excerpt: trimmed,
|
|
146
|
+
},
|
|
147
|
+
scope: { pathRoot: rootPath },
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return signals;
|
|
153
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-repo manifest scanner.
|
|
3
|
+
* Recursively walks the entire repository to discover framework manifest files,
|
|
4
|
+
* regardless of nesting depth. Handles monorepos and nested subprojects.
|
|
5
|
+
*/
|
|
6
|
+
import { ManifestFile } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Recursively scan a repository for manifest files.
|
|
9
|
+
* Returns all discovered manifest files with metadata.
|
|
10
|
+
*/
|
|
11
|
+
export declare function scanManifests(repoPath: string): ManifestFile[];
|