ai-first-cli 1.1.0 → 1.1.2
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/CHANGELOG.md +78 -0
- package/README.es.md +137 -1
- package/README.md +136 -4
- package/ai/ai_context.md +2 -2
- package/ai/architecture.md +3 -3
- package/ai/cache.json +85 -57
- package/ai/ccp/jira-123/context.json +7 -0
- package/ai/context/repo.json +56 -0
- package/ai/context/utils.json +7 -0
- package/ai/dependencies.json +51 -1026
- package/ai/files.json +195 -3
- package/ai/git/commit-activity.json +8646 -0
- package/ai/git/recent-features.json +1 -0
- package/ai/git/recent-files.json +52 -0
- package/ai/git/recent-flows.json +1 -0
- package/ai/graph/knowledge-graph.json +43643 -0
- package/ai/graph/module-graph.json +4 -0
- package/ai/graph/symbol-graph.json +3307 -879
- package/ai/graph/symbol-references.json +119 -32
- package/ai/index-state.json +843 -188
- package/ai/index.db +0 -0
- package/ai/modules.json +4 -0
- package/ai/repo-map.json +81 -17
- package/ai/repo_map.json +81 -17
- package/ai/repo_map.md +21 -7
- package/ai/summary.md +5 -5
- package/ai/symbols.json +1 -20287
- package/dist/analyzers/androidResources.d.ts +23 -0
- package/dist/analyzers/androidResources.d.ts.map +1 -0
- package/dist/analyzers/androidResources.js +93 -0
- package/dist/analyzers/androidResources.js.map +1 -0
- package/dist/analyzers/dependencies.d.ts.map +1 -1
- package/dist/analyzers/dependencies.js +37 -0
- package/dist/analyzers/dependencies.js.map +1 -1
- package/dist/analyzers/entrypoints.d.ts.map +1 -1
- package/dist/analyzers/entrypoints.js +71 -1
- package/dist/analyzers/entrypoints.js.map +1 -1
- package/dist/analyzers/gradleModules.d.ts +22 -0
- package/dist/analyzers/gradleModules.d.ts.map +1 -0
- package/dist/analyzers/gradleModules.js +75 -0
- package/dist/analyzers/gradleModules.js.map +1 -0
- package/dist/analyzers/techStack.d.ts +7 -0
- package/dist/analyzers/techStack.d.ts.map +1 -1
- package/dist/analyzers/techStack.js +44 -1
- package/dist/analyzers/techStack.js.map +1 -1
- package/dist/commands/ai-first.d.ts.map +1 -1
- package/dist/commands/ai-first.js +311 -1
- package/dist/commands/ai-first.js.map +1 -1
- package/dist/core/adapters/adapterRegistry.d.ts +39 -0
- package/dist/core/adapters/adapterRegistry.d.ts.map +1 -0
- package/dist/core/adapters/adapterRegistry.js +155 -0
- package/dist/core/adapters/adapterRegistry.js.map +1 -0
- package/dist/core/adapters/baseAdapter.d.ts +49 -0
- package/dist/core/adapters/baseAdapter.d.ts.map +1 -0
- package/dist/core/adapters/baseAdapter.js +28 -0
- package/dist/core/adapters/baseAdapter.js.map +1 -0
- package/dist/core/adapters/community/fastapiAdapter.d.ts +7 -0
- package/dist/core/adapters/community/fastapiAdapter.d.ts.map +1 -0
- package/dist/core/adapters/community/fastapiAdapter.js +40 -0
- package/dist/core/adapters/community/fastapiAdapter.js.map +1 -0
- package/dist/core/adapters/community/index.d.ts +11 -0
- package/dist/core/adapters/community/index.d.ts.map +1 -0
- package/dist/core/adapters/community/index.js +11 -0
- package/dist/core/adapters/community/index.js.map +1 -0
- package/dist/core/adapters/community/laravelAdapter.d.ts +7 -0
- package/dist/core/adapters/community/laravelAdapter.d.ts.map +1 -0
- package/dist/core/adapters/community/laravelAdapter.js +47 -0
- package/dist/core/adapters/community/laravelAdapter.js.map +1 -0
- package/dist/core/adapters/community/nestjsAdapter.d.ts +7 -0
- package/dist/core/adapters/community/nestjsAdapter.d.ts.map +1 -0
- package/dist/core/adapters/community/nestjsAdapter.js +48 -0
- package/dist/core/adapters/community/nestjsAdapter.js.map +1 -0
- package/dist/core/adapters/community/phoenixAdapter.d.ts +7 -0
- package/dist/core/adapters/community/phoenixAdapter.d.ts.map +1 -0
- package/dist/core/adapters/community/phoenixAdapter.js +45 -0
- package/dist/core/adapters/community/phoenixAdapter.js.map +1 -0
- package/dist/core/adapters/community/springBootAdapter.d.ts +7 -0
- package/dist/core/adapters/community/springBootAdapter.d.ts.map +1 -0
- package/dist/core/adapters/community/springBootAdapter.js +44 -0
- package/dist/core/adapters/community/springBootAdapter.js.map +1 -0
- package/dist/core/adapters/dotnetAdapter.d.ts +20 -0
- package/dist/core/adapters/dotnetAdapter.d.ts.map +1 -0
- package/dist/core/adapters/dotnetAdapter.js +86 -0
- package/dist/core/adapters/dotnetAdapter.js.map +1 -0
- package/dist/core/adapters/index.d.ts +18 -0
- package/dist/core/adapters/index.d.ts.map +1 -0
- package/dist/core/adapters/index.js +19 -0
- package/dist/core/adapters/index.js.map +1 -0
- package/dist/core/adapters/javascriptAdapter.d.ts +11 -0
- package/dist/core/adapters/javascriptAdapter.d.ts.map +1 -0
- package/dist/core/adapters/javascriptAdapter.js +47 -0
- package/dist/core/adapters/javascriptAdapter.js.map +1 -0
- package/dist/core/adapters/pythonAdapter.d.ts +20 -0
- package/dist/core/adapters/pythonAdapter.d.ts.map +1 -0
- package/dist/core/adapters/pythonAdapter.js +99 -0
- package/dist/core/adapters/pythonAdapter.js.map +1 -0
- package/dist/core/adapters/railsAdapter.d.ts +10 -0
- package/dist/core/adapters/railsAdapter.d.ts.map +1 -0
- package/dist/core/adapters/railsAdapter.js +52 -0
- package/dist/core/adapters/railsAdapter.js.map +1 -0
- package/dist/core/adapters/salesforceAdapter.d.ts +16 -0
- package/dist/core/adapters/salesforceAdapter.d.ts.map +1 -0
- package/dist/core/adapters/salesforceAdapter.js +64 -0
- package/dist/core/adapters/salesforceAdapter.js.map +1 -0
- package/dist/core/adapters/sdk.d.ts +83 -0
- package/dist/core/adapters/sdk.d.ts.map +1 -0
- package/dist/core/adapters/sdk.js +114 -0
- package/dist/core/adapters/sdk.js.map +1 -0
- package/dist/core/ccp.d.ts +37 -0
- package/dist/core/ccp.d.ts.map +1 -0
- package/dist/core/ccp.js +184 -0
- package/dist/core/ccp.js.map +1 -0
- package/dist/core/gitAnalyzer.d.ts +74 -0
- package/dist/core/gitAnalyzer.d.ts.map +1 -0
- package/dist/core/gitAnalyzer.js +298 -0
- package/dist/core/gitAnalyzer.js.map +1 -0
- package/dist/core/incrementalAnalyzer.d.ts +28 -0
- package/dist/core/incrementalAnalyzer.d.ts.map +1 -0
- package/dist/core/incrementalAnalyzer.js +343 -0
- package/dist/core/incrementalAnalyzer.js.map +1 -0
- package/dist/core/knowledgeGraphBuilder.d.ts +31 -0
- package/dist/core/knowledgeGraphBuilder.d.ts.map +1 -0
- package/dist/core/knowledgeGraphBuilder.js +197 -0
- package/dist/core/knowledgeGraphBuilder.js.map +1 -0
- package/dist/core/lazyAnalyzer.d.ts +57 -0
- package/dist/core/lazyAnalyzer.d.ts.map +1 -0
- package/dist/core/lazyAnalyzer.js +204 -0
- package/dist/core/lazyAnalyzer.js.map +1 -0
- package/dist/core/schema.d.ts +57 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +131 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/semanticContexts.d.ts +40 -0
- package/dist/core/semanticContexts.d.ts.map +1 -0
- package/dist/core/semanticContexts.js +454 -0
- package/dist/core/semanticContexts.js.map +1 -0
- package/docs/es/guide/adapters.md +143 -0
- package/docs/es/guide/ai-repository-schema.md +119 -0
- package/docs/es/guide/features.md +67 -0
- package/docs/es/guide/flows.md +134 -0
- package/docs/es/guide/git-intelligence.md +170 -0
- package/docs/es/guide/incremental-analysis.md +131 -0
- package/docs/es/guide/knowledge-graph.md +135 -0
- package/docs/es/guide/lazy-indexing.md +144 -0
- package/docs/es/guide/performance.md +125 -0
- package/docs/guide/adapters.md +225 -0
- package/docs/guide/ai-repository-schema.md +119 -0
- package/docs/guide/architecture.md +69 -1
- package/docs/guide/flows.md +134 -0
- package/docs/guide/git-intelligence.md +170 -0
- package/docs/guide/incremental-analysis.md +131 -0
- package/docs/guide/knowledge-graph.md +135 -0
- package/docs/guide/lazy-indexing.md +144 -0
- package/docs/guide/performance.md +125 -0
- package/package.json +6 -3
- package/src/analyzers/androidResources.ts +113 -0
- package/src/analyzers/dependencies.ts +41 -0
- package/src/analyzers/entrypoints.ts +80 -1
- package/src/analyzers/gradleModules.ts +100 -0
- package/src/analyzers/techStack.ts +56 -0
- package/src/commands/ai-first.ts +342 -1
- package/src/core/adapters/adapterRegistry.ts +187 -0
- package/src/core/adapters/baseAdapter.ts +82 -0
- package/src/core/adapters/community/fastapiAdapter.ts +50 -0
- package/src/core/adapters/community/index.ts +11 -0
- package/src/core/adapters/community/laravelAdapter.ts +56 -0
- package/src/core/adapters/community/nestjsAdapter.ts +57 -0
- package/src/core/adapters/community/phoenixAdapter.ts +54 -0
- package/src/core/adapters/community/springBootAdapter.ts +53 -0
- package/src/core/adapters/dotnetAdapter.ts +104 -0
- package/src/core/adapters/index.ts +24 -0
- package/src/core/adapters/javascriptAdapter.ts +56 -0
- package/src/core/adapters/pythonAdapter.ts +118 -0
- package/src/core/adapters/railsAdapter.ts +65 -0
- package/src/core/adapters/salesforceAdapter.ts +76 -0
- package/src/core/adapters/sdk.ts +172 -0
- package/src/core/ccp.ts +240 -0
- package/src/core/gitAnalyzer.ts +391 -0
- package/src/core/incrementalAnalyzer.ts +382 -0
- package/src/core/knowledgeGraphBuilder.ts +181 -0
- package/src/core/lazyAnalyzer.ts +261 -0
- package/src/core/schema.ts +157 -0
- package/src/core/semanticContexts.ts +575 -0
- package/tests/adapters.test.ts +159 -0
- package/tests/gitAnalyzer.test.ts +133 -0
- package/tests/incrementalAnalyzer.test.ts +83 -0
- package/tests/knowledgeGraph.test.ts +146 -0
- package/tests/lazyAnalyzer.test.ts +230 -0
- package/tests/schema.test.ts +203 -0
- package/tests/semanticContexts.test.ts +435 -0
- package/ai/context/analyzers.Symbol.json +0 -19
- package/ai/context/analyzers.extractSymbols.json +0 -19
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Intelligence Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes recent git activity to provide AI agents with context about
|
|
5
|
+
* recently changed files, features, and flows.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { ensureDir, writeFile, readJsonFile } from "../utils/fileUtils.js";
|
|
11
|
+
const DEFAULT_OPTIONS = {
|
|
12
|
+
commitLimit: 50,
|
|
13
|
+
maxAgeDays: 30,
|
|
14
|
+
maxFiles: 50
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Check if a directory is a git repository
|
|
18
|
+
*/
|
|
19
|
+
export function detectGitRepository(rootDir) {
|
|
20
|
+
const gitDir = path.join(rootDir, ".git");
|
|
21
|
+
return fs.existsSync(gitDir);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Execute git command and return output
|
|
25
|
+
*/
|
|
26
|
+
function gitExec(rootDir, command) {
|
|
27
|
+
try {
|
|
28
|
+
return execSync(command, {
|
|
29
|
+
cwd: rootDir,
|
|
30
|
+
encoding: "utf-8",
|
|
31
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
32
|
+
}).trim();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get recent commits from git repository
|
|
40
|
+
*/
|
|
41
|
+
export function getRecentCommits(rootDir, limit = 50) {
|
|
42
|
+
if (!detectGitRepository(rootDir)) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const commits = [];
|
|
46
|
+
// Get commit hashes
|
|
47
|
+
const logFormat = "%H|%ai|%s|%an";
|
|
48
|
+
const logOutput = gitExec(rootDir, `git log --format="${logFormat}" -n ${limit}`);
|
|
49
|
+
if (!logOutput) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const lines = logOutput.split("\n");
|
|
53
|
+
const maxAgeDate = new Date();
|
|
54
|
+
maxAgeDate.setDate(maxAgeDate.getDate() - DEFAULT_OPTIONS.maxAgeDays);
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
if (!line.trim())
|
|
57
|
+
continue;
|
|
58
|
+
const [hash, dateStr, message, author] = line.split("|");
|
|
59
|
+
const commitDate = new Date(dateStr);
|
|
60
|
+
// Skip commits older than maxAgeDays
|
|
61
|
+
if (commitDate < maxAgeDate) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
// Get files changed in this commit
|
|
65
|
+
const filesOutput = gitExec(rootDir, `git diff-tree --no-commit-id --name-only -r ${hash}`);
|
|
66
|
+
const files = filesOutput ? filesOutput.split("\n").filter(f => f.trim()) : [];
|
|
67
|
+
commits.push({
|
|
68
|
+
hash,
|
|
69
|
+
date: dateStr,
|
|
70
|
+
message,
|
|
71
|
+
author,
|
|
72
|
+
files
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return commits;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Extract changed files from commits
|
|
79
|
+
*/
|
|
80
|
+
export function extractChangedFiles(commits) {
|
|
81
|
+
const fileStats = new Map();
|
|
82
|
+
for (const commit of commits) {
|
|
83
|
+
for (const file of commit.files) {
|
|
84
|
+
const existing = fileStats.get(file);
|
|
85
|
+
if (existing) {
|
|
86
|
+
existing.count++;
|
|
87
|
+
if (commit.date > existing.lastChanged) {
|
|
88
|
+
existing.lastChanged = commit.date;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
fileStats.set(file, {
|
|
93
|
+
count: 1,
|
|
94
|
+
lastChanged: commit.date
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Convert to array and sort by commit count
|
|
100
|
+
const result = Array.from(fileStats.entries())
|
|
101
|
+
.map(([path, stats]) => ({
|
|
102
|
+
path,
|
|
103
|
+
commitCount: stats.count,
|
|
104
|
+
lastChanged: stats.lastChanged
|
|
105
|
+
}))
|
|
106
|
+
.sort((a, b) => b.commitCount - a.commitCount);
|
|
107
|
+
return result.slice(0, DEFAULT_OPTIONS.maxFiles);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get list of changed files
|
|
111
|
+
*/
|
|
112
|
+
export function getRecentFiles(rootDir) {
|
|
113
|
+
const commits = getRecentCommits(rootDir);
|
|
114
|
+
const recentFiles = extractChangedFiles(commits);
|
|
115
|
+
return recentFiles.map(f => f.path);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Load features from ai/context/features/
|
|
119
|
+
*/
|
|
120
|
+
function loadFeatures(aiDir) {
|
|
121
|
+
const featuresMap = new Map();
|
|
122
|
+
const featuresDir = path.join(aiDir, "context", "features");
|
|
123
|
+
if (!fs.existsSync(featuresDir)) {
|
|
124
|
+
return featuresMap;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const files = fs.readdirSync(featuresDir);
|
|
128
|
+
for (const file of files) {
|
|
129
|
+
if (!file.endsWith(".json"))
|
|
130
|
+
continue;
|
|
131
|
+
const featurePath = path.join(featuresDir, file);
|
|
132
|
+
const featureData = readJsonFile(featurePath);
|
|
133
|
+
if (featureData && featureData.name && featureData.files) {
|
|
134
|
+
// Map each file in the feature to the feature name
|
|
135
|
+
for (const filePath of featureData.files) {
|
|
136
|
+
const existing = featuresMap.get(filePath) || [];
|
|
137
|
+
existing.push(featureData.name);
|
|
138
|
+
featuresMap.set(filePath, existing);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Ignore errors reading features
|
|
145
|
+
}
|
|
146
|
+
return featuresMap;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Load flows from ai/context/flows/
|
|
150
|
+
*/
|
|
151
|
+
function loadFlows(aiDir) {
|
|
152
|
+
const flowsMap = new Map();
|
|
153
|
+
const flowsDir = path.join(aiDir, "context", "flows");
|
|
154
|
+
if (!fs.existsSync(flowsDir)) {
|
|
155
|
+
return flowsMap;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const files = fs.readdirSync(flowsDir);
|
|
159
|
+
for (const file of files) {
|
|
160
|
+
if (!file.endsWith(".json"))
|
|
161
|
+
continue;
|
|
162
|
+
const flowPath = path.join(flowsDir, file);
|
|
163
|
+
const flowData = readJsonFile(flowPath);
|
|
164
|
+
if (flowData && flowData.name && flowData.files) {
|
|
165
|
+
// Map each file in the flow to the flow name
|
|
166
|
+
for (const filePath of flowData.files) {
|
|
167
|
+
const existing = flowsMap.get(filePath) || [];
|
|
168
|
+
existing.push(flowData.name);
|
|
169
|
+
flowsMap.set(filePath, existing);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Ignore errors reading flows
|
|
176
|
+
}
|
|
177
|
+
return flowsMap;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Map changed files to features
|
|
181
|
+
*/
|
|
182
|
+
export function mapFilesToFeatures(rootDir, files) {
|
|
183
|
+
const aiDir = path.join(rootDir, "ai");
|
|
184
|
+
const featuresMap = loadFeatures(aiDir);
|
|
185
|
+
const featureSet = new Set();
|
|
186
|
+
for (const file of files) {
|
|
187
|
+
// Try exact match first
|
|
188
|
+
const directMatch = featuresMap.get(file);
|
|
189
|
+
if (directMatch) {
|
|
190
|
+
directMatch.forEach(f => featureSet.add(f));
|
|
191
|
+
}
|
|
192
|
+
// Try partial match (file is inside feature directory)
|
|
193
|
+
for (const [featureFile, featureNames] of featuresMap) {
|
|
194
|
+
if (file.startsWith(featureFile) || featureFile.startsWith(file)) {
|
|
195
|
+
featureNames.forEach(f => featureSet.add(f));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return Array.from(featureSet);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Map changed files to flows
|
|
203
|
+
*/
|
|
204
|
+
export function mapFilesToFlows(rootDir, files) {
|
|
205
|
+
const aiDir = path.join(rootDir, "ai");
|
|
206
|
+
const flowsMap = loadFlows(aiDir);
|
|
207
|
+
const flowSet = new Set();
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
// Try exact match first
|
|
210
|
+
const directMatch = flowsMap.get(file);
|
|
211
|
+
if (directMatch) {
|
|
212
|
+
directMatch.forEach(f => flowSet.add(f));
|
|
213
|
+
}
|
|
214
|
+
// Try partial match (file is inside flow)
|
|
215
|
+
for (const [flowFile, flowNames] of flowsMap) {
|
|
216
|
+
if (file.startsWith(flowFile) || flowFile.startsWith(file)) {
|
|
217
|
+
flowNames.forEach(f => flowSet.add(f));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return Array.from(flowSet);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Analyze git activity
|
|
225
|
+
*/
|
|
226
|
+
export function analyzeGitActivity(rootDir, options = {}) {
|
|
227
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
228
|
+
if (!detectGitRepository(rootDir)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const commits = getRecentCommits(rootDir, opts.commitLimit);
|
|
232
|
+
if (commits.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const recentFiles = extractChangedFiles(commits);
|
|
236
|
+
const recentFilePaths = recentFiles.map(f => f.path);
|
|
237
|
+
const features = mapFilesToFeatures(rootDir, recentFilePaths);
|
|
238
|
+
const flows = mapFilesToFlows(rootDir, recentFilePaths);
|
|
239
|
+
// Calculate feature/flow commit counts
|
|
240
|
+
const featureCounts = {};
|
|
241
|
+
const flowCounts = {};
|
|
242
|
+
const fileCounts = {};
|
|
243
|
+
for (const commit of commits) {
|
|
244
|
+
const commitFeatures = mapFilesToFeatures(rootDir, commit.files);
|
|
245
|
+
for (const feature of commitFeatures) {
|
|
246
|
+
featureCounts[feature] = (featureCounts[feature] || 0) + 1;
|
|
247
|
+
}
|
|
248
|
+
const commitFlows = mapFilesToFlows(rootDir, commit.files);
|
|
249
|
+
for (const flow of commitFlows) {
|
|
250
|
+
flowCounts[flow] = (flowCounts[flow] || 0) + 1;
|
|
251
|
+
}
|
|
252
|
+
for (const file of commit.files) {
|
|
253
|
+
fileCounts[file] = (fileCounts[file] || 0) + 1;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
totalCommits: commits.length,
|
|
258
|
+
dateRange: {
|
|
259
|
+
start: commits[commits.length - 1]?.date || "",
|
|
260
|
+
end: commits[0]?.date || ""
|
|
261
|
+
},
|
|
262
|
+
files: fileCounts,
|
|
263
|
+
features: featureCounts,
|
|
264
|
+
flows: flowCounts
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Generate git context files
|
|
269
|
+
*/
|
|
270
|
+
export function generateGitContext(rootDir, aiDir) {
|
|
271
|
+
const targetAiDir = aiDir || path.join(rootDir, "ai");
|
|
272
|
+
const gitDir = path.join(targetAiDir, "git");
|
|
273
|
+
ensureDir(gitDir);
|
|
274
|
+
const commits = getRecentCommits(rootDir);
|
|
275
|
+
const recentFiles = extractChangedFiles(commits);
|
|
276
|
+
const recentFilePaths = recentFiles.map(f => f.path);
|
|
277
|
+
const recentFeatures = mapFilesToFeatures(rootDir, recentFilePaths);
|
|
278
|
+
const recentFlows = mapFilesToFlows(rootDir, recentFilePaths);
|
|
279
|
+
const activity = analyzeGitActivity(rootDir);
|
|
280
|
+
// Write output files
|
|
281
|
+
const recentFilesJson = JSON.stringify(recentFilePaths, null, 2);
|
|
282
|
+
writeFile(path.join(gitDir, "recent-files.json"), recentFilesJson);
|
|
283
|
+
const recentFeaturesJson = JSON.stringify(recentFeatures, null, 2);
|
|
284
|
+
writeFile(path.join(gitDir, "recent-features.json"), recentFeaturesJson);
|
|
285
|
+
const recentFlowsJson = JSON.stringify(recentFlows, null, 2);
|
|
286
|
+
writeFile(path.join(gitDir, "recent-flows.json"), recentFlowsJson);
|
|
287
|
+
if (activity) {
|
|
288
|
+
const activityJson = JSON.stringify(activity, null, 2);
|
|
289
|
+
writeFile(path.join(gitDir, "commit-activity.json"), activityJson);
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
recentFiles: recentFilePaths,
|
|
293
|
+
recentFeatures,
|
|
294
|
+
recentFlows,
|
|
295
|
+
activity
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=gitAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitAnalyzer.js","sourceRoot":"","sources":["../../src/core/gitAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Performs incremental updates to repository intelligence when files change,
|
|
5
|
+
* without requiring a full re-analysis.
|
|
6
|
+
*/
|
|
7
|
+
export interface ChangedFile {
|
|
8
|
+
path: string;
|
|
9
|
+
status: "added" | "modified" | "deleted";
|
|
10
|
+
hash?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface IncrementalUpdateResult {
|
|
13
|
+
changedFiles: ChangedFile[];
|
|
14
|
+
updatedSymbols: number;
|
|
15
|
+
updatedDependencies: number;
|
|
16
|
+
updatedFeatures: string[];
|
|
17
|
+
updatedFlows: string[];
|
|
18
|
+
graphUpdated: boolean;
|
|
19
|
+
errors: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function detectChangedFiles(rootDir: string, useGit?: boolean): ChangedFile[];
|
|
22
|
+
export declare function updateSymbols(rootDir: string, changedFiles: ChangedFile[], aiDir: string): number;
|
|
23
|
+
export declare function updateDependencies(rootDir: string, changedFiles: ChangedFile[], aiDir: string): number;
|
|
24
|
+
export declare function updateFeatures(rootDir: string, changedFiles: ChangedFile[], aiDir: string): string[];
|
|
25
|
+
export declare function updateFlows(rootDir: string, changedFiles: ChangedFile[], aiDir: string): string[];
|
|
26
|
+
export declare function updateKnowledgeGraph(rootDir: string, aiDir: string): boolean;
|
|
27
|
+
export declare function runIncrementalUpdate(rootDir: string, aiDir?: string): IncrementalUpdateResult;
|
|
28
|
+
//# sourceMappingURL=incrementalAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incrementalAnalyzer.d.ts","sourceRoot":"","sources":["../../src/core/incrementalAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,GAAG,WAAW,EAAE,CAKzF;AAyED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAmCjG;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA2BtG;AAMD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmCpG;AAMD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmCjG;AAMD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAO5E;AAMD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,uBAAuB,CA8D7F"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Performs incremental updates to repository intelligence when files change,
|
|
5
|
+
* without requiring a full re-analysis.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { writeFile, readJsonFile } from "../utils/fileUtils.js";
|
|
11
|
+
import { computeFileHash, loadIndexState, saveIndexState } from "./indexState.js";
|
|
12
|
+
import { extractSymbols } from "../analyzers/symbols.js";
|
|
13
|
+
import { buildKnowledgeGraph } from "./knowledgeGraphBuilder.js";
|
|
14
|
+
// ============================================================
|
|
15
|
+
// File Change Detection
|
|
16
|
+
// ============================================================
|
|
17
|
+
export function detectChangedFiles(rootDir, useGit = true) {
|
|
18
|
+
if (useGit && isGitRepository(rootDir)) {
|
|
19
|
+
return detectChangesWithGit(rootDir);
|
|
20
|
+
}
|
|
21
|
+
return detectChangesWithTimestamps(rootDir);
|
|
22
|
+
}
|
|
23
|
+
function isGitRepository(rootDir) {
|
|
24
|
+
return fs.existsSync(path.join(rootDir, ".git"));
|
|
25
|
+
}
|
|
26
|
+
function detectChangesWithGit(rootDir) {
|
|
27
|
+
const changes = [];
|
|
28
|
+
try {
|
|
29
|
+
const output = execSync("git diff --name-status HEAD", {
|
|
30
|
+
cwd: rootDir,
|
|
31
|
+
encoding: "utf-8"
|
|
32
|
+
});
|
|
33
|
+
for (const line of output.split("\n")) {
|
|
34
|
+
if (!line.trim())
|
|
35
|
+
continue;
|
|
36
|
+
const [status, filePath] = line.split("\t").map(s => s.trim());
|
|
37
|
+
if (!filePath)
|
|
38
|
+
continue;
|
|
39
|
+
let changeStatus = "modified";
|
|
40
|
+
if (status.startsWith("A") || status === "??")
|
|
41
|
+
changeStatus = "added";
|
|
42
|
+
else if (status.startsWith("D"))
|
|
43
|
+
changeStatus = "deleted";
|
|
44
|
+
changes.push({ path: filePath, status: changeStatus });
|
|
45
|
+
}
|
|
46
|
+
const stagedOutput = execSync("git diff --name-status --cached", {
|
|
47
|
+
cwd: rootDir,
|
|
48
|
+
encoding: "utf-8"
|
|
49
|
+
});
|
|
50
|
+
for (const line of stagedOutput.split("\n")) {
|
|
51
|
+
if (!line.trim())
|
|
52
|
+
continue;
|
|
53
|
+
const [status, filePath] = line.split("\t").map(s => s.trim());
|
|
54
|
+
if (!filePath || changes.find(c => c.path === filePath))
|
|
55
|
+
continue;
|
|
56
|
+
changes.push({ path: filePath, status: "modified" });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return detectChangesWithTimestamps(rootDir);
|
|
61
|
+
}
|
|
62
|
+
return changes;
|
|
63
|
+
}
|
|
64
|
+
function detectChangesWithTimestamps(rootDir) {
|
|
65
|
+
const changes = [];
|
|
66
|
+
const aiDir = path.join(rootDir, "ai");
|
|
67
|
+
const state = loadIndexState(aiDir);
|
|
68
|
+
if (!state)
|
|
69
|
+
return [];
|
|
70
|
+
for (const [filePath, fileState] of Object.entries(state.files)) {
|
|
71
|
+
const fullPath = path.join(rootDir, filePath);
|
|
72
|
+
if (!fs.existsSync(fullPath)) {
|
|
73
|
+
changes.push({ path: filePath, status: "deleted" });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const currentHash = computeFileHash(fullPath);
|
|
77
|
+
if (currentHash && currentHash.hash !== fileState.hash) {
|
|
78
|
+
changes.push({ path: filePath, status: "modified", hash: currentHash.hash });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return changes;
|
|
82
|
+
}
|
|
83
|
+
// ============================================================
|
|
84
|
+
// Symbol Update
|
|
85
|
+
// ============================================================
|
|
86
|
+
export function updateSymbols(rootDir, changedFiles, aiDir) {
|
|
87
|
+
const symbolsPath = path.join(aiDir, "symbols.json");
|
|
88
|
+
let existingSymbols = [];
|
|
89
|
+
if (fs.existsSync(symbolsPath)) {
|
|
90
|
+
try {
|
|
91
|
+
const raw = readJsonFile(symbolsPath);
|
|
92
|
+
if (Array.isArray(raw))
|
|
93
|
+
existingSymbols = raw;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
existingSymbols = [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const changedPaths = new Set(changedFiles.map(f => f.path));
|
|
100
|
+
existingSymbols = existingSymbols.filter(s => !changedPaths.has(s.file));
|
|
101
|
+
for (const changed of changedFiles) {
|
|
102
|
+
if (changed.status === "deleted")
|
|
103
|
+
continue;
|
|
104
|
+
const fullPath = path.join(rootDir, changed.path);
|
|
105
|
+
if (!fs.existsSync(fullPath))
|
|
106
|
+
continue;
|
|
107
|
+
try {
|
|
108
|
+
const fileInfo = {
|
|
109
|
+
path: changed.path,
|
|
110
|
+
relativePath: changed.path,
|
|
111
|
+
extension: path.extname(changed.path),
|
|
112
|
+
name: path.basename(changed.path, path.extname(changed.path))
|
|
113
|
+
};
|
|
114
|
+
const symbols = extractSymbols([fileInfo]);
|
|
115
|
+
for (const symbol of symbols.symbols || []) {
|
|
116
|
+
existingSymbols.push({ name: symbol.name, file: changed.path, type: symbol.type });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch { /* skip */ }
|
|
120
|
+
}
|
|
121
|
+
writeFile(symbolsPath, JSON.stringify(existingSymbols, null, 2));
|
|
122
|
+
return existingSymbols.length;
|
|
123
|
+
}
|
|
124
|
+
// ============================================================
|
|
125
|
+
// Dependency Update
|
|
126
|
+
// ============================================================
|
|
127
|
+
export function updateDependencies(rootDir, changedFiles, aiDir) {
|
|
128
|
+
const depsPath = path.join(aiDir, "dependencies.json");
|
|
129
|
+
let existingDeps = {};
|
|
130
|
+
if (fs.existsSync(depsPath)) {
|
|
131
|
+
try {
|
|
132
|
+
existingDeps = readJsonFile(depsPath);
|
|
133
|
+
}
|
|
134
|
+
catch { /* ignore */ }
|
|
135
|
+
}
|
|
136
|
+
const packageFiles = changedFiles.filter(f => f.path.endsWith("package.json") || f.path.endsWith("requirements.txt"));
|
|
137
|
+
for (const pkgFile of packageFiles) {
|
|
138
|
+
const fullPath = path.join(rootDir, pkgFile.path);
|
|
139
|
+
if (!fs.existsSync(fullPath))
|
|
140
|
+
continue;
|
|
141
|
+
try {
|
|
142
|
+
if (pkgFile.path.endsWith("package.json")) {
|
|
143
|
+
const pkg = JSON.parse(fs.readFileSync(fullPath, "utf-8"));
|
|
144
|
+
existingDeps.dependencies = pkg.dependencies || {};
|
|
145
|
+
existingDeps.devDependencies = pkg.devDependencies || {};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch { /* skip */ }
|
|
149
|
+
}
|
|
150
|
+
writeFile(depsPath, JSON.stringify(existingDeps, null, 2));
|
|
151
|
+
return Object.keys(existingDeps.dependencies || {}).length;
|
|
152
|
+
}
|
|
153
|
+
// ============================================================
|
|
154
|
+
// Feature Update
|
|
155
|
+
// ============================================================
|
|
156
|
+
export function updateFeatures(rootDir, changedFiles, aiDir) {
|
|
157
|
+
const changedPaths = new Set(changedFiles.map(f => f.path));
|
|
158
|
+
const featuresDir = path.join(aiDir, "context", "features");
|
|
159
|
+
const updatedFeatures = [];
|
|
160
|
+
if (!fs.existsSync(featuresDir))
|
|
161
|
+
return updatedFeatures;
|
|
162
|
+
try {
|
|
163
|
+
for (const featureFile of fs.readdirSync(featuresDir)) {
|
|
164
|
+
if (!featureFile.endsWith(".json"))
|
|
165
|
+
continue;
|
|
166
|
+
const featurePath = path.join(featuresDir, featureFile);
|
|
167
|
+
const featureData = readJsonFile(featurePath);
|
|
168
|
+
if (!featureData?.files)
|
|
169
|
+
continue;
|
|
170
|
+
const featureFileSet = new Set(featureData.files);
|
|
171
|
+
const affected = [...changedPaths].some(f => featureFileSet.has(f));
|
|
172
|
+
if (affected || changedFiles.some(c => c.status === "deleted")) {
|
|
173
|
+
const featureFilesList = featureData.files.filter(f => !changedFiles.some(c => c.path === f && c.status === "deleted"));
|
|
174
|
+
if (featureFilesList.length > 0) {
|
|
175
|
+
featureData.files = featureFilesList;
|
|
176
|
+
writeFile(featurePath, JSON.stringify(featureData, null, 2));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
fs.unlinkSync(featurePath);
|
|
180
|
+
}
|
|
181
|
+
updatedFeatures.push(featureData.name);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch { /* ignore */ }
|
|
186
|
+
return updatedFeatures;
|
|
187
|
+
}
|
|
188
|
+
// ============================================================
|
|
189
|
+
// Flow Update
|
|
190
|
+
// ============================================================
|
|
191
|
+
export function updateFlows(rootDir, changedFiles, aiDir) {
|
|
192
|
+
const changedPaths = new Set(changedFiles.map(f => f.path));
|
|
193
|
+
const flowsDir = path.join(aiDir, "context", "flows");
|
|
194
|
+
const updatedFlows = [];
|
|
195
|
+
if (!fs.existsSync(flowsDir))
|
|
196
|
+
return updatedFlows;
|
|
197
|
+
try {
|
|
198
|
+
for (const flowFile of fs.readdirSync(flowsDir)) {
|
|
199
|
+
if (!flowFile.endsWith(".json"))
|
|
200
|
+
continue;
|
|
201
|
+
const flowPath = path.join(flowsDir, flowFile);
|
|
202
|
+
const flowData = readJsonFile(flowPath);
|
|
203
|
+
if (!flowData?.files)
|
|
204
|
+
continue;
|
|
205
|
+
const flowFileSet = new Set(flowData.files);
|
|
206
|
+
const affected = [...changedPaths].some(f => flowFileSet.has(f));
|
|
207
|
+
if (affected || changedFiles.some(c => c.status === "deleted")) {
|
|
208
|
+
const flowFilesList = flowData.files.filter(f => !changedFiles.some(c => c.path === f && c.status === "deleted"));
|
|
209
|
+
if (flowFilesList.length > 0) {
|
|
210
|
+
flowData.files = flowFilesList;
|
|
211
|
+
writeFile(flowPath, JSON.stringify(flowData, null, 2));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
fs.unlinkSync(flowPath);
|
|
215
|
+
}
|
|
216
|
+
updatedFlows.push(flowData.name);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch { /* ignore */ }
|
|
221
|
+
return updatedFlows;
|
|
222
|
+
}
|
|
223
|
+
// ============================================================
|
|
224
|
+
// Knowledge Graph Update
|
|
225
|
+
// ============================================================
|
|
226
|
+
export function updateKnowledgeGraph(rootDir, aiDir) {
|
|
227
|
+
try {
|
|
228
|
+
buildKnowledgeGraph(rootDir, aiDir);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// ============================================================
|
|
236
|
+
// Main Incremental Update
|
|
237
|
+
// ============================================================
|
|
238
|
+
export function runIncrementalUpdate(rootDir, aiDir) {
|
|
239
|
+
const targetAiDir = aiDir || path.join(rootDir, "ai");
|
|
240
|
+
const errors = [];
|
|
241
|
+
if (!fs.existsSync(targetAiDir)) {
|
|
242
|
+
return {
|
|
243
|
+
changedFiles: [],
|
|
244
|
+
updatedSymbols: 0,
|
|
245
|
+
updatedDependencies: 0,
|
|
246
|
+
updatedFeatures: [],
|
|
247
|
+
updatedFlows: [],
|
|
248
|
+
graphUpdated: false,
|
|
249
|
+
errors: ["AI context directory does not exist. Run 'ai-first init' first."]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const changedFiles = detectChangedFiles(rootDir);
|
|
253
|
+
if (changedFiles.length === 0) {
|
|
254
|
+
return {
|
|
255
|
+
changedFiles: [],
|
|
256
|
+
updatedSymbols: 0,
|
|
257
|
+
updatedDependencies: 0,
|
|
258
|
+
updatedFeatures: [],
|
|
259
|
+
updatedFlows: [],
|
|
260
|
+
graphUpdated: false,
|
|
261
|
+
errors: []
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
let updatedSymbols = 0;
|
|
265
|
+
try {
|
|
266
|
+
updatedSymbols = updateSymbols(rootDir, changedFiles, targetAiDir);
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
errors.push(`Failed to update symbols: ${e}`);
|
|
270
|
+
}
|
|
271
|
+
let updatedDependencies = 0;
|
|
272
|
+
try {
|
|
273
|
+
updatedDependencies = updateDependencies(rootDir, changedFiles, targetAiDir);
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
errors.push(`Failed to update dependencies: ${e}`);
|
|
277
|
+
}
|
|
278
|
+
let updatedFeatures = [];
|
|
279
|
+
try {
|
|
280
|
+
updatedFeatures = updateFeatures(rootDir, changedFiles, targetAiDir);
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
errors.push(`Failed to update features: ${e}`);
|
|
284
|
+
}
|
|
285
|
+
let updatedFlows = [];
|
|
286
|
+
try {
|
|
287
|
+
updatedFlows = updateFlows(rootDir, changedFiles, targetAiDir);
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
errors.push(`Failed to update flows: ${e}`);
|
|
291
|
+
}
|
|
292
|
+
let graphUpdated = false;
|
|
293
|
+
try {
|
|
294
|
+
graphUpdated = updateKnowledgeGraph(rootDir, targetAiDir);
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
errors.push(`Failed to update knowledge graph: ${e}`);
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
updateIndexState(rootDir, changedFiles);
|
|
301
|
+
}
|
|
302
|
+
catch { /* non-critical */ }
|
|
303
|
+
return {
|
|
304
|
+
changedFiles,
|
|
305
|
+
updatedSymbols,
|
|
306
|
+
updatedDependencies,
|
|
307
|
+
updatedFeatures,
|
|
308
|
+
updatedFlows,
|
|
309
|
+
graphUpdated,
|
|
310
|
+
errors
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function updateIndexState(rootDir, changedFiles) {
|
|
314
|
+
const aiDir = path.join(rootDir, "ai");
|
|
315
|
+
let state = loadIndexState(aiDir);
|
|
316
|
+
if (!state) {
|
|
317
|
+
state = { version: "1.0.0", lastIndexed: new Date().toISOString(), totalFiles: 0, files: {} };
|
|
318
|
+
}
|
|
319
|
+
const filesMap = new Map(Object.entries(state.files));
|
|
320
|
+
for (const changed of changedFiles) {
|
|
321
|
+
const fullPath = path.join(rootDir, changed.path);
|
|
322
|
+
if (changed.status === "deleted") {
|
|
323
|
+
filesMap.delete(changed.path);
|
|
324
|
+
}
|
|
325
|
+
else if (fs.existsSync(fullPath)) {
|
|
326
|
+
const hashData = computeFileHash(fullPath);
|
|
327
|
+
if (hashData) {
|
|
328
|
+
filesMap.set(changed.path, {
|
|
329
|
+
path: changed.path,
|
|
330
|
+
hash: hashData.hash,
|
|
331
|
+
mtime: hashData.mtime,
|
|
332
|
+
size: hashData.size,
|
|
333
|
+
indexedAt: new Date().toISOString()
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
state.files = Object.fromEntries(filesMap);
|
|
339
|
+
state.totalFiles = filesMap.size;
|
|
340
|
+
state.lastIndexed = new Date().toISOString();
|
|
341
|
+
saveIndexState(aiDir, filesMap);
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=incrementalAnalyzer.js.map
|