@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,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Full-repo manifest scanner.
|
|
4
|
+
* Recursively walks the entire repository to discover framework manifest files,
|
|
5
|
+
* regardless of nesting depth. Handles monorepos and nested subprojects.
|
|
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.scanManifests = scanManifests;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
/** 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
|
+
]);
|
|
72
|
+
/** 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
|
+
"angular.json",
|
|
80
|
+
"workspace.json",
|
|
81
|
+
"project.json",
|
|
82
|
+
"pyproject.toml",
|
|
83
|
+
"requirements.txt",
|
|
84
|
+
"poetry.lock",
|
|
85
|
+
"Pipfile",
|
|
86
|
+
"Pipfile.lock",
|
|
87
|
+
"setup.cfg",
|
|
88
|
+
"setup.py",
|
|
89
|
+
"pom.xml",
|
|
90
|
+
"build.gradle",
|
|
91
|
+
"build.gradle.kts",
|
|
92
|
+
"settings.gradle",
|
|
93
|
+
"settings.gradle.kts",
|
|
94
|
+
"gradle.properties",
|
|
95
|
+
"go.mod",
|
|
96
|
+
"go.sum",
|
|
97
|
+
"Cargo.toml",
|
|
98
|
+
"Cargo.lock",
|
|
99
|
+
"global.json",
|
|
100
|
+
"packages.lock.json",
|
|
101
|
+
"Dockerfile",
|
|
102
|
+
"docker-compose.yml",
|
|
103
|
+
"docker-compose.yaml",
|
|
104
|
+
"Makefile",
|
|
105
|
+
".gitlab-ci.yml",
|
|
106
|
+
]);
|
|
107
|
+
/** File extension patterns for manifests (e.g. *.csproj) */
|
|
108
|
+
const MANIFEST_EXTENSION_PATTERNS = [".csproj", ".fsproj", ".vbproj"];
|
|
109
|
+
/** Maximum file size in characters to read */
|
|
110
|
+
const MAX_FILE_SIZE = 500_000;
|
|
111
|
+
/**
|
|
112
|
+
* Check if a filename matches a known manifest pattern.
|
|
113
|
+
*/
|
|
114
|
+
function isManifestFile(filename) {
|
|
115
|
+
if (MANIFEST_EXACT_NAMES.has(filename))
|
|
116
|
+
return true;
|
|
117
|
+
for (const ext of MANIFEST_EXTENSION_PATTERNS) {
|
|
118
|
+
if (filename.endsWith(ext))
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Recursively scan a repository for manifest files.
|
|
125
|
+
* Returns all discovered manifest files with metadata.
|
|
126
|
+
*/
|
|
127
|
+
function scanManifests(repoPath) {
|
|
128
|
+
const results = [];
|
|
129
|
+
const absoluteRoot = path.resolve(repoPath);
|
|
130
|
+
function walk(dir, depth) {
|
|
131
|
+
let entries;
|
|
132
|
+
try {
|
|
133
|
+
entries = fs.readdirSync(dir);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// Permission denied, broken symlink, or other OS-level issue
|
|
137
|
+
console.warn(`⚠️ Skipping unreadable directory: ${path.relative(absoluteRoot, dir) || "."}\n` +
|
|
138
|
+
` Reason: ${error.message}\n` +
|
|
139
|
+
` Manifests inside this directory will not be discovered.`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
// Skip ignored directories and hidden dirs (except specific manifest patterns)
|
|
144
|
+
if (SCAN_IGNORE_DIRS.has(entry))
|
|
145
|
+
continue;
|
|
146
|
+
const fullPath = path.join(dir, entry);
|
|
147
|
+
let stat;
|
|
148
|
+
try {
|
|
149
|
+
stat = fs.statSync(fullPath);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Broken symlink or permission issue — skip silently (individual files are not noteworthy)
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (stat.isDirectory()) {
|
|
156
|
+
// Skip hidden directories (starting with .) except the repo root
|
|
157
|
+
if (entry.startsWith(".") && depth > 0)
|
|
158
|
+
continue;
|
|
159
|
+
walk(fullPath, depth + 1);
|
|
160
|
+
}
|
|
161
|
+
else if (stat.isFile()) {
|
|
162
|
+
if (isManifestFile(entry) && stat.size <= MAX_FILE_SIZE) {
|
|
163
|
+
results.push({
|
|
164
|
+
absolutePath: fullPath,
|
|
165
|
+
relativePath: path.relative(absoluteRoot, fullPath),
|
|
166
|
+
filename: entry,
|
|
167
|
+
depth,
|
|
168
|
+
size: stat.size,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
walk(absoluteRoot, 0);
|
|
175
|
+
// Sort by depth (shallow first) then alphabetically for deterministic order
|
|
176
|
+
results.sort((a, b) => {
|
|
177
|
+
if (a.depth !== b.depth)
|
|
178
|
+
return a.depth - b.depth;
|
|
179
|
+
return a.relativePath.localeCompare(b.relativePath);
|
|
180
|
+
});
|
|
181
|
+
return results;
|
|
182
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the Sylva awareness system.
|
|
3
|
+
* These types are used across manifest scanning, parsing, detection, and web grounding.
|
|
4
|
+
*/
|
|
5
|
+
/** Version certainty levels — NEVER ASSUME */
|
|
6
|
+
export type VersionCertainty = "exact" | "unknown" | "ambiguous";
|
|
7
|
+
/** Version information extracted from a manifest file */
|
|
8
|
+
export interface VersionInfo {
|
|
9
|
+
value?: string;
|
|
10
|
+
certainty: VersionCertainty;
|
|
11
|
+
sourceFile?: string;
|
|
12
|
+
sourcePath?: string;
|
|
13
|
+
notes?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Signal kinds emitted by manifest parsers */
|
|
16
|
+
export type SignalKind = "framework" | "version" | "orchestrator" | "entrypoint" | "tooling";
|
|
17
|
+
/** Evidence for a detected signal */
|
|
18
|
+
export interface SignalEvidence {
|
|
19
|
+
file: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
excerpt?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Scope of a signal — which subproject it belongs to */
|
|
24
|
+
export interface SignalScope {
|
|
25
|
+
pathRoot?: string;
|
|
26
|
+
}
|
|
27
|
+
/** A single signal emitted by a manifest parser */
|
|
28
|
+
export interface Signal {
|
|
29
|
+
kind: SignalKind;
|
|
30
|
+
frameworkId: string;
|
|
31
|
+
frameworkName: string;
|
|
32
|
+
version?: VersionInfo;
|
|
33
|
+
evidence: SignalEvidence;
|
|
34
|
+
scope: SignalScope;
|
|
35
|
+
}
|
|
36
|
+
/** A discovered manifest file */
|
|
37
|
+
export interface ManifestFile {
|
|
38
|
+
absolutePath: string;
|
|
39
|
+
relativePath: string;
|
|
40
|
+
filename: string;
|
|
41
|
+
depth: number;
|
|
42
|
+
size: number;
|
|
43
|
+
}
|
|
44
|
+
/** A detected stack with confidence and evidence */
|
|
45
|
+
export interface StackInfo {
|
|
46
|
+
frameworkId: string;
|
|
47
|
+
frameworkName: string;
|
|
48
|
+
confidence: number;
|
|
49
|
+
versions: VersionInfo[];
|
|
50
|
+
evidence: Signal[];
|
|
51
|
+
rootPath?: string;
|
|
52
|
+
}
|
|
53
|
+
/** A workload in the architecture model */
|
|
54
|
+
export interface Workload {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
rootPath: string;
|
|
58
|
+
frameworks: string[];
|
|
59
|
+
entrypoints: string[];
|
|
60
|
+
buildTools: string[];
|
|
61
|
+
evidence: SignalEvidence[];
|
|
62
|
+
}
|
|
63
|
+
/** Orchestrator info (e.g., OpenClaw) */
|
|
64
|
+
export interface OrchestratorInfo {
|
|
65
|
+
id: string;
|
|
66
|
+
configPath: string;
|
|
67
|
+
details: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
/** Architecture model derived from detection */
|
|
70
|
+
export interface ArchitectureModel {
|
|
71
|
+
primaryOrchestrator?: OrchestratorInfo;
|
|
72
|
+
workloads: Workload[];
|
|
73
|
+
repoType: "single" | "monorepo" | "unknown";
|
|
74
|
+
navigation: {
|
|
75
|
+
whatToReadFirst: string[];
|
|
76
|
+
whereThingsLive: string[];
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Reference mode for web grounding */
|
|
80
|
+
export type ReferenceMode = "versioned" | "latest_fallback";
|
|
81
|
+
/** A single web search result */
|
|
82
|
+
export interface WebSearchResult {
|
|
83
|
+
title: string;
|
|
84
|
+
url: string;
|
|
85
|
+
snippet: string;
|
|
86
|
+
}
|
|
87
|
+
/** Grouped web references for a framework */
|
|
88
|
+
export interface WebReference {
|
|
89
|
+
frameworkId: string;
|
|
90
|
+
frameworkName: string;
|
|
91
|
+
versionInfo: VersionInfo;
|
|
92
|
+
referenceMode: ReferenceMode;
|
|
93
|
+
results: WebSearchResult[];
|
|
94
|
+
}
|
|
95
|
+
/** The complete output of the awareness pipeline */
|
|
96
|
+
export interface AwarenessResult {
|
|
97
|
+
manifests: ManifestFile[];
|
|
98
|
+
signals: Signal[];
|
|
99
|
+
stacks: StackInfo[];
|
|
100
|
+
architecture: ArchitectureModel;
|
|
101
|
+
webReferences: WebReference[];
|
|
102
|
+
constraintsBlock: string;
|
|
103
|
+
awarenessContext: string;
|
|
104
|
+
errors: string[];
|
|
105
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version resolver.
|
|
3
|
+
* Consolidates version signals per framework and determines version certainty.
|
|
4
|
+
* Core rule: NEVER ASSUME versions.
|
|
5
|
+
*/
|
|
6
|
+
import { Signal, VersionInfo } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the best version info for a given framework from its signals.
|
|
9
|
+
* Prioritizes exact > ambiguous > unknown.
|
|
10
|
+
* If multiple exact versions exist for different source files, picks the shallowest one.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveVersion(signals: Signal[]): VersionInfo;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve versions for all detected frameworks.
|
|
15
|
+
* Groups signals by frameworkId and resolves each.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveAllVersions(signals: Signal[]): Map<string, VersionInfo>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Version resolver.
|
|
4
|
+
* Consolidates version signals per framework and determines version certainty.
|
|
5
|
+
* Core rule: NEVER ASSUME versions.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.resolveVersion = resolveVersion;
|
|
9
|
+
exports.resolveAllVersions = resolveAllVersions;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the best version info for a given framework from its signals.
|
|
12
|
+
* Prioritizes exact > ambiguous > unknown.
|
|
13
|
+
* If multiple exact versions exist for different source files, picks the shallowest one.
|
|
14
|
+
*/
|
|
15
|
+
function resolveVersion(signals) {
|
|
16
|
+
const versionSignals = signals.filter((s) => s.version);
|
|
17
|
+
if (versionSignals.length === 0) {
|
|
18
|
+
return { certainty: "unknown", notes: "No version information found" };
|
|
19
|
+
}
|
|
20
|
+
// Separate by certainty
|
|
21
|
+
const exact = versionSignals.filter((s) => s.version.certainty === "exact");
|
|
22
|
+
const ambiguous = versionSignals.filter((s) => s.version.certainty === "ambiguous");
|
|
23
|
+
if (exact.length === 1) {
|
|
24
|
+
return exact[0].version;
|
|
25
|
+
}
|
|
26
|
+
if (exact.length > 1) {
|
|
27
|
+
// Multiple exact versions — check if they agree
|
|
28
|
+
const uniqueValues = [...new Set(exact.map((s) => s.version.value))];
|
|
29
|
+
if (uniqueValues.length === 1) {
|
|
30
|
+
return exact[0].version;
|
|
31
|
+
}
|
|
32
|
+
// Conflicting exact versions — mark ambiguous
|
|
33
|
+
return {
|
|
34
|
+
value: uniqueValues[0],
|
|
35
|
+
certainty: "ambiguous",
|
|
36
|
+
sourceFile: exact[0].version.sourceFile,
|
|
37
|
+
notes: `Multiple exact versions found: ${uniqueValues.join(", ")}`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (ambiguous.length > 0) {
|
|
41
|
+
return ambiguous[0].version;
|
|
42
|
+
}
|
|
43
|
+
return { certainty: "unknown", notes: "No resolvable version" };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve versions for all detected frameworks.
|
|
47
|
+
* Groups signals by frameworkId and resolves each.
|
|
48
|
+
*/
|
|
49
|
+
function resolveAllVersions(signals) {
|
|
50
|
+
const grouped = new Map();
|
|
51
|
+
for (const signal of signals) {
|
|
52
|
+
if (!grouped.has(signal.frameworkId)) {
|
|
53
|
+
grouped.set(signal.frameworkId, []);
|
|
54
|
+
}
|
|
55
|
+
grouped.get(signal.frameworkId).push(signal);
|
|
56
|
+
}
|
|
57
|
+
const resolved = new Map();
|
|
58
|
+
for (const [frameworkId, frameworkSignals] of grouped) {
|
|
59
|
+
resolved.set(frameworkId, resolveVersion(frameworkSignals));
|
|
60
|
+
}
|
|
61
|
+
return resolved;
|
|
62
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web grounding — version-aware query builder and reference gatherer.
|
|
3
|
+
* Uses Brave Search to fetch official docs for detected frameworks.
|
|
4
|
+
* NEVER ASSUMES — queries are version-specific only when exact version is known.
|
|
5
|
+
*/
|
|
6
|
+
import { StackInfo, VersionInfo, WebReference } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Build search queries for a framework based on version info.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildQueries(frameworkName: string, versionInfo: VersionInfo): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Gather web references for detected frameworks.
|
|
13
|
+
* @param stacks Detected stacks sorted by confidence
|
|
14
|
+
* @param limitPerFramework Max queries per framework
|
|
15
|
+
* @param cacheDir Optional cache directory
|
|
16
|
+
*/
|
|
17
|
+
export declare function gatherReferences(stacks: StackInfo[], limitPerFramework?: number, cacheDir?: string): Promise<{
|
|
18
|
+
references: WebReference[];
|
|
19
|
+
errors: string[];
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Web grounding — version-aware query builder and reference gatherer.
|
|
4
|
+
* Uses Brave Search to fetch official docs for detected frameworks.
|
|
5
|
+
* NEVER ASSUMES — queries are version-specific only when exact version is known.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildQueries = buildQueries;
|
|
9
|
+
exports.gatherReferences = gatherReferences;
|
|
10
|
+
const braveSearch_1 = require("./braveSearch");
|
|
11
|
+
const versionResolver_1 = require("./versionResolver");
|
|
12
|
+
/**
|
|
13
|
+
* Build search queries for a framework based on version info.
|
|
14
|
+
*/
|
|
15
|
+
function buildQueries(frameworkName, versionInfo) {
|
|
16
|
+
const queries = [];
|
|
17
|
+
if (versionInfo.certainty === "exact" && versionInfo.value) {
|
|
18
|
+
// Extract major.minor from version
|
|
19
|
+
const parts = versionInfo.value.replace(/^v/, "").split(".");
|
|
20
|
+
const major = parts[0];
|
|
21
|
+
const minor = parts.length > 1 ? parts[1] : undefined;
|
|
22
|
+
const majorMinor = minor ? `${major}.${minor}` : major;
|
|
23
|
+
queries.push(`${frameworkName} ${major} documentation official`);
|
|
24
|
+
if (minor) {
|
|
25
|
+
queries.push(`${frameworkName} ${majorMinor} reference documentation`);
|
|
26
|
+
}
|
|
27
|
+
queries.push(`${frameworkName} ${major} best practices`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Unknown or ambiguous — use latest docs
|
|
31
|
+
queries.push(`${frameworkName} official documentation`);
|
|
32
|
+
queries.push(`${frameworkName} best practices`);
|
|
33
|
+
}
|
|
34
|
+
return queries;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Gather web references for detected frameworks.
|
|
38
|
+
* @param stacks Detected stacks sorted by confidence
|
|
39
|
+
* @param limitPerFramework Max queries per framework
|
|
40
|
+
* @param cacheDir Optional cache directory
|
|
41
|
+
*/
|
|
42
|
+
async function gatherReferences(stacks, limitPerFramework = 3, cacheDir) {
|
|
43
|
+
const references = [];
|
|
44
|
+
const errors = [];
|
|
45
|
+
if (!(0, braveSearch_1.isBraveSearchAvailable)()) {
|
|
46
|
+
errors.push("BRAVE_API_KEY not set — web grounding is disabled.\n" +
|
|
47
|
+
" → Framework detection and version resolution still work perfectly.\n" +
|
|
48
|
+
" → To enable web-grounded documentation references, set BRAVE_API_KEY in your .env file.\n" +
|
|
49
|
+
" → Get a free API key at: https://brave.com/search/api/");
|
|
50
|
+
return { references, errors };
|
|
51
|
+
}
|
|
52
|
+
// Pick top frameworks by confidence (skip generic ones like "nodejs", "python")
|
|
53
|
+
const GENERIC_IDS = new Set([
|
|
54
|
+
"nodejs",
|
|
55
|
+
"python",
|
|
56
|
+
"java-maven",
|
|
57
|
+
"java-gradle",
|
|
58
|
+
"dotnet",
|
|
59
|
+
"docker",
|
|
60
|
+
]);
|
|
61
|
+
const targetStacks = stacks
|
|
62
|
+
.filter((s) => !GENERIC_IDS.has(s.frameworkId) && s.confidence >= 60)
|
|
63
|
+
.slice(0, 6);
|
|
64
|
+
// Always include orchestrators
|
|
65
|
+
const orchestratorStacks = stacks.filter((s) => s.evidence.some((e) => e.kind === "orchestrator"));
|
|
66
|
+
for (const orc of orchestratorStacks) {
|
|
67
|
+
if (!targetStacks.some((t) => t.frameworkId === orc.frameworkId)) {
|
|
68
|
+
targetStacks.push(orc);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const stack of targetStacks) {
|
|
72
|
+
const versionInfo = (0, versionResolver_1.resolveVersion)(stack.evidence);
|
|
73
|
+
const queries = buildQueries(stack.frameworkName, versionInfo);
|
|
74
|
+
const referenceMode = versionInfo.certainty === "exact" ? "versioned" : "latest_fallback";
|
|
75
|
+
const allResults = [];
|
|
76
|
+
for (const query of queries.slice(0, limitPerFramework)) {
|
|
77
|
+
try {
|
|
78
|
+
const results = await (0, braveSearch_1.braveSearch)(query, { cacheDir });
|
|
79
|
+
allResults.push(...results);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
errors.push(`Web search failed for "${query}": ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Dedupe by URL
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
const deduped = allResults.filter((r) => {
|
|
88
|
+
if (seen.has(r.url))
|
|
89
|
+
return false;
|
|
90
|
+
seen.add(r.url);
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
references.push({
|
|
94
|
+
frameworkId: stack.frameworkId,
|
|
95
|
+
frameworkName: stack.frameworkName,
|
|
96
|
+
versionInfo,
|
|
97
|
+
referenceMode: referenceMode,
|
|
98
|
+
results: deduped.slice(0, 5),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return { references, errors };
|
|
102
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,7 @@ const readline = __importStar(require("readline/promises"));
|
|
|
43
43
|
const modelConfig_1 = require("./modelConfig");
|
|
44
44
|
const utils_1 = require("./utils");
|
|
45
45
|
const modules_1 = require("./modules");
|
|
46
|
+
const awareness_1 = require("./awareness");
|
|
46
47
|
function initEnvironment() {
|
|
47
48
|
const envPath = path.resolve(process.cwd(), ".env");
|
|
48
49
|
if (fs.existsSync(envPath)) {
|
|
@@ -97,23 +98,32 @@ async function runPipeline(repoDir, repoName, llmPrimary, llmMini, maxIterations
|
|
|
97
98
|
const sourceTree = (0, utils_1.loadSourceTree)(repoDir);
|
|
98
99
|
const numFiles = Object.keys(sourceTree).length;
|
|
99
100
|
if (numFiles === 0) {
|
|
100
|
-
console.warn("\n⚠️
|
|
101
|
+
console.warn("\n⚠️ The loaded source tree is empty — no supported files found.\n" +
|
|
102
|
+
" Possible causes:\n" +
|
|
103
|
+
" • The repository directory doesn't exist or is empty\n" +
|
|
104
|
+
" • All files are filtered by IGNORED_DIRS (node_modules, dist, etc.)\n" +
|
|
105
|
+
" • No files match ALLOWED_EXTENSIONS (.ts, .py, .java, .go, etc.)\n" +
|
|
106
|
+
" Run with --help to check your --local-repository or --github-repository path.");
|
|
101
107
|
}
|
|
102
108
|
else {
|
|
103
109
|
console.log(`✅ Extracted representation for ${numFiles} top-level file(s)/directory(ies).`);
|
|
104
110
|
}
|
|
111
|
+
// Run Framework Awareness (deterministic, before LLM)
|
|
112
|
+
const awarenessResult = await (0, awareness_1.runAwareness)(repoDir, repoName);
|
|
113
|
+
const awarenessContext = awarenessResult.awarenessContext;
|
|
105
114
|
const extractor = new modules_1.CodebaseConventionExtractor(maxIterations);
|
|
106
|
-
const extractResult = await extractor.extract(sourceTree);
|
|
115
|
+
const extractResult = await extractor.extract(sourceTree, awarenessContext);
|
|
107
116
|
// Use the PRIMARY model for RLM analysis (needs strong reasoning to avoid hallucination)
|
|
108
117
|
console.log(`=> Running the Codebase Analyzer RLM workflow...`);
|
|
109
118
|
const rlmResult = await extractResult.analyzer.forward(llmPrimary, {
|
|
110
119
|
sourceContext: extractResult.contextString,
|
|
120
|
+
awarenessContext,
|
|
111
121
|
});
|
|
112
122
|
// Use the PRIMARY model for compiling conventions (needs to accurately synthesize)
|
|
113
123
|
const conventionsMarkdown = await extractor.compileMarkdown(llmPrimary, rlmResult);
|
|
114
124
|
// Use MINI model for section extraction (cheaper, deterministic task)
|
|
115
125
|
const creator = new modules_1.AgentsMdCreator();
|
|
116
|
-
const sections = await creator.extractAndCompileSections(llmMini, conventionsMarkdown, repoName);
|
|
126
|
+
const sections = await creator.extractAndCompileSections(llmMini, conventionsMarkdown, repoName, awarenessContext);
|
|
117
127
|
const finalAgentsMd = (0, utils_1.compileAgentsMd)(sections, repoName);
|
|
118
128
|
(0, utils_1.saveAgentsToDisk)(repoName, finalAgentsMd);
|
|
119
129
|
console.log("\n======================================================");
|
|
@@ -166,7 +176,12 @@ async function main() {
|
|
|
166
176
|
process.exit(0);
|
|
167
177
|
}
|
|
168
178
|
catch (err) {
|
|
169
|
-
console.error(`\n❌
|
|
179
|
+
console.error(`\n❌ Pipeline failed: ${err.message}\n` +
|
|
180
|
+
` If this is an API error, check that your API key is valid and has sufficient quota.\n` +
|
|
181
|
+
` If this is unexpected, report it at: https://github.com/achatt89/sylva/issues`);
|
|
182
|
+
if (err.stack) {
|
|
183
|
+
console.error(`\n Stack trace:\n ${err.stack.split("\n").slice(1, 5).join("\n ")}`);
|
|
184
|
+
}
|
|
170
185
|
process.exit(1);
|
|
171
186
|
}
|
|
172
187
|
});
|
package/dist/constants.js
CHANGED
|
@@ -75,6 +75,11 @@ exports.ALLOWED_EXTENSIONS = new Set([
|
|
|
75
75
|
".r",
|
|
76
76
|
".m",
|
|
77
77
|
".pl",
|
|
78
|
+
".lock",
|
|
79
|
+
".cfg",
|
|
80
|
+
".csproj",
|
|
81
|
+
".fsproj",
|
|
82
|
+
".gradle",
|
|
78
83
|
]);
|
|
79
84
|
exports.IGNORED_DIRS = new Set([
|
|
80
85
|
"node_modules",
|
|
@@ -119,4 +124,10 @@ exports.DEPENDENCY_MANIFESTS = new Set([
|
|
|
119
124
|
"docker-compose.yml",
|
|
120
125
|
"docker-compose.yaml",
|
|
121
126
|
"package.json",
|
|
127
|
+
"openclaw.json",
|
|
128
|
+
"angular.json",
|
|
129
|
+
"workspace.json",
|
|
130
|
+
"global.json",
|
|
131
|
+
"Cargo.lock",
|
|
132
|
+
"go.mod",
|
|
122
133
|
]);
|
package/dist/modules.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { AgentsMdSections, TreeType } from "./utils";
|
|
2
2
|
export declare class CodebaseConventionExtractor {
|
|
3
3
|
private maxIterations;
|
|
4
|
+
private awarenessContext?;
|
|
4
5
|
constructor(maxIterations?: number);
|
|
5
6
|
extract(sourceTree: {
|
|
6
7
|
[key: string]: TreeType;
|
|
7
|
-
}): Promise<{
|
|
8
|
+
}, awarenessContext?: string): Promise<{
|
|
8
9
|
analyzer: import("@ax-llm/ax").AxAgent<{
|
|
9
10
|
readonly sourceContext: string;
|
|
11
|
+
} & {
|
|
12
|
+
readonly awarenessContext: string;
|
|
10
13
|
}, {
|
|
11
14
|
readonly projectOverview: string;
|
|
12
15
|
} & {
|
|
@@ -49,5 +52,5 @@ export declare class CodebaseConventionExtractor {
|
|
|
49
52
|
compileMarkdown(llm: any, extractResult: any): Promise<string>;
|
|
50
53
|
}
|
|
51
54
|
export declare class AgentsMdCreator {
|
|
52
|
-
extractAndCompileSections(llm: any, conventionsMarkdown: string, repositoryName: string): Promise<AgentsMdSections>;
|
|
55
|
+
extractAndCompileSections(llm: any, conventionsMarkdown: string, repositoryName: string, awarenessContext?: string): Promise<AgentsMdSections>;
|
|
53
56
|
}
|
package/dist/modules.js
CHANGED
|
@@ -20,12 +20,19 @@ function serializeSourceTree(tree, indent = "") {
|
|
|
20
20
|
}
|
|
21
21
|
class CodebaseConventionExtractor {
|
|
22
22
|
maxIterations;
|
|
23
|
+
awarenessContext;
|
|
23
24
|
constructor(maxIterations = 35) {
|
|
24
25
|
this.maxIterations = maxIterations;
|
|
25
26
|
}
|
|
26
|
-
async extract(sourceTree) {
|
|
27
|
+
async extract(sourceTree, awarenessContext) {
|
|
27
28
|
console.log("=> Preparing and serializing Source Tree for RLM analysis...");
|
|
28
|
-
|
|
29
|
+
let contextString = serializeSourceTree(sourceTree);
|
|
30
|
+
// Store awareness context for use in subsequent pipeline steps
|
|
31
|
+
this.awarenessContext = awarenessContext;
|
|
32
|
+
// Prepend awareness context if available
|
|
33
|
+
if (awarenessContext) {
|
|
34
|
+
contextString = `${awarenessContext}\n\n---\n\nSOURCE TREE:\n${contextString}`;
|
|
35
|
+
}
|
|
29
36
|
console.log(`=> Running AxAgent (RLM) for Codebase Analysis on ${Object.keys(sourceTree).length} root modules...`);
|
|
30
37
|
// Use the f() builder for reliable type definition instead of long strings
|
|
31
38
|
const agentSig = prompts_1.CODEBASE_ANALYSIS_SIGNATURE;
|
|
@@ -48,13 +55,18 @@ class CodebaseConventionExtractor {
|
|
|
48
55
|
// Equivalent to dspy.ChainOfThought(CompileConventionsMarkdown)
|
|
49
56
|
const compileSig = prompts_1.COMPILE_CONVENTIONS_SIGNATURE;
|
|
50
57
|
const compiler = (0, ax_1.ax)(compileSig);
|
|
51
|
-
|
|
58
|
+
// Inject awarenessContext into the compilation step so constraints flow through
|
|
59
|
+
const inputWithAwareness = {
|
|
60
|
+
...extractResult,
|
|
61
|
+
awarenessContext: this.awarenessContext || "No framework awareness context available.",
|
|
62
|
+
};
|
|
63
|
+
const finalResult = await compiler.forward(llm, inputWithAwareness);
|
|
52
64
|
return finalResult.markdownDocument;
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
exports.CodebaseConventionExtractor = CodebaseConventionExtractor;
|
|
56
68
|
class AgentsMdCreator {
|
|
57
|
-
async extractAndCompileSections(llm, conventionsMarkdown, repositoryName) {
|
|
69
|
+
async extractAndCompileSections(llm, conventionsMarkdown, repositoryName, awarenessContext) {
|
|
58
70
|
console.log(`=> Extracting individual AGENTS.md sections for repository: ${repositoryName}...`);
|
|
59
71
|
// Equivalent to ExtractAgentsSections via ChainOfThought
|
|
60
72
|
const sectionSig = prompts_1.EXTRACT_AGENTS_SECTIONS_SIGNATURE;
|
|
@@ -62,6 +74,7 @@ class AgentsMdCreator {
|
|
|
62
74
|
const sections = await sectionExtractor.forward(llm, {
|
|
63
75
|
conventionsMarkdown,
|
|
64
76
|
repositoryName,
|
|
77
|
+
awarenessContext: awarenessContext || "No framework awareness context available.",
|
|
65
78
|
});
|
|
66
79
|
return sections;
|
|
67
80
|
}
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const CODEBASE_ANALYSIS_SIGNATURE: import("@ax-llm/ax").AxSignature<{
|
|
2
2
|
readonly sourceContext: string;
|
|
3
|
+
} & {
|
|
4
|
+
readonly awarenessContext: string;
|
|
3
5
|
}, {
|
|
4
6
|
readonly projectOverview: string;
|
|
5
7
|
} & {
|
|
@@ -77,6 +79,8 @@ export declare const COMPILE_CONVENTIONS_SIGNATURE: import("@ax-llm/ax").AxSigna
|
|
|
77
79
|
readonly agentWorkflow: string;
|
|
78
80
|
} & {
|
|
79
81
|
readonly fewShotExamples: string;
|
|
82
|
+
} & {
|
|
83
|
+
readonly awarenessContext: string;
|
|
80
84
|
}, {
|
|
81
85
|
readonly markdownDocument: string;
|
|
82
86
|
}>;
|
|
@@ -84,6 +88,8 @@ export declare const EXTRACT_AGENTS_SECTIONS_SIGNATURE: import("@ax-llm/ax").AxS
|
|
|
84
88
|
readonly conventionsMarkdown: string;
|
|
85
89
|
} & {
|
|
86
90
|
readonly repositoryName: string;
|
|
91
|
+
} & {
|
|
92
|
+
readonly awarenessContext: string;
|
|
87
93
|
}, {
|
|
88
94
|
readonly projectOverview: string;
|
|
89
95
|
} & {
|