@thelogicatelier/sylva 1.0.13 → 1.0.14
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.
|
@@ -49,6 +49,8 @@ const BRAVE_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search";
|
|
|
49
49
|
function cacheKey(query) {
|
|
50
50
|
return crypto.createHash("md5").update(query).digest("hex");
|
|
51
51
|
}
|
|
52
|
+
let lastRequestTime = 0;
|
|
53
|
+
const RATE_LIMIT_MS = 1100; // 1.1 seconds to be safe
|
|
52
54
|
/**
|
|
53
55
|
* Search Brave API for a query. Returns parsed results.
|
|
54
56
|
* Caches results to disk if cacheDir is provided.
|
|
@@ -74,6 +76,12 @@ async function braveSearch(query, options) {
|
|
|
74
76
|
}
|
|
75
77
|
try {
|
|
76
78
|
const url = `${BRAVE_SEARCH_URL}?q=${encodeURIComponent(query)}&count=5`;
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const timeSinceLast = now - lastRequestTime;
|
|
81
|
+
if (timeSinceLast < RATE_LIMIT_MS) {
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, RATE_LIMIT_MS - timeSinceLast));
|
|
83
|
+
}
|
|
84
|
+
lastRequestTime = Date.now();
|
|
77
85
|
const response = await fetch(url, {
|
|
78
86
|
headers: {
|
|
79
87
|
Accept: "application/json",
|
|
@@ -81,6 +89,7 @@ async function braveSearch(query, options) {
|
|
|
81
89
|
"X-Subscription-Token": apiKey,
|
|
82
90
|
},
|
|
83
91
|
});
|
|
92
|
+
lastRequestTime = Date.now();
|
|
84
93
|
if (!response.ok) {
|
|
85
94
|
const statusText = response.statusText || "Unknown error";
|
|
86
95
|
const errorMsg = `HTTP ${response.status} (${statusText})`;
|
|
@@ -43,6 +43,15 @@ const ignore_1 = __importDefault(require("ignore"));
|
|
|
43
43
|
const constants_1 = require("../constants");
|
|
44
44
|
const MAX_SCAN_FILES = 200;
|
|
45
45
|
const MAX_LINES_PER_FILE = 500;
|
|
46
|
+
// Precompute known config files and docker files for fast lookup
|
|
47
|
+
const CONFIG_FILE_NAMES = new Set();
|
|
48
|
+
for (const int of constants_1.INTEGRATIONS) {
|
|
49
|
+
if (int.configPatterns) {
|
|
50
|
+
for (const pat of int.configPatterns)
|
|
51
|
+
CONFIG_FILE_NAMES.add(pat);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const DOCKER_FILE_NAMES = new Set(["docker-compose.yml", "docker-compose.yaml"]);
|
|
46
55
|
/**
|
|
47
56
|
* Loads .gitignore rules from the root if available
|
|
48
57
|
*/
|
|
@@ -92,7 +101,12 @@ function collectSourceFiles(dir, ig, rootPath, files = []) {
|
|
|
92
101
|
}
|
|
93
102
|
else if (entry.isFile()) {
|
|
94
103
|
const ext = path.extname(entry.name).toLowerCase();
|
|
95
|
-
|
|
104
|
+
const baseName = entry.name;
|
|
105
|
+
const lowerBaseName = baseName.toLowerCase();
|
|
106
|
+
if (constants_1.SOURCE_EXTENSIONS.includes(ext) ||
|
|
107
|
+
CONFIG_FILE_NAMES.has(baseName) ||
|
|
108
|
+
DOCKER_FILE_NAMES.has(lowerBaseName) ||
|
|
109
|
+
lowerBaseName.includes("dockerfile")) {
|
|
96
110
|
files.push(fullPath);
|
|
97
111
|
}
|
|
98
112
|
}
|
|
@@ -123,6 +137,31 @@ function scanSourceFiles(rootPath) {
|
|
|
123
137
|
const detectedIntegrationIds = new Set();
|
|
124
138
|
for (const file of targetFiles) {
|
|
125
139
|
try {
|
|
140
|
+
const fileName = path.basename(file);
|
|
141
|
+
const lowerFileName = fileName.toLowerCase();
|
|
142
|
+
const isDocker = lowerFileName.includes("dockerfile") || DOCKER_FILE_NAMES.has(lowerFileName);
|
|
143
|
+
// 1. Check exact config file matches (no file reading required)
|
|
144
|
+
for (const integration of constants_1.INTEGRATIONS) {
|
|
145
|
+
if (detectedIntegrationIds.has(integration.id))
|
|
146
|
+
continue;
|
|
147
|
+
if (integration.configPatterns?.includes(fileName)) {
|
|
148
|
+
detectedIntegrationIds.add(integration.id);
|
|
149
|
+
signals.push({
|
|
150
|
+
kind: "integration",
|
|
151
|
+
frameworkId: integration.id,
|
|
152
|
+
frameworkName: integration.name,
|
|
153
|
+
version: { certainty: "unknown", value: undefined },
|
|
154
|
+
evidence: {
|
|
155
|
+
file: path.relative(rootPath, file),
|
|
156
|
+
reason: `Source code scanner detected ${integration.name}: Found config file "${fileName}"`,
|
|
157
|
+
},
|
|
158
|
+
scope: { pathRoot: rootPath },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// If we found everything, stop scanning
|
|
163
|
+
if (detectedIntegrationIds.size === constants_1.INTEGRATIONS.length)
|
|
164
|
+
break;
|
|
126
165
|
// Read file and limit to N lines to keep it fast
|
|
127
166
|
const contentRaw = fs.readFileSync(file, "utf-8");
|
|
128
167
|
let content = contentRaw;
|
|
@@ -130,25 +169,35 @@ function scanSourceFiles(rootPath) {
|
|
|
130
169
|
if (lines.length > MAX_LINES_PER_FILE) {
|
|
131
170
|
content = lines.slice(0, MAX_LINES_PER_FILE).join("\n");
|
|
132
171
|
}
|
|
133
|
-
//
|
|
172
|
+
// 2. Content Checks
|
|
134
173
|
for (const integration of constants_1.INTEGRATIONS) {
|
|
135
174
|
if (detectedIntegrationIds.has(integration.id))
|
|
136
175
|
continue; // Found it already somewhere
|
|
137
176
|
let reason = "";
|
|
138
177
|
let matchedPattern = "";
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
178
|
+
if (isDocker) {
|
|
179
|
+
// Specialized Dockerfile scanning: scan the exact full content
|
|
180
|
+
matchedPattern = matchPatterns(contentRaw, integration.dockerPatterns) || "";
|
|
181
|
+
if (matchedPattern) {
|
|
182
|
+
reason = `Found deployment marker "${matchedPattern}" in ${fileName}`;
|
|
183
|
+
}
|
|
142
184
|
}
|
|
143
185
|
else {
|
|
144
|
-
|
|
186
|
+
// Standard source code checks
|
|
187
|
+
matchedPattern = matchPatterns(content, integration.urlPatterns) || "";
|
|
145
188
|
if (matchedPattern) {
|
|
146
|
-
reason = `Found
|
|
189
|
+
reason = `Found API URL pattern "${matchedPattern}"`;
|
|
147
190
|
}
|
|
148
191
|
else {
|
|
149
|
-
matchedPattern = matchPatterns(content, integration.
|
|
192
|
+
matchedPattern = matchPatterns(content, integration.importPatterns) || "";
|
|
150
193
|
if (matchedPattern) {
|
|
151
|
-
reason = `Found
|
|
194
|
+
reason = `Found SDK import pattern "${matchedPattern}"`;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
matchedPattern = matchPatterns(content, integration.envPatterns) || "";
|
|
198
|
+
if (matchedPattern) {
|
|
199
|
+
reason = `Found env var reference "${matchedPattern}"`;
|
|
200
|
+
}
|
|
152
201
|
}
|
|
153
202
|
}
|
|
154
203
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface IntegrationDef {
|
|
|
25
25
|
urlPatterns?: string[];
|
|
26
26
|
importPatterns?: string[];
|
|
27
27
|
envPatterns?: string[];
|
|
28
|
+
configPatterns?: string[];
|
|
29
|
+
dockerPatterns?: string[];
|
|
28
30
|
}
|
|
29
31
|
export declare const INTEGRATIONS: IntegrationDef[];
|
|
30
32
|
export declare const SOURCE_EXTENSIONS: string[];
|
package/dist/constants.js
CHANGED
|
@@ -154,41 +154,73 @@ exports.INTEGRATIONS = [
|
|
|
154
154
|
id: "aws",
|
|
155
155
|
name: "AWS",
|
|
156
156
|
importPatterns: ["boto3", "aws-sdk", "@aws-sdk"],
|
|
157
|
+
configPatterns: ["buildspec.yml", "samconfig.toml", "serverless.yml"],
|
|
157
158
|
envPatterns: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_DEFAULT_REGION"],
|
|
159
|
+
dockerPatterns: ["public.ecr.aws", "amazonaws.com"],
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
162
|
id: "azure",
|
|
161
163
|
name: "Microsoft Azure",
|
|
162
164
|
importPatterns: ["azure", "@azure"],
|
|
163
165
|
urlPatterns: ["windows.net", "azure.com"],
|
|
166
|
+
configPatterns: ["azure-pipelines.yml"],
|
|
164
167
|
envPatterns: ["AZURE_CLIENT_ID", "AZURE_TENANT_ID"],
|
|
168
|
+
dockerPatterns: ["mcr.microsoft.com", "azurecr.io"],
|
|
165
169
|
},
|
|
166
170
|
{
|
|
167
171
|
id: "gcp",
|
|
168
172
|
name: "Google Cloud",
|
|
169
173
|
importPatterns: ["google-cloud", "@google-cloud"],
|
|
170
174
|
urlPatterns: ["googleapis.com"],
|
|
175
|
+
configPatterns: ["app.yaml", "cloudbuild.yaml"],
|
|
171
176
|
envPatterns: ["GOOGLE_APPLICATION_CREDENTIALS"],
|
|
177
|
+
dockerPatterns: ["gcr.io", "pkg.dev"],
|
|
172
178
|
},
|
|
173
179
|
// Hosting & Edge
|
|
174
180
|
{
|
|
175
181
|
id: "vercel",
|
|
176
182
|
name: "Vercel",
|
|
177
183
|
importPatterns: ["@vercel"],
|
|
184
|
+
configPatterns: ["vercel.json"],
|
|
178
185
|
envPatterns: ["VERCEL_URL", "VERCEL_PROJECT_ID"],
|
|
179
186
|
},
|
|
180
187
|
{
|
|
181
188
|
id: "netlify",
|
|
182
189
|
name: "Netlify",
|
|
183
190
|
importPatterns: ["@netlify"],
|
|
184
|
-
|
|
191
|
+
configPatterns: ["netlify.toml"],
|
|
192
|
+
envPatterns: ["NETLIFY", "NETLIFY_AUTH_TOKEN"], // Removed generic URL
|
|
185
193
|
},
|
|
186
194
|
{
|
|
187
195
|
id: "cloudflare",
|
|
188
196
|
name: "Cloudflare",
|
|
189
197
|
importPatterns: ["cloudflare", "@cloudflare"],
|
|
198
|
+
configPatterns: ["wrangler.toml"],
|
|
190
199
|
envPatterns: ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"],
|
|
191
200
|
},
|
|
201
|
+
{
|
|
202
|
+
id: "flyio",
|
|
203
|
+
name: "Fly.io",
|
|
204
|
+
configPatterns: ["fly.toml"],
|
|
205
|
+
dockerPatterns: ["flyio", "flyctl"],
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: "railway",
|
|
209
|
+
name: "Railway",
|
|
210
|
+
configPatterns: ["railway.json", "railway.toml"],
|
|
211
|
+
dockerPatterns: ["railwayapp"],
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "render",
|
|
215
|
+
name: "Render",
|
|
216
|
+
configPatterns: ["render.yaml"],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "digitalocean",
|
|
220
|
+
name: "DigitalOcean",
|
|
221
|
+
configPatterns: ["app-spec.yaml", "digitalocean.yaml"],
|
|
222
|
+
dockerPatterns: ["digitalocean"],
|
|
223
|
+
},
|
|
192
224
|
// Firebase
|
|
193
225
|
{
|
|
194
226
|
id: "firebase",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thelogicatelier/sylva",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Auto-generate AGENTS.md for your repository using Ax-LLM. Analyze the structural backbone, data flow, and day-to-day coding conventions natively.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -64,4 +64,4 @@
|
|
|
64
64
|
"prettier": "^3.8.1",
|
|
65
65
|
"typescript-eslint": "^8.56.1"
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
}
|