@thelogicatelier/sylva 1.0.11 → 1.0.13

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.
@@ -43,23 +43,7 @@ exports.parsePipfile = parsePipfile;
43
43
  exports.parseSetupCfg = parseSetupCfg;
44
44
  const fs = __importStar(require("fs"));
45
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
- ];
46
+ const constants_1 = require("../../constants");
63
47
  function parsePythonVersion(versionSpec, sourceFile, pkgName) {
64
48
  if (!versionSpec || versionSpec.trim() === "") {
65
49
  return { certainty: "unknown", sourceFile, notes: `No version for ${pkgName}` };
@@ -110,7 +94,7 @@ function parseRequirementsTxt(manifest) {
110
94
  continue;
111
95
  const pkgName = match[1].toLowerCase();
112
96
  const versionSpec = match[2] || "";
113
- const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
97
+ const framework = constants_1.PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
114
98
  if (framework) {
115
99
  const versionInfo = parsePythonVersion(versionSpec, manifest.relativePath, pkgName);
116
100
  signals.push({
@@ -179,7 +163,7 @@ function parsePyprojectToml(manifest) {
179
163
  if (simpleMatch) {
180
164
  const pkgName = simpleMatch[1].toLowerCase();
181
165
  const versionSpec = simpleMatch[2];
182
- const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
166
+ const framework = constants_1.PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
183
167
  if (framework) {
184
168
  const isPinned = /^\d+\.\d+/.test(versionSpec) && !/[><=~^]/.test(versionSpec);
185
169
  const versionInfo = isPinned
@@ -266,7 +250,7 @@ function parsePipfile(manifest) {
266
250
  const match = line.trim().match(/^([a-zA-Z0-9_.-]+)\s*=\s*"([^"]*)"/);
267
251
  if (match) {
268
252
  const pkgName = match[1].toLowerCase();
269
- const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
253
+ const framework = constants_1.PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
270
254
  if (framework) {
271
255
  const versionSpec = match[2];
272
256
  const isExact = versionSpec === "*" ? false : /^\d+\.\d+/.test(versionSpec);
@@ -321,7 +305,7 @@ function parseSetupCfg(manifest) {
321
305
  const match = trimmed.match(/^([a-zA-Z0-9_.-]+)\s*([><=~!]+.*)?$/);
322
306
  if (match) {
323
307
  const pkgName = match[1].toLowerCase();
324
- const framework = PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
308
+ const framework = constants_1.PYTHON_FRAMEWORKS.find((f) => f.pkg === pkgName);
325
309
  if (framework) {
326
310
  const versionInfo = parsePythonVersion(match[2] || "", manifest.relativePath, pkgName);
327
311
  signals.push({
@@ -40,17 +40,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.parseCargoToml = parseCargoToml;
41
41
  const fs = __importStar(require("fs"));
42
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
- ];
43
+ const constants_1 = require("../../constants");
54
44
  /**
55
45
  * Parse Cargo.toml
56
46
  */
@@ -117,7 +107,7 @@ function parseCargoToml(manifest) {
117
107
  }
118
108
  if (!crateName)
119
109
  continue;
120
- const framework = RUST_FRAMEWORKS.find((f) => f.crate === crateName);
110
+ const framework = constants_1.RUST_FRAMEWORKS.find((f) => f.crate === crateName);
121
111
  if (framework) {
122
112
  let versionInfo;
123
113
  if (versionStr) {
@@ -41,81 +41,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.scanManifests = scanManifests;
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
+ const constants_1 = require("../constants");
44
45
  /** Directories to skip during scanning */
45
- const SCAN_IGNORE_DIRS = new Set([
46
- ".git",
47
- "node_modules",
48
- "__pycache__",
49
- "venv",
50
- ".venv",
51
- "env",
52
- ".env",
53
- "dist",
54
- "build",
55
- "target",
56
- "vendor",
57
- "bin",
58
- "obj",
59
- "out",
60
- "coverage",
61
- "logs",
62
- "tmp",
63
- "temp",
64
- ".idea",
65
- ".vscode",
66
- ".next",
67
- ".nuxt",
68
- ".svelte-kit",
69
- ".angular",
70
- ".DS_Store",
71
- ]);
46
+ const IGNORE_DIRS_SET = new Set(constants_1.SCAN_IGNORE_DIRS);
72
47
  /** Exact filenames to match as manifests */
73
- const MANIFEST_EXACT_NAMES = new Set([
74
- "package.json",
75
- "package-lock.json",
76
- "yarn.lock",
77
- "pnpm-lock.yaml",
78
- "openclaw.json",
79
- ".openclaw.json",
80
- "angular.json",
81
- "workspace.json",
82
- "project.json",
83
- "pyproject.toml",
84
- "requirements.txt",
85
- "poetry.lock",
86
- "Pipfile",
87
- "Pipfile.lock",
88
- "setup.cfg",
89
- "setup.py",
90
- "pom.xml",
91
- "build.gradle",
92
- "build.gradle.kts",
93
- "settings.gradle",
94
- "settings.gradle.kts",
95
- "gradle.properties",
96
- "go.mod",
97
- "go.sum",
98
- "Cargo.toml",
99
- "Cargo.lock",
100
- "global.json",
101
- "packages.lock.json",
102
- "Dockerfile",
103
- "docker-compose.yml",
104
- "docker-compose.yaml",
105
- "Makefile",
106
- ".gitlab-ci.yml",
107
- ]);
108
- /** File extension patterns for manifests (e.g. *.csproj) */
109
- const MANIFEST_EXTENSION_PATTERNS = [".csproj", ".fsproj", ".vbproj"];
110
- /** Maximum file size in characters to read */
111
- const MAX_FILE_SIZE = 500_000;
48
+ const MANIFEST_EXACT_NAMES = new Set(constants_1.MANIFEST_EXACT_NAMES);
112
49
  /**
113
50
  * Check if a filename matches a known manifest pattern.
114
51
  */
115
52
  function isManifestFile(filename) {
116
53
  if (MANIFEST_EXACT_NAMES.has(filename))
117
54
  return true;
118
- for (const ext of MANIFEST_EXTENSION_PATTERNS) {
55
+ for (const ext of constants_1.MANIFEST_EXTENSION_PATTERNS) {
119
56
  if (filename.endsWith(ext))
120
57
  return true;
121
58
  }
@@ -142,7 +79,7 @@ function scanManifests(repoPath) {
142
79
  }
143
80
  for (const entry of entries) {
144
81
  // Skip ignored directories and hidden dirs (except specific manifest patterns)
145
- if (SCAN_IGNORE_DIRS.has(entry))
82
+ if (IGNORE_DIRS_SET.has(entry))
146
83
  continue;
147
84
  const fullPath = path.join(dir, entry);
148
85
  let stat;
@@ -160,7 +97,11 @@ function scanManifests(repoPath) {
160
97
  walk(fullPath, depth + 1);
161
98
  }
162
99
  else if (stat.isFile()) {
163
- if (isManifestFile(entry) && stat.size <= MAX_FILE_SIZE) {
100
+ if (isManifestFile(entry)) {
101
+ if (stat.size > constants_1.MAX_MANIFEST_FILE_SIZE) {
102
+ // File is too large, skip it
103
+ continue;
104
+ }
164
105
  results.push({
165
106
  absolutePath: fullPath,
166
107
  relativePath: path.relative(absoluteRoot, fullPath),
@@ -0,0 +1,5 @@
1
+ import { Signal } from "./types";
2
+ /**
3
+ * Scan source codebase for integrations.
4
+ */
5
+ export declare function scanSourceFiles(rootPath: string): Signal[];
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.scanSourceFiles = scanSourceFiles;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const ignore_1 = __importDefault(require("ignore"));
43
+ const constants_1 = require("../constants");
44
+ const MAX_SCAN_FILES = 200;
45
+ const MAX_LINES_PER_FILE = 500;
46
+ /**
47
+ * Loads .gitignore rules from the root if available
48
+ */
49
+ function loadGitIgnore(rootPath) {
50
+ const ig = (0, ignore_1.default)();
51
+ // Add hardcoded global ignores
52
+ for (const item of constants_1.SCAN_IGNORE_DIRS)
53
+ ig.add(`**/${item}/**`);
54
+ for (const item of constants_1.ALWAYS_IGNORE_FILES)
55
+ ig.add(`**/${item}`);
56
+ ig.add("**/*.env*"); // Cover all .env permutations globally
57
+ try {
58
+ const gitignorePath = path.join(rootPath, ".gitignore");
59
+ if (fs.existsSync(gitignorePath)) {
60
+ const content = fs.readFileSync(gitignorePath, "utf-8");
61
+ ig.add(content);
62
+ }
63
+ }
64
+ catch {
65
+ // Ignore errors reading .gitignore
66
+ }
67
+ return ig;
68
+ }
69
+ /**
70
+ * Collect source files sequentially via BFS
71
+ */
72
+ function collectSourceFiles(dir, ig, rootPath, files = []) {
73
+ if (files.length >= MAX_SCAN_FILES)
74
+ return files;
75
+ try {
76
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
77
+ for (const entry of entries) {
78
+ if (files.length >= MAX_SCAN_FILES)
79
+ break;
80
+ const fullPath = path.join(dir, entry.name);
81
+ // Ensure posix path for gitignore matching
82
+ const relPath = path.relative(rootPath, fullPath).split(path.sep).join(path.posix.sep);
83
+ // Skip ignored files/directories
84
+ // Note: ignore().ignores() requires directory paths to end with '/' to match properly sometimes,
85
+ // but relPath works fine for most rules.
86
+ if (ig.ignores(relPath))
87
+ continue;
88
+ if (entry.isDirectory()) {
89
+ if (!ig.ignores(relPath + "/")) {
90
+ collectSourceFiles(fullPath, ig, rootPath, files);
91
+ }
92
+ }
93
+ else if (entry.isFile()) {
94
+ const ext = path.extname(entry.name).toLowerCase();
95
+ if (constants_1.SOURCE_EXTENSIONS.includes(ext)) {
96
+ files.push(fullPath);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ // access errors skipped
103
+ }
104
+ return files;
105
+ }
106
+ /**
107
+ * Heuristic: check if this phrase occurs in the text
108
+ */
109
+ function matchPatterns(content, patterns = []) {
110
+ for (const pat of patterns) {
111
+ if (content.includes(pat))
112
+ return pat;
113
+ }
114
+ return null;
115
+ }
116
+ /**
117
+ * Scan source codebase for integrations.
118
+ */
119
+ function scanSourceFiles(rootPath) {
120
+ const ig = loadGitIgnore(rootPath);
121
+ const targetFiles = collectSourceFiles(rootPath, ig, rootPath);
122
+ const signals = [];
123
+ const detectedIntegrationIds = new Set();
124
+ for (const file of targetFiles) {
125
+ try {
126
+ // Read file and limit to N lines to keep it fast
127
+ const contentRaw = fs.readFileSync(file, "utf-8");
128
+ let content = contentRaw;
129
+ const lines = contentRaw.split("\n");
130
+ if (lines.length > MAX_LINES_PER_FILE) {
131
+ content = lines.slice(0, MAX_LINES_PER_FILE).join("\n");
132
+ }
133
+ // Check all integrations against this chunk
134
+ for (const integration of constants_1.INTEGRATIONS) {
135
+ if (detectedIntegrationIds.has(integration.id))
136
+ continue; // Found it already somewhere
137
+ let reason = "";
138
+ let matchedPattern = "";
139
+ matchedPattern = matchPatterns(content, integration.urlPatterns) || "";
140
+ if (matchedPattern) {
141
+ reason = `Found API URL pattern "${matchedPattern}"`;
142
+ }
143
+ else {
144
+ matchedPattern = matchPatterns(content, integration.importPatterns) || "";
145
+ if (matchedPattern) {
146
+ reason = `Found SDK import pattern "${matchedPattern}"`;
147
+ }
148
+ else {
149
+ matchedPattern = matchPatterns(content, integration.envPatterns) || "";
150
+ if (matchedPattern) {
151
+ reason = `Found env var reference "${matchedPattern}"`;
152
+ }
153
+ }
154
+ }
155
+ if (reason) {
156
+ detectedIntegrationIds.add(integration.id);
157
+ signals.push({
158
+ kind: "integration",
159
+ frameworkId: integration.id,
160
+ frameworkName: integration.name,
161
+ version: { certainty: "unknown", value: undefined },
162
+ evidence: {
163
+ file: path.relative(rootPath, file),
164
+ reason: `Source code scanner detected ${integration.name}: ${reason}`,
165
+ },
166
+ scope: { pathRoot: rootPath },
167
+ });
168
+ }
169
+ }
170
+ // If we found everything, stop scanning
171
+ if (detectedIntegrationIds.size === constants_1.INTEGRATIONS.length)
172
+ break;
173
+ }
174
+ catch {
175
+ // Ignore unreadable files
176
+ }
177
+ }
178
+ return signals;
179
+ }
@@ -13,7 +13,7 @@ export interface VersionInfo {
13
13
  notes?: string;
14
14
  }
15
15
  /** Signal kinds emitted by manifest parsers */
16
- export type SignalKind = "framework" | "version" | "orchestrator" | "entrypoint" | "tooling";
16
+ export type SignalKind = "framework" | "version" | "orchestrator" | "entrypoint" | "tooling" | "agent" | "subagent" | "heartbeat" | "cron" | "hook" | "skill" | "plugin" | "integration";
17
17
  /** Evidence for a detected signal */
18
18
  export interface SignalEvidence {
19
19
  file: string;
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Shared programmatic constants and integration registries for Sylva.
3
+ */
1
4
  export interface ModelMetadata {
2
5
  provider: string;
3
6
  tier: "primary" | "mini";
@@ -16,3 +19,44 @@ export declare const IGNORED_DIRS: Set<string>;
16
19
  * source tree so the AI reads them FIRST, preventing framework hallucination.
17
20
  */
18
21
  export declare const DEPENDENCY_MANIFESTS: Set<string>;
22
+ export interface IntegrationDef {
23
+ id: string;
24
+ name: string;
25
+ urlPatterns?: string[];
26
+ importPatterns?: string[];
27
+ envPatterns?: string[];
28
+ }
29
+ export declare const INTEGRATIONS: IntegrationDef[];
30
+ export declare const SOURCE_EXTENSIONS: string[];
31
+ export declare const SCAN_IGNORE_DIRS: string[];
32
+ export declare const ALWAYS_IGNORE_FILES: string[];
33
+ export declare const MANIFEST_EXACT_NAMES: string[];
34
+ export declare const MANIFEST_EXTENSION_PATTERNS: string[];
35
+ export declare const MAX_MANIFEST_FILE_SIZE = 500000;
36
+ /** Confidence scoring rules mapped by framework ID */
37
+ export declare const CONFIDENCE_SCORES: Record<string, number>;
38
+ export declare const NPM_FRAMEWORKS: {
39
+ depPattern: string | RegExp;
40
+ frameworkId: string;
41
+ frameworkName: string;
42
+ }[];
43
+ export declare const PYTHON_FRAMEWORKS: {
44
+ pkg: string;
45
+ frameworkId: string;
46
+ frameworkName: string;
47
+ }[];
48
+ export declare const JAVA_FRAMEWORKS: {
49
+ groupArtifact: string;
50
+ frameworkId: string;
51
+ frameworkName: string;
52
+ }[];
53
+ export declare const GO_FRAMEWORKS: {
54
+ module: string;
55
+ frameworkId: string;
56
+ frameworkName: string;
57
+ }[];
58
+ export declare const RUST_FRAMEWORKS: {
59
+ crate: string;
60
+ frameworkId: string;
61
+ frameworkName: string;
62
+ }[];