spec-gen-cli 1.2.5 → 1.2.7
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 +66 -6
- package/dist/api/analyze.d.ts.map +1 -1
- package/dist/api/analyze.js +6 -1
- package/dist/api/analyze.js.map +1 -1
- package/dist/api/audit.d.ts +10 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +117 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/generate.d.ts.map +1 -1
- package/dist/api/generate.js +9 -1
- package/dist/api/generate.js.map +1 -1
- package/dist/api/index.d.ts +3 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/run.d.ts.map +1 -1
- package/dist/api/run.js +5 -1
- package/dist/api/run.js.map +1 -1
- package/dist/api/types.d.ts +13 -4
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +9 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +98 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +4 -2
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts +327 -2
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +147 -3
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/refresh-stories.d.ts +10 -0
- package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
- package/dist/cli/commands/refresh-stories.js +314 -0
- package/dist/cli/commands/refresh-stories.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
- package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
- package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
- package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
- package/dist/core/analyzer/unified-search.d.ts +107 -0
- package/dist/core/analyzer/unified-search.d.ts.map +1 -0
- package/dist/core/analyzer/unified-search.js +237 -0
- package/dist/core/analyzer/unified-search.js.map +1 -0
- package/dist/core/generator/openspec-format-generator.js +1 -1
- package/dist/core/generator/openspec-format-generator.js.map +1 -1
- package/dist/core/services/chat-tools.d.ts.map +1 -1
- package/dist/core/services/chat-tools.js +54 -6
- package/dist/core/services/chat-tools.js.map +1 -1
- package/dist/core/services/llm-service.d.ts +18 -1
- package/dist/core/services/llm-service.d.ts.map +1 -1
- package/dist/core/services/llm-service.js +112 -4
- package/dist/core/services/llm-service.js.map +1 -1
- package/dist/core/services/mcp-handlers/analysis.d.ts +5 -0
- package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/analysis.js +20 -0
- package/dist/core/services/mcp-handlers/analysis.js.map +1 -1
- package/dist/core/services/mcp-handlers/change.d.ts +14 -0
- package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/change.js +416 -0
- package/dist/core/services/mcp-handlers/change.js.map +1 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts +4 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/semantic.js +101 -32
- package/dist/core/services/mcp-handlers/semantic.js.map +1 -1
- package/dist/types/index.d.ts +70 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/viewer/InteractiveGraphViewer.jsx +46 -4
- package/src/viewer/components/ChatPanel.jsx +2 -3
- package/src/viewer/components/ClusterGraph.jsx +22 -6
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Snapshot Generator
|
|
3
|
+
*
|
|
4
|
+
* Derives a compact coverage summary from existing analysis artifacts —
|
|
5
|
+
* no LLM required. Reads llm-context.json, mapping.json, and spec files
|
|
6
|
+
* to produce spec-snapshot.json.
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import { readFile, stat, readdir, writeFile } from 'node:fs/promises';
|
|
11
|
+
import { join, relative } from 'node:path';
|
|
12
|
+
import { SPEC_GEN_DIR, SPEC_GEN_ANALYSIS_SUBDIR, ARTIFACT_LLM_CONTEXT, ARTIFACT_MAPPING, ARTIFACT_SPEC_SNAPSHOT, OPENSPEC_DIR, OPENSPEC_SPECS_SUBDIR, } from '../../constants.js';
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// GIT HELPERS
|
|
16
|
+
// ============================================================================
|
|
17
|
+
async function getGitState(rootPath) {
|
|
18
|
+
try {
|
|
19
|
+
const [commitResult, branchResult, statusResult] = await Promise.all([
|
|
20
|
+
execFileAsync('git', ['rev-parse', '--short', 'HEAD'], { cwd: rootPath }).catch(() => ({ stdout: '' })),
|
|
21
|
+
execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: rootPath }).catch(() => ({ stdout: '' })),
|
|
22
|
+
execFileAsync('git', ['status', '--porcelain'], { cwd: rootPath }).catch(() => ({ stdout: '' })),
|
|
23
|
+
]);
|
|
24
|
+
return {
|
|
25
|
+
commit: commitResult.stdout.trim() || 'unknown',
|
|
26
|
+
branch: branchResult.stdout.trim() || 'unknown',
|
|
27
|
+
dirty: statusResult.stdout.trim().length > 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { commit: 'unknown', branch: 'unknown', dirty: false };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// SPEC FILE HELPERS
|
|
36
|
+
// ============================================================================
|
|
37
|
+
/** Count H2/H3 headings in a spec.md as a proxy for requirement count. */
|
|
38
|
+
function countRequirements(content) {
|
|
39
|
+
const matches = content.match(/^#{2,3} /gm);
|
|
40
|
+
return matches ? matches.length : 0;
|
|
41
|
+
}
|
|
42
|
+
/** Get max mtime across a list of file paths (returns epoch ISO string if none exist). */
|
|
43
|
+
async function maxMtime(filePaths, rootPath) {
|
|
44
|
+
let max = 0;
|
|
45
|
+
for (const rel of filePaths) {
|
|
46
|
+
try {
|
|
47
|
+
const s = await stat(join(rootPath, rel));
|
|
48
|
+
if (s.mtimeMs > max)
|
|
49
|
+
max = s.mtimeMs;
|
|
50
|
+
}
|
|
51
|
+
catch { /* file may not exist */ }
|
|
52
|
+
}
|
|
53
|
+
return max > 0 ? new Date(max).toISOString() : new Date(0).toISOString();
|
|
54
|
+
}
|
|
55
|
+
async function discoverSpecDomains(openspecPath, rootPath) {
|
|
56
|
+
const specsDir = join(openspecPath, OPENSPEC_SPECS_SUBDIR);
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = await readdir(specsDir);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const domains = [];
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const specFilePath = join(specsDir, entry, 'spec.md');
|
|
67
|
+
try {
|
|
68
|
+
const [content, s] = await Promise.all([
|
|
69
|
+
readFile(specFilePath, 'utf-8'),
|
|
70
|
+
stat(specFilePath),
|
|
71
|
+
]);
|
|
72
|
+
domains.push({
|
|
73
|
+
name: entry,
|
|
74
|
+
specFile: relative(rootPath, specFilePath),
|
|
75
|
+
specModifiedAt: s.mtime.toISOString(),
|
|
76
|
+
requirementCount: countRequirements(content),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch { /* skip missing spec.md */ }
|
|
80
|
+
}
|
|
81
|
+
return domains;
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// COVERAGE COMPUTATION
|
|
85
|
+
// ============================================================================
|
|
86
|
+
function buildCoveredFunctionSet(mapping) {
|
|
87
|
+
const covered = new Set();
|
|
88
|
+
for (const m of mapping.mappings) {
|
|
89
|
+
for (const fn of m.functions) {
|
|
90
|
+
if (fn.name && fn.name !== '*') {
|
|
91
|
+
covered.add(`${fn.file}::${fn.name}`);
|
|
92
|
+
covered.add(fn.name); // also by name alone for looser matching
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return covered;
|
|
97
|
+
}
|
|
98
|
+
function isFunctionCovered(node, covered) {
|
|
99
|
+
const byFileAndName = `${node.filePath}::${node.name}`;
|
|
100
|
+
return covered.has(byFileAndName) || covered.has(node.name);
|
|
101
|
+
}
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// PUBLIC API
|
|
104
|
+
// ============================================================================
|
|
105
|
+
export class SpecSnapshotGenerator {
|
|
106
|
+
rootPath;
|
|
107
|
+
openspecRelPath;
|
|
108
|
+
constructor(rootPath, openspecRelPath = OPENSPEC_DIR) {
|
|
109
|
+
this.rootPath = rootPath;
|
|
110
|
+
this.openspecRelPath = openspecRelPath;
|
|
111
|
+
}
|
|
112
|
+
async generate() {
|
|
113
|
+
const analysisDir = join(this.rootPath, SPEC_GEN_DIR, SPEC_GEN_ANALYSIS_SUBDIR);
|
|
114
|
+
const openspecPath = join(this.rootPath, this.openspecRelPath);
|
|
115
|
+
// Load artifacts in parallel
|
|
116
|
+
const [llmContextRaw, mappingRaw, git, specDomains] = await Promise.all([
|
|
117
|
+
readFile(join(analysisDir, ARTIFACT_LLM_CONTEXT), 'utf-8').catch(() => null),
|
|
118
|
+
readFile(join(analysisDir, ARTIFACT_MAPPING), 'utf-8').catch(() => null),
|
|
119
|
+
getGitState(this.rootPath),
|
|
120
|
+
discoverSpecDomains(openspecPath, this.rootPath),
|
|
121
|
+
]);
|
|
122
|
+
const llmContext = llmContextRaw ? JSON.parse(llmContextRaw) : null;
|
|
123
|
+
const mapping = mappingRaw ? JSON.parse(mappingRaw) : null;
|
|
124
|
+
const callGraph = llmContext?.callGraph;
|
|
125
|
+
const allNodes = callGraph?.nodes ?? [];
|
|
126
|
+
const hubNodes = callGraph?.hubFunctions ?? [];
|
|
127
|
+
// Build coverage index from mapping
|
|
128
|
+
const covered = mapping ? buildCoveredFunctionSet(mapping) : new Set();
|
|
129
|
+
const coveredCount = mapping ? allNodes.filter(n => isFunctionCovered(n, covered)).length : 0;
|
|
130
|
+
const orphanCount = mapping?.orphanFunctions?.length ?? 0;
|
|
131
|
+
const totalFunctions = allNodes.length;
|
|
132
|
+
// Build per-domain info
|
|
133
|
+
// For each spec domain, find which mapping entries belong to it
|
|
134
|
+
const domainMappings = new Map();
|
|
135
|
+
if (mapping) {
|
|
136
|
+
for (const m of mapping.mappings) {
|
|
137
|
+
const entry = domainMappings.get(m.domain) ?? { mappedFunctions: new Set(), sourceFiles: new Set() };
|
|
138
|
+
for (const fn of m.functions) {
|
|
139
|
+
if (fn.name && fn.name !== '*')
|
|
140
|
+
entry.mappedFunctions.add(fn.name);
|
|
141
|
+
if (fn.file && fn.file !== '*')
|
|
142
|
+
entry.sourceFiles.add(fn.file);
|
|
143
|
+
}
|
|
144
|
+
domainMappings.set(m.domain, entry);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const domains = await Promise.all(specDomains.map(async (d) => {
|
|
148
|
+
const dm = domainMappings.get(d.name);
|
|
149
|
+
const sourceFiles = dm ? Array.from(dm.sourceFiles) : [];
|
|
150
|
+
const mappedFunctionCount = dm ? dm.mappedFunctions.size : 0;
|
|
151
|
+
const sourcesModifiedAt = await maxMtime(sourceFiles, this.rootPath);
|
|
152
|
+
const coveragePct = d.requirementCount > 0
|
|
153
|
+
? Math.round((mappedFunctionCount / Math.max(d.requirementCount, mappedFunctionCount)) * 100)
|
|
154
|
+
: 0;
|
|
155
|
+
return {
|
|
156
|
+
name: d.name,
|
|
157
|
+
specFile: d.specFile,
|
|
158
|
+
sourceFiles,
|
|
159
|
+
requirementCount: d.requirementCount,
|
|
160
|
+
mappedFunctionCount,
|
|
161
|
+
coveragePct,
|
|
162
|
+
specModifiedAt: d.specModifiedAt,
|
|
163
|
+
sourcesModifiedAt,
|
|
164
|
+
};
|
|
165
|
+
}));
|
|
166
|
+
// Hub coverage
|
|
167
|
+
const hubs = hubNodes.map(n => ({
|
|
168
|
+
name: n.name,
|
|
169
|
+
file: n.filePath,
|
|
170
|
+
fanIn: n.fanIn,
|
|
171
|
+
covered: isFunctionCovered(n, covered),
|
|
172
|
+
}));
|
|
173
|
+
const snapshot = {
|
|
174
|
+
version: '1',
|
|
175
|
+
generatedAt: new Date().toISOString(),
|
|
176
|
+
git,
|
|
177
|
+
coverage: {
|
|
178
|
+
totalFunctions,
|
|
179
|
+
coveredFunctions: coveredCount,
|
|
180
|
+
orphanFunctions: orphanCount,
|
|
181
|
+
coveragePct: totalFunctions > 0 ? Math.round((coveredCount / totalFunctions) * 100) : 0,
|
|
182
|
+
},
|
|
183
|
+
domains,
|
|
184
|
+
hubs,
|
|
185
|
+
};
|
|
186
|
+
// Persist
|
|
187
|
+
await writeFile(join(analysisDir, ARTIFACT_SPEC_SNAPSHOT), JSON.stringify(snapshot, null, 2));
|
|
188
|
+
return snapshot;
|
|
189
|
+
}
|
|
190
|
+
/** Load a previously generated snapshot, or return null if not found. */
|
|
191
|
+
static async load(rootPath) {
|
|
192
|
+
try {
|
|
193
|
+
const raw = await readFile(join(rootPath, SPEC_GEN_DIR, SPEC_GEN_ANALYSIS_SUBDIR, ARTIFACT_SPEC_SNAPSHOT), 'utf-8');
|
|
194
|
+
return JSON.parse(raw);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=spec-snapshot-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-snapshot-generator.js","sourceRoot":"","sources":["../../../src/core/analyzer/spec-snapshot-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Search
|
|
3
|
+
*
|
|
4
|
+
* Combines code and spec indexes with cross-scoring to boost results
|
|
5
|
+
* that are linked through bidirectional mappings.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const results = await UnifiedSearch.unifiedSearch(
|
|
9
|
+
* outputDir,
|
|
10
|
+
* "validate user authentication",
|
|
11
|
+
* embedSvc,
|
|
12
|
+
* { limit: 10 }
|
|
13
|
+
* );
|
|
14
|
+
*/
|
|
15
|
+
import type { EmbeddingService } from './embedding-service.js';
|
|
16
|
+
import type { SearchResult as CodeSearchResult } from './vector-index.js';
|
|
17
|
+
import type { SpecSearchResult } from './spec-vector-index.js';
|
|
18
|
+
export interface UnifiedSearchResult {
|
|
19
|
+
id: string;
|
|
20
|
+
type: 'code' | 'spec' | 'both';
|
|
21
|
+
score: number;
|
|
22
|
+
baseScore: number;
|
|
23
|
+
mappingBoost: number;
|
|
24
|
+
source: {
|
|
25
|
+
filePath?: string;
|
|
26
|
+
functionName?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
domain?: string;
|
|
29
|
+
section?: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
language?: string;
|
|
32
|
+
};
|
|
33
|
+
linkedArtifacts: Array<{
|
|
34
|
+
type: 'code' | 'spec';
|
|
35
|
+
id: string;
|
|
36
|
+
score: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface CrossScoringConfig {
|
|
40
|
+
directMappingBoost: number;
|
|
41
|
+
reverseMappingBoost: number;
|
|
42
|
+
mutualMappingBoost: number;
|
|
43
|
+
additionalLinkBoost: number;
|
|
44
|
+
maxAdditionalBoost: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build a bidirectional mapping index from mapping.json
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildBidirectionalMapping(mappings: any[]): {
|
|
50
|
+
functionToRequirements: Map<string, Array<{
|
|
51
|
+
domain: string;
|
|
52
|
+
requirement: string;
|
|
53
|
+
}>>;
|
|
54
|
+
requirementToFunctions: Map<string, Array<{
|
|
55
|
+
file: string;
|
|
56
|
+
name: string;
|
|
57
|
+
}>>;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Calculate cross-scoring boost based on bidirectional mappings
|
|
61
|
+
*/
|
|
62
|
+
export declare function calculateCrossScore(result: CodeSearchResult | SpecSearchResult, mappingIndex: {
|
|
63
|
+
functionToRequirements: Map<string, Array<{
|
|
64
|
+
domain: string;
|
|
65
|
+
requirement: string;
|
|
66
|
+
}>>;
|
|
67
|
+
requirementToFunctions: Map<string, Array<{
|
|
68
|
+
file: string;
|
|
69
|
+
name: string;
|
|
70
|
+
}>>;
|
|
71
|
+
}, config: CrossScoringConfig): {
|
|
72
|
+
mappingBoost: number;
|
|
73
|
+
linkedArtifacts: Array<{
|
|
74
|
+
type: 'code' | 'spec';
|
|
75
|
+
id: string;
|
|
76
|
+
score: number;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Determine result type based on source and linked artifacts
|
|
81
|
+
*/
|
|
82
|
+
export declare function determineResultType(result: CodeSearchResult | SpecSearchResult, linkedArtifacts: Array<{
|
|
83
|
+
type: 'code' | 'spec';
|
|
84
|
+
id: string;
|
|
85
|
+
score: number;
|
|
86
|
+
}>): 'code' | 'spec' | 'both';
|
|
87
|
+
/**
|
|
88
|
+
* Extract source metadata from result
|
|
89
|
+
*/
|
|
90
|
+
export declare function extractSourceMetadata(result: CodeSearchResult | SpecSearchResult): UnifiedSearchResult['source'];
|
|
91
|
+
export declare class UnifiedSearch {
|
|
92
|
+
/**
|
|
93
|
+
* Unified search that combines code and spec indexes with cross-scoring
|
|
94
|
+
*/
|
|
95
|
+
static unifiedSearch(outputDir: string, query: string, embedSvc: EmbeddingService | null | undefined, opts?: {
|
|
96
|
+
limit?: number;
|
|
97
|
+
language?: string;
|
|
98
|
+
domain?: string;
|
|
99
|
+
section?: string;
|
|
100
|
+
config?: Partial<CrossScoringConfig>;
|
|
101
|
+
}): Promise<UnifiedSearchResult[]>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if unified search is available
|
|
105
|
+
*/
|
|
106
|
+
export declare function unifiedSearchAvailable(outputDir: string): Promise<boolean>;
|
|
107
|
+
//# sourceMappingURL=unified-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-search.d.ts","sourceRoot":"","sources":["../../../src/core/analyzer/unified-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Search
|
|
3
|
+
*
|
|
4
|
+
* Combines code and spec indexes with cross-scoring to boost results
|
|
5
|
+
* that are linked through bidirectional mappings.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const results = await UnifiedSearch.unifiedSearch(
|
|
9
|
+
* outputDir,
|
|
10
|
+
* "validate user authentication",
|
|
11
|
+
* embedSvc,
|
|
12
|
+
* { limit: 10 }
|
|
13
|
+
* );
|
|
14
|
+
*/
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// CONSTANTS
|
|
18
|
+
// ============================================================================
|
|
19
|
+
const DEFAULT_CONFIG = {
|
|
20
|
+
directMappingBoost: 0.3,
|
|
21
|
+
reverseMappingBoost: 0.3,
|
|
22
|
+
mutualMappingBoost: 0.5,
|
|
23
|
+
additionalLinkBoost: 0.1,
|
|
24
|
+
maxAdditionalBoost: 0.3,
|
|
25
|
+
};
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// MAPPING INDEX
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Build a bidirectional mapping index from mapping.json
|
|
31
|
+
*/
|
|
32
|
+
export function buildBidirectionalMapping(mappings) {
|
|
33
|
+
const functionToRequirements = new Map();
|
|
34
|
+
const requirementToFunctions = new Map();
|
|
35
|
+
for (const m of mappings) {
|
|
36
|
+
if (!m.requirement || !m.domain || !m.functions)
|
|
37
|
+
continue;
|
|
38
|
+
// Use dot+camelCase format to match spec vector index IDs (e.g. 'auth.validateToken')
|
|
39
|
+
const reqCamel = m.requirement.charAt(0).toLowerCase() + m.requirement.slice(1);
|
|
40
|
+
const requirementKey = `${m.domain}.${reqCamel}`;
|
|
41
|
+
// Build function → requirements mapping
|
|
42
|
+
for (const fn of m.functions) {
|
|
43
|
+
if (!fn.file || !fn.name)
|
|
44
|
+
continue;
|
|
45
|
+
const functionKey = `${fn.file}::${fn.name}`;
|
|
46
|
+
const existing = functionToRequirements.get(functionKey) ?? [];
|
|
47
|
+
existing.push({ domain: m.domain, requirement: m.requirement });
|
|
48
|
+
functionToRequirements.set(functionKey, existing);
|
|
49
|
+
}
|
|
50
|
+
// Build requirement → functions mapping
|
|
51
|
+
const functions = m.functions
|
|
52
|
+
.filter((f) => f.file && f.name)
|
|
53
|
+
.map((f) => ({ file: f.file, name: f.name }));
|
|
54
|
+
if (functions.length > 0) {
|
|
55
|
+
requirementToFunctions.set(requirementKey, functions);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { functionToRequirements, requirementToFunctions };
|
|
59
|
+
}
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// CROSS-SCORING ALGORITHM
|
|
62
|
+
// ============================================================================
|
|
63
|
+
/**
|
|
64
|
+
* Calculate cross-scoring boost based on bidirectional mappings
|
|
65
|
+
*/
|
|
66
|
+
export function calculateCrossScore(result, mappingIndex, config) {
|
|
67
|
+
const linkedArtifacts = [];
|
|
68
|
+
let mappingBoost = 0;
|
|
69
|
+
if ('filePath' in result.record) {
|
|
70
|
+
// This is a code result - look for linked requirements
|
|
71
|
+
const rec = result.record;
|
|
72
|
+
const functionKey = `${rec.filePath}::${rec.name}`;
|
|
73
|
+
const requirements = mappingIndex.functionToRequirements.get(functionKey);
|
|
74
|
+
if (requirements && requirements.length > 0) {
|
|
75
|
+
mappingBoost += config.directMappingBoost;
|
|
76
|
+
// Add linked requirements as artifacts
|
|
77
|
+
for (const req of requirements) {
|
|
78
|
+
const reqCamel = req.requirement.charAt(0).toLowerCase() + req.requirement.slice(1);
|
|
79
|
+
const artifactId = `${req.domain}.${reqCamel}`;
|
|
80
|
+
linkedArtifacts.push({
|
|
81
|
+
type: 'spec',
|
|
82
|
+
id: artifactId,
|
|
83
|
+
score: result.score,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Additional boost for multiple links (capped)
|
|
87
|
+
const additionalBoost = Math.min((requirements.length - 1) * config.additionalLinkBoost, config.maxAdditionalBoost);
|
|
88
|
+
mappingBoost += additionalBoost;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// This is a spec result - look for linked functions
|
|
93
|
+
const specRec = result.record;
|
|
94
|
+
const requirementKey = specRec.id;
|
|
95
|
+
const functions = mappingIndex.requirementToFunctions.get(requirementKey);
|
|
96
|
+
if (functions && functions.length > 0) {
|
|
97
|
+
mappingBoost += config.reverseMappingBoost;
|
|
98
|
+
// Add linked functions as artifacts
|
|
99
|
+
for (const fn of functions) {
|
|
100
|
+
const artifactId = `${fn.file}::${fn.name}`;
|
|
101
|
+
linkedArtifacts.push({
|
|
102
|
+
type: 'code',
|
|
103
|
+
id: artifactId,
|
|
104
|
+
score: result.score,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Additional boost for multiple links (capped)
|
|
108
|
+
const additionalBoost = Math.min((functions.length - 1) * config.additionalLinkBoost, config.maxAdditionalBoost);
|
|
109
|
+
mappingBoost += additionalBoost;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { mappingBoost, linkedArtifacts };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Determine result type based on source and linked artifacts
|
|
116
|
+
*/
|
|
117
|
+
export function determineResultType(result, linkedArtifacts) {
|
|
118
|
+
const isCode = 'filePath' in result.record;
|
|
119
|
+
if (isCode && linkedArtifacts.some(art => art.type === 'spec'))
|
|
120
|
+
return 'both';
|
|
121
|
+
if (!isCode && linkedArtifacts.some(art => art.type === 'code'))
|
|
122
|
+
return 'both';
|
|
123
|
+
return isCode ? 'code' : 'spec';
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Extract source metadata from result
|
|
127
|
+
*/
|
|
128
|
+
export function extractSourceMetadata(result) {
|
|
129
|
+
if ('filePath' in result.record) {
|
|
130
|
+
const rec = result.record;
|
|
131
|
+
return {
|
|
132
|
+
filePath: rec.filePath,
|
|
133
|
+
functionName: rec.name,
|
|
134
|
+
className: rec.className || undefined,
|
|
135
|
+
language: rec.language,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const rec = result.record;
|
|
140
|
+
return {
|
|
141
|
+
domain: rec.domain,
|
|
142
|
+
section: rec.section,
|
|
143
|
+
title: rec.title,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// UNIFIED SEARCH
|
|
149
|
+
// ============================================================================
|
|
150
|
+
export class UnifiedSearch {
|
|
151
|
+
/**
|
|
152
|
+
* Unified search that combines code and spec indexes with cross-scoring
|
|
153
|
+
*/
|
|
154
|
+
static async unifiedSearch(outputDir, query, embedSvc, opts = {}) {
|
|
155
|
+
const { limit = 10, language, domain, section, config = {} } = opts;
|
|
156
|
+
const scoringConfig = { ...DEFAULT_CONFIG, ...config };
|
|
157
|
+
// Import index classes dynamically
|
|
158
|
+
const { VectorIndex } = await import('./vector-index.js');
|
|
159
|
+
const { SpecVectorIndex } = await import('./spec-vector-index.js');
|
|
160
|
+
// Load mapping index
|
|
161
|
+
const mappingJsonPath = join(outputDir, 'mapping.json');
|
|
162
|
+
let mappingIndex = {
|
|
163
|
+
functionToRequirements: new Map(),
|
|
164
|
+
requirementToFunctions: new Map(),
|
|
165
|
+
};
|
|
166
|
+
try {
|
|
167
|
+
const { readFile } = await import('node:fs/promises');
|
|
168
|
+
const raw = JSON.parse(await readFile(mappingJsonPath, 'utf-8'));
|
|
169
|
+
mappingIndex = buildBidirectionalMapping(raw.mappings ?? []);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Non-fatal - proceed without cross-scoring
|
|
173
|
+
}
|
|
174
|
+
// Execute parallel searches
|
|
175
|
+
const svc = embedSvc ?? null;
|
|
176
|
+
const [codeResults, specResults] = await Promise.all([
|
|
177
|
+
VectorIndex.search(outputDir, query, svc, { limit: limit * 3, language }).catch(() => []),
|
|
178
|
+
svc
|
|
179
|
+
? SpecVectorIndex.search(outputDir, query, svc, { limit: limit * 3, domain, section }).catch(() => [])
|
|
180
|
+
: Promise.resolve([]),
|
|
181
|
+
]);
|
|
182
|
+
// Combine and score results
|
|
183
|
+
const allResults = [];
|
|
184
|
+
// Process code results
|
|
185
|
+
for (const result of codeResults) {
|
|
186
|
+
const { mappingBoost, linkedArtifacts } = calculateCrossScore(result, mappingIndex, scoringConfig);
|
|
187
|
+
const finalScore = result.score + mappingBoost;
|
|
188
|
+
const resultType = determineResultType(result, linkedArtifacts);
|
|
189
|
+
allResults.push({
|
|
190
|
+
result,
|
|
191
|
+
unified: {
|
|
192
|
+
id: result.record.id,
|
|
193
|
+
type: resultType,
|
|
194
|
+
score: finalScore,
|
|
195
|
+
baseScore: result.score,
|
|
196
|
+
mappingBoost,
|
|
197
|
+
source: extractSourceMetadata(result),
|
|
198
|
+
linkedArtifacts,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Process spec results
|
|
203
|
+
for (const result of specResults) {
|
|
204
|
+
const { mappingBoost, linkedArtifacts } = calculateCrossScore(result, mappingIndex, scoringConfig);
|
|
205
|
+
const finalScore = result.score + mappingBoost;
|
|
206
|
+
const resultType = determineResultType(result, linkedArtifacts);
|
|
207
|
+
allResults.push({
|
|
208
|
+
result,
|
|
209
|
+
unified: {
|
|
210
|
+
id: result.record.id,
|
|
211
|
+
type: resultType,
|
|
212
|
+
score: finalScore,
|
|
213
|
+
baseScore: result.score,
|
|
214
|
+
mappingBoost,
|
|
215
|
+
source: extractSourceMetadata(result),
|
|
216
|
+
linkedArtifacts,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// Sort by final score
|
|
221
|
+
allResults.sort((a, b) => b.unified.score - a.unified.score);
|
|
222
|
+
// Return top results
|
|
223
|
+
return allResults.slice(0, limit).map(r => r.unified);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// UTILITIES
|
|
228
|
+
// ============================================================================
|
|
229
|
+
/**
|
|
230
|
+
* Check if unified search is available
|
|
231
|
+
*/
|
|
232
|
+
export async function unifiedSearchAvailable(outputDir) {
|
|
233
|
+
const { VectorIndex } = await import('./vector-index.js');
|
|
234
|
+
const { SpecVectorIndex } = await import('./spec-vector-index.js');
|
|
235
|
+
return VectorIndex.exists(outputDir) && SpecVectorIndex.exists(outputDir);
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=unified-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-search.js","sourceRoot":"","sources":["../../../src/core/analyzer/unified-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -671,7 +671,7 @@ export class OpenSpecFormatGenerator {
|
|
|
671
671
|
const order = { llm: 0, semantic: 1, heuristic: 2 };
|
|
672
672
|
return (order[a.confidence] ?? 3) - (order[b.confidence] ?? 3);
|
|
673
673
|
})[0];
|
|
674
|
-
lines.push(`> Implementation: \`${best.
|
|
674
|
+
lines.push(`> Implementation: \`${best.name}\` in \`${best.file}\` · confidence: ${best.confidence}`);
|
|
675
675
|
lines.push('');
|
|
676
676
|
}
|
|
677
677
|
/**
|