pmp-gywd 3.3.0
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/LICENSE +27 -0
- package/README.md +567 -0
- package/bin/install.js +348 -0
- package/commands/gywd/add-phase.md +207 -0
- package/commands/gywd/anticipate.md +271 -0
- package/commands/gywd/bootstrap.md +336 -0
- package/commands/gywd/challenge.md +344 -0
- package/commands/gywd/check-drift.md +144 -0
- package/commands/gywd/complete-milestone.md +106 -0
- package/commands/gywd/consider-issues.md +202 -0
- package/commands/gywd/context.md +93 -0
- package/commands/gywd/create-roadmap.md +115 -0
- package/commands/gywd/deps.md +169 -0
- package/commands/gywd/digest.md +138 -0
- package/commands/gywd/discuss-milestone.md +47 -0
- package/commands/gywd/discuss-phase.md +60 -0
- package/commands/gywd/execute-plan.md +161 -0
- package/commands/gywd/extract-decisions.md +325 -0
- package/commands/gywd/health.md +150 -0
- package/commands/gywd/help.md +556 -0
- package/commands/gywd/history.md +278 -0
- package/commands/gywd/impact.md +317 -0
- package/commands/gywd/init.md +95 -0
- package/commands/gywd/insert-phase.md +227 -0
- package/commands/gywd/list-phase-assumptions.md +50 -0
- package/commands/gywd/map-codebase.md +84 -0
- package/commands/gywd/memory.md +159 -0
- package/commands/gywd/new-milestone.md +59 -0
- package/commands/gywd/new-project.md +315 -0
- package/commands/gywd/pause-work.md +123 -0
- package/commands/gywd/plan-fix.md +205 -0
- package/commands/gywd/plan-phase.md +93 -0
- package/commands/gywd/preview-plan.md +139 -0
- package/commands/gywd/profile.md +363 -0
- package/commands/gywd/progress.md +317 -0
- package/commands/gywd/remove-phase.md +338 -0
- package/commands/gywd/research-phase.md +91 -0
- package/commands/gywd/resume-work.md +40 -0
- package/commands/gywd/rollback.md +179 -0
- package/commands/gywd/status.md +42 -0
- package/commands/gywd/sync-github.md +234 -0
- package/commands/gywd/verify-work.md +71 -0
- package/commands/gywd/why.md +251 -0
- package/docs/COMMANDS.md +722 -0
- package/docs/CONTRIBUTING.md +342 -0
- package/docs/EXAMPLES.md +535 -0
- package/docs/GETTING-STARTED.md +262 -0
- package/docs/README.md +55 -0
- package/docs/RELEASING.md +159 -0
- package/get-your-work-done/core/agent-patterns.md +331 -0
- package/get-your-work-done/core/architecture.md +334 -0
- package/get-your-work-done/core/context-model-schema.json +154 -0
- package/get-your-work-done/core/decisions-schema.json +193 -0
- package/get-your-work-done/core/learning-state-schema.json +133 -0
- package/get-your-work-done/core/profile-schema.json +257 -0
- package/get-your-work-done/references/adaptive-decomposition.md +175 -0
- package/get-your-work-done/references/checkpoints.md +287 -0
- package/get-your-work-done/references/confidence-scoring.md +169 -0
- package/get-your-work-done/references/continuation-format.md +255 -0
- package/get-your-work-done/references/git-integration.md +254 -0
- package/get-your-work-done/references/plan-format.md +428 -0
- package/get-your-work-done/references/principles.md +157 -0
- package/get-your-work-done/references/questioning.md +162 -0
- package/get-your-work-done/references/research-pitfalls.md +215 -0
- package/get-your-work-done/references/scope-estimation.md +172 -0
- package/get-your-work-done/references/tdd.md +263 -0
- package/get-your-work-done/templates/codebase/architecture.md +255 -0
- package/get-your-work-done/templates/codebase/concerns.md +310 -0
- package/get-your-work-done/templates/codebase/conventions.md +307 -0
- package/get-your-work-done/templates/codebase/integrations.md +280 -0
- package/get-your-work-done/templates/codebase/stack.md +186 -0
- package/get-your-work-done/templates/codebase/structure.md +285 -0
- package/get-your-work-done/templates/codebase/testing.md +480 -0
- package/get-your-work-done/templates/config.json +18 -0
- package/get-your-work-done/templates/context.md +161 -0
- package/get-your-work-done/templates/continue-here.md +78 -0
- package/get-your-work-done/templates/discovery.md +146 -0
- package/get-your-work-done/templates/issues.md +32 -0
- package/get-your-work-done/templates/milestone-archive.md +123 -0
- package/get-your-work-done/templates/milestone-context.md +93 -0
- package/get-your-work-done/templates/milestone.md +115 -0
- package/get-your-work-done/templates/phase-prompt.md +303 -0
- package/get-your-work-done/templates/project.md +184 -0
- package/get-your-work-done/templates/research.md +529 -0
- package/get-your-work-done/templates/roadmap.md +196 -0
- package/get-your-work-done/templates/state.md +210 -0
- package/get-your-work-done/templates/summary.md +273 -0
- package/get-your-work-done/templates/uat-issues.md +143 -0
- package/get-your-work-done/workflows/complete-milestone.md +643 -0
- package/get-your-work-done/workflows/create-milestone.md +416 -0
- package/get-your-work-done/workflows/create-roadmap.md +481 -0
- package/get-your-work-done/workflows/discovery-phase.md +293 -0
- package/get-your-work-done/workflows/discuss-milestone.md +236 -0
- package/get-your-work-done/workflows/discuss-phase.md +247 -0
- package/get-your-work-done/workflows/execute-phase.md +1625 -0
- package/get-your-work-done/workflows/list-phase-assumptions.md +178 -0
- package/get-your-work-done/workflows/map-codebase.md +434 -0
- package/get-your-work-done/workflows/plan-phase.md +488 -0
- package/get-your-work-done/workflows/research-phase.md +436 -0
- package/get-your-work-done/workflows/resume-project.md +287 -0
- package/get-your-work-done/workflows/transition.md +580 -0
- package/get-your-work-done/workflows/verify-work.md +202 -0
- package/lib/automation/dependency-analyzer.js +635 -0
- package/lib/automation/doc-generator.js +643 -0
- package/lib/automation/index.js +42 -0
- package/lib/automation/test-generator.js +628 -0
- package/lib/context/context-analyzer.js +554 -0
- package/lib/context/context-cache.js +426 -0
- package/lib/context/context-predictor.js +622 -0
- package/lib/context/index.js +44 -0
- package/lib/memory/confidence-calibrator.js +484 -0
- package/lib/memory/feedback-collector.js +551 -0
- package/lib/memory/global-memory.js +465 -0
- package/lib/memory/index.js +75 -0
- package/lib/memory/pattern-aggregator.js +487 -0
- package/lib/memory/team-sync.js +501 -0
- package/lib/profile/index.js +24 -0
- package/lib/profile/pattern-learner.js +303 -0
- package/lib/profile/profile-manager.js +445 -0
- package/lib/questioning/index.js +49 -0
- package/lib/questioning/question-engine.js +311 -0
- package/lib/questioning/question-templates.js +315 -0
- package/lib/validators/command-validator.js +188 -0
- package/lib/validators/index.js +29 -0
- package/lib/validators/schema-validator.js +183 -0
- package/package.json +61 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Predictor
|
|
3
|
+
*
|
|
4
|
+
* Predicts what context a developer needs based on patterns,
|
|
5
|
+
* relationships, and access history. The "anticipate" brain.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { ContextAnalyzer, RELATIONSHIP_WEIGHTS: _RELATIONSHIP_WEIGHTS } = require('./context-analyzer');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prediction confidence levels
|
|
12
|
+
*/
|
|
13
|
+
const CONFIDENCE = {
|
|
14
|
+
HIGH: 'high',
|
|
15
|
+
MEDIUM: 'medium',
|
|
16
|
+
LOW: 'low',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Context types
|
|
21
|
+
*/
|
|
22
|
+
const CONTEXT_TYPES = {
|
|
23
|
+
FILE: 'file',
|
|
24
|
+
DECISION: 'decision',
|
|
25
|
+
PATTERN: 'pattern',
|
|
26
|
+
DOCUMENTATION: 'documentation',
|
|
27
|
+
HISTORY: 'history',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Access pattern tracker
|
|
32
|
+
*/
|
|
33
|
+
class AccessPattern {
|
|
34
|
+
constructor(maxSize = 1000) {
|
|
35
|
+
this.accesses = [];
|
|
36
|
+
this.sessions = [];
|
|
37
|
+
this.currentSession = [];
|
|
38
|
+
this.maxSize = maxSize;
|
|
39
|
+
this.coOccurrence = new Map();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Record a file access
|
|
44
|
+
* @param {string} file - File path
|
|
45
|
+
* @param {string} context - Access context (e.g., 'read', 'edit', 'search')
|
|
46
|
+
*/
|
|
47
|
+
recordAccess(file, context = 'read') {
|
|
48
|
+
const entry = {
|
|
49
|
+
file,
|
|
50
|
+
timestamp: Date.now(),
|
|
51
|
+
context,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.accesses.push(entry);
|
|
55
|
+
this.currentSession.push(file);
|
|
56
|
+
|
|
57
|
+
// Trim if exceeding max size
|
|
58
|
+
if (this.accesses.length > this.maxSize) {
|
|
59
|
+
this.accesses = this.accesses.slice(-this.maxSize);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Update co-occurrence with recent accesses
|
|
63
|
+
const recentFiles = this.currentSession.slice(-5);
|
|
64
|
+
for (const recent of recentFiles) {
|
|
65
|
+
if (recent !== file) {
|
|
66
|
+
this.incrementCoOccurrence(file, recent);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Increment co-occurrence count
|
|
73
|
+
* @param {string} file1 - First file
|
|
74
|
+
* @param {string} file2 - Second file
|
|
75
|
+
*/
|
|
76
|
+
incrementCoOccurrence(file1, file2) {
|
|
77
|
+
if (!this.coOccurrence.has(file1)) {
|
|
78
|
+
this.coOccurrence.set(file1, new Map());
|
|
79
|
+
}
|
|
80
|
+
if (!this.coOccurrence.has(file2)) {
|
|
81
|
+
this.coOccurrence.set(file2, new Map());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const count1 = this.coOccurrence.get(file1).get(file2) || 0;
|
|
85
|
+
this.coOccurrence.get(file1).set(file2, count1 + 1);
|
|
86
|
+
|
|
87
|
+
const count2 = this.coOccurrence.get(file2).get(file1) || 0;
|
|
88
|
+
this.coOccurrence.get(file2).set(file1, count2 + 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* End current session and start new one
|
|
93
|
+
*/
|
|
94
|
+
endSession() {
|
|
95
|
+
if (this.currentSession.length > 0) {
|
|
96
|
+
this.sessions.push([...this.currentSession]);
|
|
97
|
+
this.currentSession = [];
|
|
98
|
+
|
|
99
|
+
// Keep only last 50 sessions
|
|
100
|
+
if (this.sessions.length > 50) {
|
|
101
|
+
this.sessions = this.sessions.slice(-50);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get files frequently accessed with given file
|
|
108
|
+
* @param {string} file - File path
|
|
109
|
+
* @param {number} limit - Maximum results
|
|
110
|
+
* @returns {Array<{file: string, count: number}>}
|
|
111
|
+
*/
|
|
112
|
+
getFrequentlyAccessedWith(file, limit = 10) {
|
|
113
|
+
const coOccurMap = this.coOccurrence.get(file);
|
|
114
|
+
if (!coOccurMap) return [];
|
|
115
|
+
|
|
116
|
+
const results = [];
|
|
117
|
+
for (const [otherFile, count] of coOccurMap) {
|
|
118
|
+
results.push({ file: otherFile, count });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
results.sort((a, b) => b.count - a.count);
|
|
122
|
+
return results.slice(0, limit);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get recently accessed files
|
|
127
|
+
* @param {number} limit - Maximum results
|
|
128
|
+
* @returns {string[]}
|
|
129
|
+
*/
|
|
130
|
+
getRecentFiles(limit = 10) {
|
|
131
|
+
const seen = new Set();
|
|
132
|
+
const result = [];
|
|
133
|
+
|
|
134
|
+
for (let i = this.accesses.length - 1; i >= 0 && result.length < limit; i--) {
|
|
135
|
+
const file = this.accesses[i].file;
|
|
136
|
+
if (!seen.has(file)) {
|
|
137
|
+
seen.add(file);
|
|
138
|
+
result.push(file);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get session patterns (files commonly accessed together)
|
|
147
|
+
* @returns {Array<{files: string[], frequency: number}>}
|
|
148
|
+
*/
|
|
149
|
+
getSessionPatterns() {
|
|
150
|
+
const patterns = new Map();
|
|
151
|
+
|
|
152
|
+
for (const session of this.sessions) {
|
|
153
|
+
// Look for pairs that appear together
|
|
154
|
+
for (let i = 0; i < session.length; i++) {
|
|
155
|
+
for (let j = i + 1; j < session.length; j++) {
|
|
156
|
+
const key = [session[i], session[j]].sort().join('|');
|
|
157
|
+
patterns.set(key, (patterns.get(key) || 0) + 1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = [];
|
|
163
|
+
for (const [key, frequency] of patterns) {
|
|
164
|
+
if (frequency >= 2) { // Appeared in at least 2 sessions
|
|
165
|
+
result.push({
|
|
166
|
+
files: key.split('|'),
|
|
167
|
+
frequency,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
result.sort((a, b) => b.frequency - a.frequency);
|
|
173
|
+
return result.slice(0, 20);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Export access data
|
|
178
|
+
* @returns {object}
|
|
179
|
+
*/
|
|
180
|
+
export() {
|
|
181
|
+
const coOccur = {};
|
|
182
|
+
for (const [file, map] of this.coOccurrence) {
|
|
183
|
+
coOccur[file] = Object.fromEntries(map);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
accesses: this.accesses.slice(-500),
|
|
188
|
+
sessions: this.sessions.slice(-20),
|
|
189
|
+
coOccurrence: coOccur,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Import access data
|
|
195
|
+
* @param {object} data - Previously exported data
|
|
196
|
+
*/
|
|
197
|
+
import(data) {
|
|
198
|
+
if (data.accesses) {
|
|
199
|
+
this.accesses = data.accesses;
|
|
200
|
+
}
|
|
201
|
+
if (data.sessions) {
|
|
202
|
+
this.sessions = data.sessions;
|
|
203
|
+
}
|
|
204
|
+
if (data.coOccurrence) {
|
|
205
|
+
for (const [file, map] of Object.entries(data.coOccurrence)) {
|
|
206
|
+
this.coOccurrence.set(file, new Map(Object.entries(map)));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Clear all data
|
|
213
|
+
*/
|
|
214
|
+
clear() {
|
|
215
|
+
this.accesses = [];
|
|
216
|
+
this.sessions = [];
|
|
217
|
+
this.currentSession = [];
|
|
218
|
+
this.coOccurrence.clear();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Context Predictor class
|
|
224
|
+
*/
|
|
225
|
+
class ContextPredictor {
|
|
226
|
+
constructor() {
|
|
227
|
+
this.analyzer = new ContextAnalyzer();
|
|
228
|
+
this.accessPattern = new AccessPattern();
|
|
229
|
+
this.featureFileMap = new Map(); // feature -> Set<files>
|
|
230
|
+
this.taskHistory = []; // [{task, files, timestamp}]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get the context analyzer
|
|
235
|
+
* @returns {ContextAnalyzer}
|
|
236
|
+
*/
|
|
237
|
+
getAnalyzer() {
|
|
238
|
+
return this.analyzer;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Record file access
|
|
243
|
+
* @param {string} file - File path
|
|
244
|
+
* @param {string} context - Access context
|
|
245
|
+
*/
|
|
246
|
+
recordAccess(file, context = 'read') {
|
|
247
|
+
this.accessPattern.recordAccess(file, context);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Record a task with its associated files
|
|
252
|
+
* @param {string} task - Task description
|
|
253
|
+
* @param {string[]} files - Files used for task
|
|
254
|
+
*/
|
|
255
|
+
recordTask(task, files) {
|
|
256
|
+
this.taskHistory.push({
|
|
257
|
+
task,
|
|
258
|
+
files,
|
|
259
|
+
timestamp: Date.now(),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Keep last 100 tasks
|
|
263
|
+
if (this.taskHistory.length > 100) {
|
|
264
|
+
this.taskHistory = this.taskHistory.slice(-100);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Extract keywords and map to files
|
|
268
|
+
const keywords = this.extractTaskKeywords(task);
|
|
269
|
+
for (const keyword of keywords) {
|
|
270
|
+
if (!this.featureFileMap.has(keyword)) {
|
|
271
|
+
this.featureFileMap.set(keyword, new Set());
|
|
272
|
+
}
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
this.featureFileMap.get(keyword).add(file);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Extract keywords from task description
|
|
281
|
+
* @param {string} task - Task description
|
|
282
|
+
* @returns {string[]} Keywords
|
|
283
|
+
*/
|
|
284
|
+
extractTaskKeywords(task) {
|
|
285
|
+
const words = task
|
|
286
|
+
.toLowerCase()
|
|
287
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
288
|
+
.split(/\s+/)
|
|
289
|
+
.filter(w => w.length > 2);
|
|
290
|
+
|
|
291
|
+
// Filter common words
|
|
292
|
+
const stopWords = new Set([
|
|
293
|
+
'the', 'and', 'for', 'add', 'fix', 'update', 'create', 'implement',
|
|
294
|
+
'make', 'use', 'with', 'from', 'this', 'that', 'will', 'should',
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
return words.filter(w => !stopWords.has(w));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Predict context needed for a task
|
|
302
|
+
* @param {string} task - Task description
|
|
303
|
+
* @param {object} options - Prediction options
|
|
304
|
+
* @returns {object} Predicted context
|
|
305
|
+
*/
|
|
306
|
+
predictForTask(task, options = {}) {
|
|
307
|
+
const {
|
|
308
|
+
maxFiles = 10,
|
|
309
|
+
includeDecisions: _includeDecisions = true,
|
|
310
|
+
includePatterns: _includePatterns = true,
|
|
311
|
+
} = options;
|
|
312
|
+
|
|
313
|
+
const predictions = {
|
|
314
|
+
files: [],
|
|
315
|
+
decisions: [],
|
|
316
|
+
patterns: [],
|
|
317
|
+
confidence: CONFIDENCE.MEDIUM,
|
|
318
|
+
reasoning: [],
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const keywords = this.extractTaskKeywords(task);
|
|
322
|
+
const fileScores = new Map();
|
|
323
|
+
|
|
324
|
+
// Score files based on task keywords
|
|
325
|
+
for (const keyword of keywords) {
|
|
326
|
+
// Check feature-file map from task history
|
|
327
|
+
const featureFiles = this.featureFileMap.get(keyword);
|
|
328
|
+
if (featureFiles) {
|
|
329
|
+
for (const file of featureFiles) {
|
|
330
|
+
const current = fileScores.get(file) || 0;
|
|
331
|
+
fileScores.set(file, current + 0.8);
|
|
332
|
+
}
|
|
333
|
+
predictions.reasoning.push(`Keyword "${keyword}" matched from task history`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Check analyzer keywords
|
|
337
|
+
const analyzerFiles = this.analyzer.findByKeyword(keyword);
|
|
338
|
+
for (const file of analyzerFiles) {
|
|
339
|
+
const current = fileScores.get(file) || 0;
|
|
340
|
+
fileScores.set(file, current + 0.5);
|
|
341
|
+
}
|
|
342
|
+
if (analyzerFiles.length > 0) {
|
|
343
|
+
predictions.reasoning.push(`Keyword "${keyword}" found in ${analyzerFiles.length} files`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Add scores from recent access patterns
|
|
348
|
+
const recentFiles = this.accessPattern.getRecentFiles(5);
|
|
349
|
+
for (const file of recentFiles) {
|
|
350
|
+
// Get related files
|
|
351
|
+
const related = this.analyzer.getRelatedFiles(file, 5);
|
|
352
|
+
for (const { file: relatedFile, score } of related) {
|
|
353
|
+
const current = fileScores.get(relatedFile) || 0;
|
|
354
|
+
fileScores.set(relatedFile, current + score * 0.3);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Check session patterns
|
|
359
|
+
const sessionPatterns = this.accessPattern.getSessionPatterns();
|
|
360
|
+
for (const { files: patternFiles } of sessionPatterns.slice(0, 5)) {
|
|
361
|
+
// If one file in pattern is scored, boost the others
|
|
362
|
+
for (const file of patternFiles) {
|
|
363
|
+
if (fileScores.has(file)) {
|
|
364
|
+
for (const otherFile of patternFiles) {
|
|
365
|
+
if (otherFile !== file) {
|
|
366
|
+
const current = fileScores.get(otherFile) || 0;
|
|
367
|
+
fileScores.set(otherFile, current + 0.4);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Sort and limit results
|
|
375
|
+
const sortedFiles = Array.from(fileScores.entries())
|
|
376
|
+
.map(([file, score]) => ({ file, score }))
|
|
377
|
+
.sort((a, b) => b.score - a.score)
|
|
378
|
+
.slice(0, maxFiles);
|
|
379
|
+
|
|
380
|
+
predictions.files = sortedFiles.map(({ file, score }) => ({
|
|
381
|
+
path: file,
|
|
382
|
+
score,
|
|
383
|
+
confidence: score > 1.5 ? CONFIDENCE.HIGH :
|
|
384
|
+
score > 0.8 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW,
|
|
385
|
+
type: CONTEXT_TYPES.FILE,
|
|
386
|
+
}));
|
|
387
|
+
|
|
388
|
+
// Determine overall confidence
|
|
389
|
+
if (predictions.files.length > 0) {
|
|
390
|
+
const avgScore = predictions.files.reduce((a, b) => a + b.score, 0) / predictions.files.length;
|
|
391
|
+
predictions.confidence = avgScore > 1.5 ? CONFIDENCE.HIGH :
|
|
392
|
+
avgScore > 0.8 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return predictions;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Predict context for working with a specific file
|
|
400
|
+
* @param {string} filePath - File being worked on
|
|
401
|
+
* @param {object} options - Prediction options
|
|
402
|
+
* @returns {object} Predicted context
|
|
403
|
+
*/
|
|
404
|
+
predictForFile(filePath, options = {}) {
|
|
405
|
+
const { maxFiles = 10 } = options;
|
|
406
|
+
|
|
407
|
+
const predictions = {
|
|
408
|
+
files: [],
|
|
409
|
+
confidence: CONFIDENCE.MEDIUM,
|
|
410
|
+
reasoning: [],
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const fileScores = new Map();
|
|
414
|
+
|
|
415
|
+
// Get directly related files from analyzer
|
|
416
|
+
const related = this.analyzer.getRelatedFiles(filePath, maxFiles * 2);
|
|
417
|
+
for (const { file, score, relationships } of related) {
|
|
418
|
+
fileScores.set(file, score);
|
|
419
|
+
predictions.reasoning.push(`${file} related via: ${relationships.join(', ')}`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Get frequently accessed together
|
|
423
|
+
const frequentlyWith = this.accessPattern.getFrequentlyAccessedWith(filePath, 5);
|
|
424
|
+
for (const { file, count } of frequentlyWith) {
|
|
425
|
+
const current = fileScores.get(file) || 0;
|
|
426
|
+
const bonus = Math.min(count * 0.2, 1.0); // Cap bonus at 1.0
|
|
427
|
+
fileScores.set(file, current + bonus);
|
|
428
|
+
predictions.reasoning.push(`${file} accessed together ${count} times`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Sort and format results
|
|
432
|
+
const sortedFiles = Array.from(fileScores.entries())
|
|
433
|
+
.map(([file, score]) => ({ file, score }))
|
|
434
|
+
.sort((a, b) => b.score - a.score)
|
|
435
|
+
.slice(0, maxFiles);
|
|
436
|
+
|
|
437
|
+
predictions.files = sortedFiles.map(({ file, score }) => ({
|
|
438
|
+
path: file,
|
|
439
|
+
score,
|
|
440
|
+
confidence: score > 2.0 ? CONFIDENCE.HIGH :
|
|
441
|
+
score > 1.0 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW,
|
|
442
|
+
type: CONTEXT_TYPES.FILE,
|
|
443
|
+
}));
|
|
444
|
+
|
|
445
|
+
if (predictions.files.length > 0) {
|
|
446
|
+
const maxScore = predictions.files[0].score;
|
|
447
|
+
predictions.confidence = maxScore > 2.0 ? CONFIDENCE.HIGH :
|
|
448
|
+
maxScore > 1.0 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return predictions;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Predict context for a feature/topic
|
|
456
|
+
* @param {string} feature - Feature or topic name
|
|
457
|
+
* @param {object} options - Prediction options
|
|
458
|
+
* @returns {object} Predicted context
|
|
459
|
+
*/
|
|
460
|
+
predictForFeature(feature, options = {}) {
|
|
461
|
+
const { maxFiles = 15 } = options;
|
|
462
|
+
|
|
463
|
+
const predictions = {
|
|
464
|
+
files: [],
|
|
465
|
+
confidence: CONFIDENCE.LOW,
|
|
466
|
+
reasoning: [],
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const keywords = feature
|
|
470
|
+
.toLowerCase()
|
|
471
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
472
|
+
.split(/\s+/)
|
|
473
|
+
.filter(w => w.length > 2);
|
|
474
|
+
|
|
475
|
+
const fileScores = new Map();
|
|
476
|
+
|
|
477
|
+
// Search by keywords
|
|
478
|
+
for (const keyword of keywords) {
|
|
479
|
+
const files = this.analyzer.findByKeyword(keyword);
|
|
480
|
+
for (const file of files) {
|
|
481
|
+
const current = fileScores.get(file) || 0;
|
|
482
|
+
fileScores.set(file, current + 1.0);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Check feature-file map
|
|
486
|
+
const featureFiles = this.featureFileMap.get(keyword);
|
|
487
|
+
if (featureFiles) {
|
|
488
|
+
for (const file of featureFiles) {
|
|
489
|
+
const current = fileScores.get(file) || 0;
|
|
490
|
+
fileScores.set(file, current + 1.5);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Search by multiple keywords (intersection gets bonus)
|
|
496
|
+
if (keywords.length > 1) {
|
|
497
|
+
const intersectionFiles = this.analyzer.findByKeywords(keywords);
|
|
498
|
+
for (const file of intersectionFiles) {
|
|
499
|
+
const current = fileScores.get(file) || 0;
|
|
500
|
+
fileScores.set(file, current + 2.0);
|
|
501
|
+
predictions.reasoning.push(`${file} matches multiple keywords`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Add related files for top scored files
|
|
506
|
+
const topFiles = Array.from(fileScores.entries())
|
|
507
|
+
.sort((a, b) => b[1] - a[1])
|
|
508
|
+
.slice(0, 3);
|
|
509
|
+
|
|
510
|
+
for (const [file] of topFiles) {
|
|
511
|
+
const related = this.analyzer.getRelatedFiles(file, 3);
|
|
512
|
+
for (const { file: relatedFile, score } of related) {
|
|
513
|
+
const current = fileScores.get(relatedFile) || 0;
|
|
514
|
+
fileScores.set(relatedFile, current + score * 0.5);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Sort and format results
|
|
519
|
+
const sortedFiles = Array.from(fileScores.entries())
|
|
520
|
+
.map(([file, score]) => ({ file, score }))
|
|
521
|
+
.sort((a, b) => b.score - a.score)
|
|
522
|
+
.slice(0, maxFiles);
|
|
523
|
+
|
|
524
|
+
predictions.files = sortedFiles.map(({ file, score }) => ({
|
|
525
|
+
path: file,
|
|
526
|
+
score,
|
|
527
|
+
confidence: score > 3.0 ? CONFIDENCE.HIGH :
|
|
528
|
+
score > 1.5 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW,
|
|
529
|
+
type: CONTEXT_TYPES.FILE,
|
|
530
|
+
}));
|
|
531
|
+
|
|
532
|
+
if (predictions.files.length > 0) {
|
|
533
|
+
predictions.confidence = predictions.files[0].score > 3.0 ? CONFIDENCE.HIGH :
|
|
534
|
+
predictions.files[0].score > 1.5 ? CONFIDENCE.MEDIUM : CONFIDENCE.LOW;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
predictions.reasoning.push(`Searched for feature: "${feature}"`);
|
|
538
|
+
predictions.reasoning.push(`Found ${predictions.files.length} relevant files`);
|
|
539
|
+
|
|
540
|
+
return predictions;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Get prediction summary
|
|
545
|
+
* @returns {object} Summary statistics
|
|
546
|
+
*/
|
|
547
|
+
getSummary() {
|
|
548
|
+
return {
|
|
549
|
+
analyzer: this.analyzer.getSummary(),
|
|
550
|
+
accessPatterns: {
|
|
551
|
+
totalAccesses: this.accessPattern.accesses.length,
|
|
552
|
+
sessionsTracked: this.accessPattern.sessions.length,
|
|
553
|
+
currentSessionFiles: this.accessPattern.currentSession.length,
|
|
554
|
+
},
|
|
555
|
+
featuresTracked: this.featureFileMap.size,
|
|
556
|
+
tasksRecorded: this.taskHistory.length,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* End current session
|
|
562
|
+
*/
|
|
563
|
+
endSession() {
|
|
564
|
+
this.accessPattern.endSession();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Export all data for persistence
|
|
569
|
+
* @returns {object}
|
|
570
|
+
*/
|
|
571
|
+
export() {
|
|
572
|
+
const featureMap = {};
|
|
573
|
+
for (const [feature, files] of this.featureFileMap) {
|
|
574
|
+
featureMap[feature] = Array.from(files);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return {
|
|
578
|
+
analyzer: this.analyzer.export(),
|
|
579
|
+
accessPattern: this.accessPattern.export(),
|
|
580
|
+
featureFileMap: featureMap,
|
|
581
|
+
taskHistory: this.taskHistory.slice(-50),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Import data from persistence
|
|
587
|
+
* @param {object} data - Previously exported data
|
|
588
|
+
*/
|
|
589
|
+
import(data) {
|
|
590
|
+
if (data.analyzer) {
|
|
591
|
+
this.analyzer.import(data.analyzer);
|
|
592
|
+
}
|
|
593
|
+
if (data.accessPattern) {
|
|
594
|
+
this.accessPattern.import(data.accessPattern);
|
|
595
|
+
}
|
|
596
|
+
if (data.featureFileMap) {
|
|
597
|
+
for (const [feature, files] of Object.entries(data.featureFileMap)) {
|
|
598
|
+
this.featureFileMap.set(feature, new Set(files));
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (data.taskHistory) {
|
|
602
|
+
this.taskHistory = data.taskHistory;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Clear all data
|
|
608
|
+
*/
|
|
609
|
+
clear() {
|
|
610
|
+
this.analyzer.clear();
|
|
611
|
+
this.accessPattern.clear();
|
|
612
|
+
this.featureFileMap.clear();
|
|
613
|
+
this.taskHistory = [];
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
module.exports = {
|
|
618
|
+
ContextPredictor,
|
|
619
|
+
AccessPattern,
|
|
620
|
+
CONFIDENCE,
|
|
621
|
+
CONTEXT_TYPES,
|
|
622
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Module
|
|
3
|
+
*
|
|
4
|
+
* Intelligent context prediction and management.
|
|
5
|
+
* The "anticipate" brain - pre-loads what you need before you ask.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
ContextAnalyzer,
|
|
10
|
+
RELATIONSHIP_TYPES,
|
|
11
|
+
RELATIONSHIP_WEIGHTS,
|
|
12
|
+
} = require('./context-analyzer');
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
ContextPredictor,
|
|
16
|
+
AccessPattern,
|
|
17
|
+
CONFIDENCE,
|
|
18
|
+
CONTEXT_TYPES,
|
|
19
|
+
} = require('./context-predictor');
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
LRUCache,
|
|
23
|
+
ContextCache,
|
|
24
|
+
} = require('./context-cache');
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
// Classes
|
|
28
|
+
ContextAnalyzer,
|
|
29
|
+
ContextPredictor,
|
|
30
|
+
AccessPattern,
|
|
31
|
+
LRUCache,
|
|
32
|
+
ContextCache,
|
|
33
|
+
|
|
34
|
+
// Constants
|
|
35
|
+
RELATIONSHIP_TYPES,
|
|
36
|
+
RELATIONSHIP_WEIGHTS,
|
|
37
|
+
CONFIDENCE,
|
|
38
|
+
CONTEXT_TYPES,
|
|
39
|
+
|
|
40
|
+
// Factory functions
|
|
41
|
+
createContextPredictor: () => new ContextPredictor(),
|
|
42
|
+
createContextCache: (options) => new ContextCache(options),
|
|
43
|
+
createContextAnalyzer: () => new ContextAnalyzer(),
|
|
44
|
+
};
|