@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.
Files changed (39) hide show
  1. package/README.md +20 -0
  2. package/dist/awareness/braveSearch.d.ts +18 -0
  3. package/dist/awareness/braveSearch.js +134 -0
  4. package/dist/awareness/detector.d.ts +18 -0
  5. package/dist/awareness/detector.js +176 -0
  6. package/dist/awareness/index.d.ts +11 -0
  7. package/dist/awareness/index.js +259 -0
  8. package/dist/awareness/manifestParsers/dotnetParsers.d.ts +13 -0
  9. package/dist/awareness/manifestParsers/dotnetParsers.js +151 -0
  10. package/dist/awareness/manifestParsers/goParsers.d.ts +9 -0
  11. package/dist/awareness/manifestParsers/goParsers.js +137 -0
  12. package/dist/awareness/manifestParsers/index.d.ts +13 -0
  13. package/dist/awareness/manifestParsers/index.js +182 -0
  14. package/dist/awareness/manifestParsers/javaParsers.d.ts +13 -0
  15. package/dist/awareness/manifestParsers/javaParsers.js +243 -0
  16. package/dist/awareness/manifestParsers/openclawParser.d.ts +6 -0
  17. package/dist/awareness/manifestParsers/openclawParser.js +103 -0
  18. package/dist/awareness/manifestParsers/packageJsonParser.d.ts +7 -0
  19. package/dist/awareness/manifestParsers/packageJsonParser.js +209 -0
  20. package/dist/awareness/manifestParsers/pythonParsers.d.ts +21 -0
  21. package/dist/awareness/manifestParsers/pythonParsers.js +344 -0
  22. package/dist/awareness/manifestParsers/rustParsers.d.ts +9 -0
  23. package/dist/awareness/manifestParsers/rustParsers.js +153 -0
  24. package/dist/awareness/manifestScanner.d.ts +11 -0
  25. package/dist/awareness/manifestScanner.js +182 -0
  26. package/dist/awareness/types.d.ts +105 -0
  27. package/dist/awareness/types.js +6 -0
  28. package/dist/awareness/versionResolver.d.ts +17 -0
  29. package/dist/awareness/versionResolver.js +62 -0
  30. package/dist/awareness/webGrounding.d.ts +20 -0
  31. package/dist/awareness/webGrounding.js +102 -0
  32. package/dist/cli.js +19 -4
  33. package/dist/constants.js +11 -0
  34. package/dist/modules.d.ts +5 -2
  35. package/dist/modules.js +17 -4
  36. package/dist/prompts.d.ts +6 -0
  37. package/dist/prompts.js +5 -2
  38. package/dist/utils.js +12 -6
  39. package/package.json +2 -2
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * Java/JVM manifest parsers.
4
+ * Handles pom.xml and build.gradle(.kts) with regex-based parsing.
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.parsePomXml = parsePomXml;
41
+ exports.parseBuildGradle = parseBuildGradle;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ /** Known Java frameworks */
45
+ const JAVA_FRAMEWORKS = [
46
+ {
47
+ groupArtifact: "org.springframework.boot",
48
+ frameworkId: "spring-boot",
49
+ frameworkName: "Spring Boot",
50
+ },
51
+ {
52
+ groupArtifact: "org.springframework",
53
+ frameworkId: "spring",
54
+ frameworkName: "Spring Framework",
55
+ },
56
+ { groupArtifact: "io.quarkus", frameworkId: "quarkus", frameworkName: "Quarkus" },
57
+ { groupArtifact: "io.micronaut", frameworkId: "micronaut", frameworkName: "Micronaut" },
58
+ { groupArtifact: "org.hibernate", frameworkId: "hibernate", frameworkName: "Hibernate" },
59
+ { groupArtifact: "junit", frameworkId: "junit", frameworkName: "JUnit" },
60
+ { groupArtifact: "org.junit", frameworkId: "junit5", frameworkName: "JUnit 5" },
61
+ { groupArtifact: "org.apache.kafka", frameworkId: "kafka", frameworkName: "Apache Kafka" },
62
+ ];
63
+ /**
64
+ * Parse pom.xml (Maven)
65
+ */
66
+ function parsePomXml(manifest) {
67
+ const signals = [];
68
+ const content = fs.readFileSync(manifest.absolutePath, "utf-8");
69
+ const rootPath = path.dirname(manifest.relativePath) || ".";
70
+ signals.push({
71
+ kind: "framework",
72
+ frameworkId: "java-maven",
73
+ frameworkName: "Java (Maven)",
74
+ evidence: {
75
+ file: manifest.relativePath,
76
+ reason: "pom.xml found",
77
+ },
78
+ scope: { pathRoot: rootPath },
79
+ });
80
+ // Extract properties for version resolution
81
+ const properties = {};
82
+ const propsMatch = content.match(/<properties>([\s\S]*?)<\/properties>/);
83
+ if (propsMatch) {
84
+ const propRegex = /<([a-zA-Z0-9._-]+)>([^<]+)<\/\1>/g;
85
+ let m;
86
+ while ((m = propRegex.exec(propsMatch[1])) !== null) {
87
+ properties[m[1]] = m[2];
88
+ }
89
+ }
90
+ // Extract Java version
91
+ const javaVersion = properties["java.version"] || properties["maven.compiler.source"];
92
+ if (javaVersion) {
93
+ signals.push({
94
+ kind: "version",
95
+ frameworkId: "java",
96
+ frameworkName: "Java",
97
+ version: { value: javaVersion, certainty: "exact", sourceFile: manifest.relativePath },
98
+ evidence: {
99
+ file: manifest.relativePath,
100
+ reason: "Java version specified in pom.xml properties",
101
+ excerpt: `java.version = ${javaVersion}`,
102
+ },
103
+ scope: { pathRoot: rootPath },
104
+ });
105
+ }
106
+ // Extract parent (Spring Boot parent, etc.)
107
+ const parentMatch = content.match(/<parent>[\s\S]*?<groupId>([^<]+)<\/groupId>[\s\S]*?<version>([^<]+)<\/version>[\s\S]*?<\/parent>/);
108
+ if (parentMatch) {
109
+ const parentGroup = parentMatch[1];
110
+ const parentVersion = parentMatch[2];
111
+ const framework = JAVA_FRAMEWORKS.find((f) => parentGroup.includes(f.groupArtifact));
112
+ if (framework) {
113
+ const resolvedVersion = parentVersion.startsWith("${")
114
+ ? properties[parentVersion.slice(2, -1)]
115
+ : parentVersion;
116
+ const versionInfo = resolvedVersion
117
+ ? { value: resolvedVersion, certainty: "exact", sourceFile: manifest.relativePath }
118
+ : {
119
+ certainty: "ambiguous",
120
+ sourceFile: manifest.relativePath,
121
+ notes: `Unresolved property: ${parentVersion}`,
122
+ };
123
+ signals.push({
124
+ kind: "framework",
125
+ frameworkId: framework.frameworkId,
126
+ frameworkName: framework.frameworkName,
127
+ version: versionInfo,
128
+ evidence: {
129
+ file: manifest.relativePath,
130
+ reason: `Parent POM references ${framework.frameworkName}`,
131
+ excerpt: `<groupId>${parentGroup}</groupId> <version>${parentVersion}</version>`,
132
+ },
133
+ scope: { pathRoot: rootPath },
134
+ });
135
+ }
136
+ }
137
+ // Extract dependencies
138
+ const depRegex = /<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>(?:\s*<version>([^<]+)<\/version>)?/g;
139
+ let depMatch;
140
+ while ((depMatch = depRegex.exec(content)) !== null) {
141
+ const groupId = depMatch[1];
142
+ const artifactId = depMatch[2];
143
+ const version = depMatch[3];
144
+ const framework = JAVA_FRAMEWORKS.find((f) => groupId.includes(f.groupArtifact));
145
+ if (framework) {
146
+ let versionInfo;
147
+ if (version) {
148
+ const resolvedVersion = version.startsWith("${")
149
+ ? properties[version.slice(2, -1)]
150
+ : version;
151
+ versionInfo = resolvedVersion
152
+ ? { value: resolvedVersion, certainty: "exact", sourceFile: manifest.relativePath }
153
+ : {
154
+ certainty: "ambiguous",
155
+ sourceFile: manifest.relativePath,
156
+ notes: `Unresolved: ${version}`,
157
+ };
158
+ }
159
+ else {
160
+ versionInfo = {
161
+ certainty: "unknown",
162
+ sourceFile: manifest.relativePath,
163
+ notes: "Version managed by parent/BOM",
164
+ };
165
+ }
166
+ signals.push({
167
+ kind: "framework",
168
+ frameworkId: framework.frameworkId,
169
+ frameworkName: framework.frameworkName,
170
+ version: versionInfo,
171
+ evidence: {
172
+ file: manifest.relativePath,
173
+ reason: `Dependency ${groupId}:${artifactId} in pom.xml`,
174
+ excerpt: `<groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId>`,
175
+ },
176
+ scope: { pathRoot: rootPath },
177
+ });
178
+ }
179
+ }
180
+ return signals;
181
+ }
182
+ /**
183
+ * Parse build.gradle or build.gradle.kts (Gradle)
184
+ */
185
+ function parseBuildGradle(manifest) {
186
+ const signals = [];
187
+ const content = fs.readFileSync(manifest.absolutePath, "utf-8");
188
+ const rootPath = path.dirname(manifest.relativePath) || ".";
189
+ const isKts = manifest.filename.endsWith(".kts");
190
+ signals.push({
191
+ kind: "framework",
192
+ frameworkId: "java-gradle",
193
+ frameworkName: `Java (Gradle${isKts ? " KTS" : ""})`,
194
+ evidence: {
195
+ file: manifest.relativePath,
196
+ reason: `${manifest.filename} found`,
197
+ },
198
+ scope: { pathRoot: rootPath },
199
+ });
200
+ // Detect Spring Boot plugin
201
+ const springBootPlugin = content.match(/id\s*[("']org\.springframework\.boot[)"']\s*version\s*[("']([^)"']+)[)"']/);
202
+ if (springBootPlugin) {
203
+ signals.push({
204
+ kind: "framework",
205
+ frameworkId: "spring-boot",
206
+ frameworkName: "Spring Boot",
207
+ version: {
208
+ value: springBootPlugin[1],
209
+ certainty: "exact",
210
+ sourceFile: manifest.relativePath,
211
+ },
212
+ evidence: {
213
+ file: manifest.relativePath,
214
+ reason: "Spring Boot Gradle plugin found",
215
+ excerpt: springBootPlugin[0],
216
+ },
217
+ scope: { pathRoot: rootPath },
218
+ });
219
+ }
220
+ // Detect dependencies with versions
221
+ const depRegex = /(?:implementation|api|compile|testImplementation)\s*[("']([^)"':]+):([^)"':]+):([^)"']+)[)"']/g;
222
+ let m;
223
+ while ((m = depRegex.exec(content)) !== null) {
224
+ const groupId = m[1];
225
+ const version = m[3];
226
+ const framework = JAVA_FRAMEWORKS.find((f) => groupId.includes(f.groupArtifact));
227
+ if (framework) {
228
+ signals.push({
229
+ kind: "framework",
230
+ frameworkId: framework.frameworkId,
231
+ frameworkName: framework.frameworkName,
232
+ version: { value: version, certainty: "exact", sourceFile: manifest.relativePath },
233
+ evidence: {
234
+ file: manifest.relativePath,
235
+ reason: `Gradle dependency references ${framework.frameworkName}`,
236
+ excerpt: m[0],
237
+ },
238
+ scope: { pathRoot: rootPath },
239
+ });
240
+ }
241
+ }
242
+ return signals;
243
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * OpenClaw manifest parser.
3
+ * Parses openclaw.json to extract orchestrator configuration.
4
+ */
5
+ import { Signal, ManifestFile } from "../types";
6
+ export declare function parseOpenclawJson(manifest: ManifestFile): Signal[];
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * OpenClaw manifest parser.
4
+ * Parses openclaw.json to extract orchestrator configuration.
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.parseOpenclawJson = parseOpenclawJson;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ function parseOpenclawJson(manifest) {
44
+ const signals = [];
45
+ const content = fs.readFileSync(manifest.absolutePath, "utf-8");
46
+ let config;
47
+ try {
48
+ config = JSON.parse(content);
49
+ }
50
+ catch (error) {
51
+ console.warn(`⚠️ Could not parse ${manifest.relativePath}: ${error.message}\n` +
52
+ ` This file appears to be malformed JSON. OpenClaw orchestrator detection is skipped for this file.\n` +
53
+ ` Fix the JSON syntax to enable OpenClaw orchestrator detection.`);
54
+ return signals;
55
+ }
56
+ const rootPath = path.dirname(manifest.relativePath) || ".";
57
+ // Primary orchestrator signal
58
+ signals.push({
59
+ kind: "orchestrator",
60
+ frameworkId: "openclaw",
61
+ frameworkName: "OpenClaw",
62
+ evidence: {
63
+ file: manifest.relativePath,
64
+ reason: "openclaw.json configuration file found",
65
+ excerpt: content.length > 500 ? content.substring(0, 500) + "..." : content,
66
+ },
67
+ scope: { pathRoot: rootPath },
68
+ });
69
+ // Extract tool signals
70
+ const tools = config.tools;
71
+ if (tools && typeof tools === "object") {
72
+ for (const [toolName, toolConfig] of Object.entries(tools)) {
73
+ signals.push({
74
+ kind: "tooling",
75
+ frameworkId: `openclaw-tool-${toolName}`,
76
+ frameworkName: `OpenClaw Tool: ${toolName}`,
77
+ evidence: {
78
+ file: manifest.relativePath,
79
+ reason: `Tool '${toolName}' configured in openclaw.json`,
80
+ excerpt: JSON.stringify(toolConfig, null, 2).substring(0, 200),
81
+ },
82
+ scope: { pathRoot: rootPath },
83
+ });
84
+ }
85
+ }
86
+ // Extract channel signals
87
+ const channels = config.channels;
88
+ if (channels && typeof channels === "object") {
89
+ for (const [channelName] of Object.entries(channels)) {
90
+ signals.push({
91
+ kind: "tooling",
92
+ frameworkId: `openclaw-channel-${channelName}`,
93
+ frameworkName: `OpenClaw Channel: ${channelName}`,
94
+ evidence: {
95
+ file: manifest.relativePath,
96
+ reason: `Channel '${channelName}' configured in openclaw.json`,
97
+ },
98
+ scope: { pathRoot: rootPath },
99
+ });
100
+ }
101
+ }
102
+ return signals;
103
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * package.json parser.
3
+ * Detects Node.js/JS/TS frameworks from dependencies, scripts, and metadata.
4
+ * Handles React, Angular, Vue, Next.js, Express, NestJS, and more.
5
+ */
6
+ import { Signal, ManifestFile } from "../types";
7
+ export declare function parsePackageJson(manifest: ManifestFile): Signal[];
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * package.json parser.
4
+ * Detects Node.js/JS/TS frameworks from dependencies, scripts, and metadata.
5
+ * Handles React, Angular, Vue, Next.js, Express, NestJS, and more.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.parsePackageJson = parsePackageJson;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ /** Framework detection rules: dep name patterns → framework info */
45
+ const FRAMEWORK_DETECTION_RULES = [
46
+ { depPattern: "@angular/core", frameworkId: "angular", frameworkName: "Angular" },
47
+ { depPattern: "react", frameworkId: "react", frameworkName: "React" },
48
+ { depPattern: "react-dom", frameworkId: "react-dom", frameworkName: "React DOM" },
49
+ { depPattern: "next", frameworkId: "nextjs", frameworkName: "Next.js" },
50
+ { depPattern: "vue", frameworkId: "vue", frameworkName: "Vue.js" },
51
+ { depPattern: "nuxt", frameworkId: "nuxt", frameworkName: "Nuxt" },
52
+ { depPattern: "@sveltejs/kit", frameworkId: "sveltekit", frameworkName: "SvelteKit" },
53
+ { depPattern: "svelte", frameworkId: "svelte", frameworkName: "Svelte" },
54
+ { depPattern: "express", frameworkId: "express", frameworkName: "Express" },
55
+ { depPattern: "@nestjs/core", frameworkId: "nestjs", frameworkName: "NestJS" },
56
+ { depPattern: "fastify", frameworkId: "fastify", frameworkName: "Fastify" },
57
+ { depPattern: "koa", frameworkId: "koa", frameworkName: "Koa" },
58
+ { depPattern: "typescript", frameworkId: "typescript", frameworkName: "TypeScript" },
59
+ { depPattern: "@ax-llm/ax", frameworkId: "ax-llm", frameworkName: "Ax-LLM" },
60
+ { depPattern: "electron", frameworkId: "electron", frameworkName: "Electron" },
61
+ { depPattern: "react-native", frameworkId: "react-native", frameworkName: "React Native" },
62
+ { depPattern: "tailwindcss", frameworkId: "tailwindcss", frameworkName: "Tailwind CSS" },
63
+ { depPattern: "vite", frameworkId: "vite", frameworkName: "Vite" },
64
+ { depPattern: "webpack", frameworkId: "webpack", frameworkName: "Webpack" },
65
+ { depPattern: "esbuild", frameworkId: "esbuild", frameworkName: "esbuild" },
66
+ ];
67
+ /**
68
+ * Determine version certainty from a semver-like string.
69
+ */
70
+ function parseVersionCertainty(versionStr, sourceFile, depName, lockfileAvailable) {
71
+ if (!versionStr) {
72
+ return { certainty: "unknown", sourceFile, notes: `No version specified for ${depName}` };
73
+ }
74
+ // Exact pinned version (no ^, ~, >=, etc.)
75
+ if (/^\d+\.\d+\.\d+/.test(versionStr) && !/^[~^>=<]/.test(versionStr)) {
76
+ return { value: versionStr, certainty: "exact", sourceFile };
77
+ }
78
+ // Range-based version
79
+ const cleanVersion = versionStr.replace(/^[~^>=<]+/, "");
80
+ if (lockfileAvailable) {
81
+ return {
82
+ value: cleanVersion,
83
+ certainty: "ambiguous",
84
+ sourceFile,
85
+ notes: `Range '${versionStr}' in package.json; lockfile exists but not parsed for exact resolution`,
86
+ };
87
+ }
88
+ return {
89
+ value: cleanVersion,
90
+ certainty: "ambiguous",
91
+ sourceFile,
92
+ notes: `Range '${versionStr}' without lockfile resolution`,
93
+ };
94
+ }
95
+ function parsePackageJson(manifest) {
96
+ const signals = [];
97
+ const content = fs.readFileSync(manifest.absolutePath, "utf-8");
98
+ let pkg;
99
+ try {
100
+ pkg = JSON.parse(content);
101
+ }
102
+ catch (error) {
103
+ console.warn(`⚠️ Could not parse ${manifest.relativePath}: ${error.message}\n` +
104
+ ` This package.json has malformed JSON. Node.js framework detection is skipped for this file.`);
105
+ return signals;
106
+ }
107
+ const rootPath = path.dirname(manifest.relativePath) || ".";
108
+ const deps = (pkg.dependencies || {});
109
+ const devDeps = (pkg.devDependencies || {});
110
+ const peerDeps = (pkg.peerDependencies || {});
111
+ const allDeps = { ...peerDeps, ...devDeps, ...deps }; // deps last for precedence
112
+ // Check if a lockfile exists nearby
113
+ const manifestDir = path.dirname(manifest.absolutePath);
114
+ const hasLockfile = fs.existsSync(path.join(manifestDir, "package-lock.json")) ||
115
+ fs.existsSync(path.join(manifestDir, "yarn.lock")) ||
116
+ fs.existsSync(path.join(manifestDir, "pnpm-lock.yaml"));
117
+ // Emit a Node.js/npm signal for the package itself
118
+ signals.push({
119
+ kind: "framework",
120
+ frameworkId: "nodejs",
121
+ frameworkName: "Node.js",
122
+ evidence: {
123
+ file: manifest.relativePath,
124
+ reason: "package.json found",
125
+ },
126
+ scope: { pathRoot: rootPath },
127
+ });
128
+ // Detect frameworks from dependencies
129
+ for (const rule of FRAMEWORK_DETECTION_RULES) {
130
+ const depName = typeof rule.depPattern === "string" ? rule.depPattern : undefined;
131
+ let matchedDep;
132
+ let matchedVersion;
133
+ if (depName && allDeps[depName]) {
134
+ matchedDep = depName;
135
+ matchedVersion = allDeps[depName];
136
+ }
137
+ else if (rule.depPattern instanceof RegExp) {
138
+ for (const [dep, ver] of Object.entries(allDeps)) {
139
+ if (rule.depPattern.test(dep)) {
140
+ matchedDep = dep;
141
+ matchedVersion = ver;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ if (matchedDep && matchedVersion !== undefined) {
147
+ const versionInfo = parseVersionCertainty(matchedVersion, manifest.relativePath, matchedDep, hasLockfile);
148
+ signals.push({
149
+ kind: "framework",
150
+ frameworkId: rule.frameworkId,
151
+ frameworkName: rule.frameworkName,
152
+ version: versionInfo,
153
+ evidence: {
154
+ file: manifest.relativePath,
155
+ reason: `Dependency '${matchedDep}' found in package.json`,
156
+ excerpt: `"${matchedDep}": "${matchedVersion}"`,
157
+ },
158
+ scope: { pathRoot: rootPath },
159
+ });
160
+ }
161
+ }
162
+ // Detect Angular specifically from multiple @angular/* deps
163
+ const angularDeps = Object.entries(allDeps).filter(([dep]) => dep.startsWith("@angular/"));
164
+ if (angularDeps.length > 1) {
165
+ // Angular workspace confirmed (multiple @angular packages)
166
+ const coreDep = angularDeps.find(([dep]) => dep === "@angular/core");
167
+ if (coreDep) {
168
+ const versionInfo = parseVersionCertainty(coreDep[1], manifest.relativePath, "@angular/core", hasLockfile);
169
+ // Only add if not already added
170
+ const alreadyAdded = signals.some((s) => s.frameworkId === "angular");
171
+ if (!alreadyAdded) {
172
+ signals.push({
173
+ kind: "framework",
174
+ frameworkId: "angular",
175
+ frameworkName: "Angular",
176
+ version: versionInfo,
177
+ evidence: {
178
+ file: manifest.relativePath,
179
+ reason: `${angularDeps.length} @angular/* dependencies found`,
180
+ excerpt: angularDeps
181
+ .slice(0, 5)
182
+ .map(([d, v]) => `"${d}": "${v}"`)
183
+ .join(", "),
184
+ },
185
+ scope: { pathRoot: rootPath },
186
+ });
187
+ }
188
+ }
189
+ }
190
+ // Detect build tools from scripts
191
+ const scripts = (pkg.scripts || {});
192
+ if (scripts.build || scripts.start || scripts.dev) {
193
+ signals.push({
194
+ kind: "entrypoint",
195
+ frameworkId: "npm-scripts",
196
+ frameworkName: "npm scripts",
197
+ evidence: {
198
+ file: manifest.relativePath,
199
+ reason: "Build/start/dev scripts found in package.json",
200
+ excerpt: Object.entries(scripts)
201
+ .slice(0, 5)
202
+ .map(([k, v]) => `"${k}": "${v}"`)
203
+ .join(", "),
204
+ },
205
+ scope: { pathRoot: rootPath },
206
+ });
207
+ }
208
+ return signals;
209
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Python manifest parsers.
3
+ * Handles requirements.txt, pyproject.toml, poetry.lock, Pipfile, Pipfile.lock, setup.cfg.
4
+ */
5
+ import { Signal, ManifestFile } from "../types";
6
+ /**
7
+ * Parse requirements.txt
8
+ */
9
+ export declare function parseRequirementsTxt(manifest: ManifestFile): Signal[];
10
+ /**
11
+ * Parse pyproject.toml (basic TOML parsing with regex — no external dependency)
12
+ */
13
+ export declare function parsePyprojectToml(manifest: ManifestFile): Signal[];
14
+ /**
15
+ * Parse Pipfile (basic TOML-like parsing)
16
+ */
17
+ export declare function parsePipfile(manifest: ManifestFile): Signal[];
18
+ /**
19
+ * Parse setup.cfg (ini-like)
20
+ */
21
+ export declare function parseSetupCfg(manifest: ManifestFile): Signal[];