@girardmedia/bootspring 2.2.0 → 2.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/README.md +2 -2
- package/bin/bootspring.js +35 -96
- package/claude-commands/agent.md +34 -0
- package/claude-commands/bs.md +31 -0
- package/claude-commands/build.md +25 -0
- package/claude-commands/skill.md +31 -0
- package/claude-commands/todo.md +25 -0
- package/dist/cli/index.cjs +17808 -0
- package/dist/core/index.d.ts +5814 -0
- package/dist/core.js +5780 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp-server.js +2299 -0
- package/generators/api-docs.js +2 -2
- package/generators/decisions.js +3 -3
- package/generators/health.js +16 -16
- package/generators/sprint.js +2 -2
- package/package.json +27 -59
- package/core/api-client.d.ts +0 -69
- package/core/api-client.js +0 -1482
- package/core/auth.d.ts +0 -98
- package/core/auth.js +0 -737
- package/core/build-orchestrator.js +0 -508
- package/core/build-state.js +0 -612
- package/core/config.d.ts +0 -106
- package/core/config.js +0 -1328
- package/core/context-loader.js +0 -580
- package/core/context.d.ts +0 -61
- package/core/context.js +0 -327
- package/core/entitlements.d.ts +0 -70
- package/core/entitlements.js +0 -322
- package/core/index.d.ts +0 -53
- package/core/index.js +0 -62
- package/core/mcp-config.js +0 -115
- package/core/policies.d.ts +0 -43
- package/core/policies.js +0 -113
- package/core/policy-matrix.js +0 -303
- package/core/project-activity.js +0 -175
- package/core/redaction.d.ts +0 -5
- package/core/redaction.js +0 -63
- package/core/self-update.js +0 -259
- package/core/session.js +0 -353
- package/core/task-extractor.js +0 -1098
- package/core/telemetry.d.ts +0 -55
- package/core/telemetry.js +0 -617
- package/core/tier-enforcement.js +0 -928
- package/core/utils.d.ts +0 -90
- package/core/utils.js +0 -455
- package/core/validation.js +0 -572
- package/mcp/server.d.ts +0 -57
- package/mcp/server.js +0 -264
package/core/context-loader.js
DELETED
|
@@ -1,580 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Context Loader
|
|
3
|
-
* Loads and indexes project context files for AI agent access
|
|
4
|
-
*
|
|
5
|
-
* Context Types:
|
|
6
|
-
* - seed: SEED.md project configuration
|
|
7
|
-
* - claude: CLAUDE.md AI context
|
|
8
|
-
* - plan: Planning documents (MASTER_PLAN, PRD, etc.)
|
|
9
|
-
* - business: Business documents (plans, competitive analysis)
|
|
10
|
-
* - legal: Legal documents (terms, privacy)
|
|
11
|
-
* - mvp: MVP source code
|
|
12
|
-
* - logs: Action logs and history
|
|
13
|
-
*
|
|
14
|
-
* @package bootspring
|
|
15
|
-
* @module core/context-loader
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
const config = require('./config');
|
|
21
|
-
const utils = require('./utils');
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Default context file locations
|
|
25
|
-
*/
|
|
26
|
-
const CONTEXT_LOCATIONS = {
|
|
27
|
-
// Root level files
|
|
28
|
-
seed: 'SEED.md',
|
|
29
|
-
claude: 'CLAUDE.md',
|
|
30
|
-
todo: 'todo.md',
|
|
31
|
-
readme: 'README.md',
|
|
32
|
-
changelog: 'CHANGELOG.md',
|
|
33
|
-
|
|
34
|
-
// Planning folder
|
|
35
|
-
plan: 'planning/MASTER_PLAN.md',
|
|
36
|
-
prd: 'planning/PRD.md',
|
|
37
|
-
architecture: 'planning/ARCHITECTURE.md',
|
|
38
|
-
decisions: 'planning/DECISION_LOG.md',
|
|
39
|
-
roadmap: 'planning/ROADMAP.md',
|
|
40
|
-
|
|
41
|
-
// Business documents
|
|
42
|
-
business_plan: 'planning/BUSINESS_PLAN.md',
|
|
43
|
-
pitch_deck: 'planning/PITCH_DECK.md',
|
|
44
|
-
competitive: 'planning/COMPETITIVE_ANALYSIS.md',
|
|
45
|
-
market: 'planning/MARKET_RESEARCH.md',
|
|
46
|
-
financial: 'planning/FINANCIAL_MODEL.md',
|
|
47
|
-
|
|
48
|
-
// Legal documents
|
|
49
|
-
terms: 'legal/TERMS_OF_SERVICE.md',
|
|
50
|
-
privacy: 'legal/PRIVACY_POLICY.md',
|
|
51
|
-
legal_checklist: 'legal/LEGAL_CHECKLIST.md',
|
|
52
|
-
|
|
53
|
-
// MVP and code context
|
|
54
|
-
mvp_source: '.bootspring/mvp/source/',
|
|
55
|
-
mvp_references: '.bootspring/mvp/references/',
|
|
56
|
-
|
|
57
|
-
// Logs
|
|
58
|
-
logs: '.bootspring/logs/',
|
|
59
|
-
context_index: '.bootspring/context-index.json'
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Agent to context mapping
|
|
64
|
-
* Each agent receives relevant context files automatically
|
|
65
|
-
*/
|
|
66
|
-
const AGENT_CONTEXT_MAP = {
|
|
67
|
-
// Technical agents
|
|
68
|
-
'frontend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
|
|
69
|
-
'backend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
|
|
70
|
-
'database-expert': ['seed', 'architecture', 'mvp_source'],
|
|
71
|
-
'api-expert': ['seed', 'architecture', 'prd'],
|
|
72
|
-
'security-expert': ['architecture', 'legal_checklist', 'prd'],
|
|
73
|
-
'testing-expert': ['architecture', 'prd'],
|
|
74
|
-
'performance-expert': ['architecture', 'mvp_source'],
|
|
75
|
-
'devops-expert': ['architecture', 'plan'],
|
|
76
|
-
'ui-ux-expert': ['seed', 'prd', 'mvp_source'],
|
|
77
|
-
'code-review-expert': ['architecture', 'mvp_source'],
|
|
78
|
-
|
|
79
|
-
// Business agents
|
|
80
|
-
'business-strategy-expert': ['seed', 'business_plan', 'competitive', 'plan'],
|
|
81
|
-
'financial-expert': ['seed', 'business_plan', 'financial'],
|
|
82
|
-
'fundraising-expert': ['pitch_deck', 'financial', 'business_plan'],
|
|
83
|
-
'legal-expert': ['terms', 'privacy', 'legal_checklist'],
|
|
84
|
-
'marketing-expert': ['business_plan', 'competitive', 'plan'],
|
|
85
|
-
'sales-expert': ['business_plan', 'competitive', 'prd'],
|
|
86
|
-
'growth-expert': ['plan', 'competitive', 'market'],
|
|
87
|
-
'operations-expert': ['plan', 'architecture'],
|
|
88
|
-
'competitive-analysis-expert': ['competitive', 'market', 'business_plan'],
|
|
89
|
-
'market-research-expert': ['market', 'competitive', 'business_plan'],
|
|
90
|
-
'investor-relations-expert': ['pitch_deck', 'financial', 'plan'],
|
|
91
|
-
|
|
92
|
-
// Research agents
|
|
93
|
-
'research-expert': ['plan', 'architecture', 'prd'],
|
|
94
|
-
'data-modeling-expert': ['architecture', 'prd', 'mvp_source'],
|
|
95
|
-
|
|
96
|
-
// Technical specialists
|
|
97
|
-
'vercel-expert': ['architecture', 'prd', 'plan'],
|
|
98
|
-
'architecture-expert': ['architecture', 'prd', 'plan', 'mvp_source'],
|
|
99
|
-
'ai-integration-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
|
|
100
|
-
'auth-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
|
|
101
|
-
'email-expert': ['seed', 'prd', 'mvp_source'],
|
|
102
|
-
'monitoring-expert': ['architecture', 'plan'],
|
|
103
|
-
'payment-expert': ['seed', 'business_plan', 'prd', 'mvp_source'],
|
|
104
|
-
'railway-expert': ['architecture', 'plan'],
|
|
105
|
-
|
|
106
|
-
// Product and content
|
|
107
|
-
'content-expert': ['seed', 'prd', 'plan'],
|
|
108
|
-
'product-expert': ['seed', 'prd', 'plan', 'roadmap'],
|
|
109
|
-
'mobile-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
|
|
110
|
-
'infrastructure-expert': ['architecture', 'plan'],
|
|
111
|
-
|
|
112
|
-
// Business specialists
|
|
113
|
-
'partnerships-expert': ['business_plan', 'competitive', 'plan'],
|
|
114
|
-
'private-equity-expert': ['pitch_deck', 'financial', 'business_plan']
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get the bootspring directory path
|
|
119
|
-
* @param {string} projectRoot - Project root path
|
|
120
|
-
* @returns {string} Bootspring directory path
|
|
121
|
-
*/
|
|
122
|
-
function getBootspringDir(projectRoot) {
|
|
123
|
-
return path.join(projectRoot, '.bootspring');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Ensure bootspring directories exist
|
|
128
|
-
* @param {string} projectRoot - Project root path
|
|
129
|
-
*/
|
|
130
|
-
function ensureDirectories(projectRoot) {
|
|
131
|
-
const bootspringDir = getBootspringDir(projectRoot);
|
|
132
|
-
const dirs = [
|
|
133
|
-
bootspringDir,
|
|
134
|
-
path.join(bootspringDir, 'logs'),
|
|
135
|
-
path.join(bootspringDir, 'logs', 'agents'),
|
|
136
|
-
path.join(bootspringDir, 'logs', 'code'),
|
|
137
|
-
path.join(bootspringDir, 'logs', 'decisions'),
|
|
138
|
-
path.join(bootspringDir, 'logs', 'fixes'),
|
|
139
|
-
path.join(bootspringDir, 'logs', 'business'),
|
|
140
|
-
path.join(bootspringDir, 'logs', 'meetings'),
|
|
141
|
-
path.join(bootspringDir, 'mvp'),
|
|
142
|
-
path.join(bootspringDir, 'mvp', 'source'),
|
|
143
|
-
path.join(bootspringDir, 'mvp', 'references')
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
for (const dir of dirs) {
|
|
147
|
-
utils.ensureDir(dir);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Load a specific context file
|
|
153
|
-
* @param {string} contextType - Type of context (seed, plan, prd, etc.)
|
|
154
|
-
* @param {object} [options] - Options
|
|
155
|
-
* @returns {object} Context data { exists, path, content, metadata }
|
|
156
|
-
*/
|
|
157
|
-
function load(contextType, options = {}) {
|
|
158
|
-
const cfg = options.config || config.load();
|
|
159
|
-
const projectRoot = cfg._projectRoot;
|
|
160
|
-
|
|
161
|
-
const relativePath = CONTEXT_LOCATIONS[contextType];
|
|
162
|
-
if (!relativePath) {
|
|
163
|
-
return { exists: false, error: `Unknown context type: ${contextType}` };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const fullPath = path.join(projectRoot, relativePath);
|
|
167
|
-
|
|
168
|
-
// Check if it's a directory (like mvp_source)
|
|
169
|
-
if (relativePath.endsWith('/')) {
|
|
170
|
-
return loadDirectory(fullPath, contextType);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Load single file
|
|
174
|
-
if (!utils.fileExists(fullPath)) {
|
|
175
|
-
return {
|
|
176
|
-
exists: false,
|
|
177
|
-
type: contextType,
|
|
178
|
-
path: relativePath,
|
|
179
|
-
content: null
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const content = utils.readFile(fullPath);
|
|
184
|
-
const stats = fs.statSync(fullPath);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
exists: true,
|
|
188
|
-
type: contextType,
|
|
189
|
-
path: relativePath,
|
|
190
|
-
fullPath: fullPath,
|
|
191
|
-
content: content,
|
|
192
|
-
metadata: {
|
|
193
|
-
size: stats.size,
|
|
194
|
-
modified: stats.mtime,
|
|
195
|
-
modifiedRelative: utils.formatRelativeTime(stats.mtime)
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Load all files from a directory
|
|
202
|
-
* @param {string} dirPath - Directory path
|
|
203
|
-
* @param {string} contextType - Context type
|
|
204
|
-
* @returns {object} Directory context data
|
|
205
|
-
*/
|
|
206
|
-
function loadDirectory(dirPath, contextType) {
|
|
207
|
-
if (!utils.fileExists(dirPath)) {
|
|
208
|
-
return {
|
|
209
|
-
exists: false,
|
|
210
|
-
type: contextType,
|
|
211
|
-
path: dirPath,
|
|
212
|
-
files: []
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const files = [];
|
|
217
|
-
|
|
218
|
-
function walkDir(dir, relativeTo) {
|
|
219
|
-
try {
|
|
220
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
221
|
-
|
|
222
|
-
for (const entry of entries) {
|
|
223
|
-
const fullPath = path.join(dir, entry.name);
|
|
224
|
-
const relativePath = path.relative(relativeTo, fullPath);
|
|
225
|
-
|
|
226
|
-
// Skip hidden files and common excludes
|
|
227
|
-
if (entry.name.startsWith('.') ||
|
|
228
|
-
entry.name === 'node_modules' ||
|
|
229
|
-
entry.name === 'dist' ||
|
|
230
|
-
entry.name === 'build') {
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (entry.isDirectory()) {
|
|
235
|
-
walkDir(fullPath, relativeTo);
|
|
236
|
-
} else if (entry.isFile()) {
|
|
237
|
-
const stats = fs.statSync(fullPath);
|
|
238
|
-
files.push({
|
|
239
|
-
name: entry.name,
|
|
240
|
-
path: relativePath,
|
|
241
|
-
fullPath: fullPath,
|
|
242
|
-
size: stats.size,
|
|
243
|
-
modified: stats.mtime,
|
|
244
|
-
extension: path.extname(entry.name)
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
} catch (err) {
|
|
249
|
-
// Directory read error, skip
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
walkDir(dirPath, dirPath);
|
|
254
|
-
|
|
255
|
-
return {
|
|
256
|
-
exists: true,
|
|
257
|
-
type: contextType,
|
|
258
|
-
path: dirPath,
|
|
259
|
-
files: files,
|
|
260
|
-
fileCount: files.length
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Load a specific file from a directory context
|
|
266
|
-
* @param {string} contextType - Context type (e.g., 'mvp_source')
|
|
267
|
-
* @param {string} filePath - Relative file path within the context
|
|
268
|
-
* @param {object} [options] - Options
|
|
269
|
-
* @returns {object} File context data
|
|
270
|
-
*/
|
|
271
|
-
function loadFile(contextType, filePath, options = {}) {
|
|
272
|
-
const cfg = options.config || config.load();
|
|
273
|
-
const projectRoot = cfg._projectRoot;
|
|
274
|
-
|
|
275
|
-
const basePath = CONTEXT_LOCATIONS[contextType];
|
|
276
|
-
if (!basePath) {
|
|
277
|
-
return { exists: false, error: `Unknown context type: ${contextType}` };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const fullPath = path.join(projectRoot, basePath, filePath);
|
|
281
|
-
|
|
282
|
-
if (!utils.fileExists(fullPath)) {
|
|
283
|
-
return {
|
|
284
|
-
exists: false,
|
|
285
|
-
type: contextType,
|
|
286
|
-
path: filePath,
|
|
287
|
-
content: null
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const content = utils.readFile(fullPath);
|
|
292
|
-
const stats = fs.statSync(fullPath);
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
exists: true,
|
|
296
|
-
type: contextType,
|
|
297
|
-
path: filePath,
|
|
298
|
-
fullPath: fullPath,
|
|
299
|
-
content: content,
|
|
300
|
-
metadata: {
|
|
301
|
-
size: stats.size,
|
|
302
|
-
modified: stats.mtime,
|
|
303
|
-
extension: path.extname(fullPath)
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Get all context for a specific agent
|
|
310
|
-
* @param {string} agentName - Agent name
|
|
311
|
-
* @param {object} [options] - Options
|
|
312
|
-
* @returns {object} Agent context data
|
|
313
|
-
*/
|
|
314
|
-
function getAgentContext(agentName, options = {}) {
|
|
315
|
-
const contextTypes = AGENT_CONTEXT_MAP[agentName] || [];
|
|
316
|
-
const context = {
|
|
317
|
-
agent: agentName,
|
|
318
|
-
files: [],
|
|
319
|
-
summary: []
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
for (const contextType of contextTypes) {
|
|
323
|
-
const loaded = load(contextType, options);
|
|
324
|
-
if (loaded.exists) {
|
|
325
|
-
if (loaded.files) {
|
|
326
|
-
// Directory context
|
|
327
|
-
context.files.push({
|
|
328
|
-
type: contextType,
|
|
329
|
-
isDirectory: true,
|
|
330
|
-
fileCount: loaded.fileCount,
|
|
331
|
-
files: loaded.files.slice(0, 10) // Limit to first 10 files
|
|
332
|
-
});
|
|
333
|
-
context.summary.push(`${contextType}: ${loaded.fileCount} files`);
|
|
334
|
-
} else {
|
|
335
|
-
// Single file context
|
|
336
|
-
context.files.push({
|
|
337
|
-
type: contextType,
|
|
338
|
-
isDirectory: false,
|
|
339
|
-
path: loaded.path,
|
|
340
|
-
content: loaded.content,
|
|
341
|
-
metadata: loaded.metadata
|
|
342
|
-
});
|
|
343
|
-
context.summary.push(`${contextType}: ${loaded.path}`);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return context;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* List all available context types and their status
|
|
353
|
-
* @param {object} [options] - Options
|
|
354
|
-
* @returns {object[]} List of context statuses
|
|
355
|
-
*/
|
|
356
|
-
function list(options = {}) {
|
|
357
|
-
const cfg = options.config || config.load();
|
|
358
|
-
const projectRoot = cfg._projectRoot;
|
|
359
|
-
const results = [];
|
|
360
|
-
|
|
361
|
-
for (const [type, relativePath] of Object.entries(CONTEXT_LOCATIONS)) {
|
|
362
|
-
const fullPath = path.join(projectRoot, relativePath);
|
|
363
|
-
const exists = utils.fileExists(fullPath);
|
|
364
|
-
|
|
365
|
-
const result = {
|
|
366
|
-
type,
|
|
367
|
-
path: relativePath,
|
|
368
|
-
exists
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
if (exists) {
|
|
372
|
-
if (relativePath.endsWith('/')) {
|
|
373
|
-
// Directory
|
|
374
|
-
const dirContext = loadDirectory(fullPath, type);
|
|
375
|
-
result.isDirectory = true;
|
|
376
|
-
result.fileCount = dirContext.fileCount;
|
|
377
|
-
} else {
|
|
378
|
-
// File
|
|
379
|
-
const stats = fs.statSync(fullPath);
|
|
380
|
-
result.isDirectory = false;
|
|
381
|
-
result.size = stats.size;
|
|
382
|
-
result.modified = stats.mtime;
|
|
383
|
-
result.modifiedRelative = utils.formatRelativeTime(stats.mtime);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
results.push(result);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return results;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Search across all context files
|
|
395
|
-
* @param {string} query - Search query
|
|
396
|
-
* @param {object} [options] - Options { types, limit }
|
|
397
|
-
* @returns {object[]} Search results
|
|
398
|
-
*/
|
|
399
|
-
function search(query, options = {}) {
|
|
400
|
-
const cfg = options.config || config.load();
|
|
401
|
-
const projectRoot = cfg._projectRoot;
|
|
402
|
-
const types = options.types || Object.keys(CONTEXT_LOCATIONS);
|
|
403
|
-
const limit = options.limit || 20;
|
|
404
|
-
const results = [];
|
|
405
|
-
const queryLower = query.toLowerCase();
|
|
406
|
-
|
|
407
|
-
for (const type of types) {
|
|
408
|
-
if (!CONTEXT_LOCATIONS[type]) continue;
|
|
409
|
-
|
|
410
|
-
const loaded = load(type, { config: cfg });
|
|
411
|
-
if (!loaded.exists) continue;
|
|
412
|
-
|
|
413
|
-
if (loaded.files) {
|
|
414
|
-
// Directory - search file names and contents
|
|
415
|
-
for (const file of loaded.files) {
|
|
416
|
-
// Search in filename
|
|
417
|
-
if (file.name.toLowerCase().includes(queryLower)) {
|
|
418
|
-
results.push({
|
|
419
|
-
type,
|
|
420
|
-
file: file.path,
|
|
421
|
-
matchType: 'filename',
|
|
422
|
-
preview: file.name
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Search in content for text files
|
|
427
|
-
if (['.md', '.txt', '.js', '.ts', '.json', '.yaml', '.yml'].includes(file.extension)) {
|
|
428
|
-
const content = utils.readFile(file.fullPath);
|
|
429
|
-
if (content && content.toLowerCase().includes(queryLower)) {
|
|
430
|
-
const lines = content.split('\n');
|
|
431
|
-
const matchingLines = lines
|
|
432
|
-
.map((line, i) => ({ line, num: i + 1 }))
|
|
433
|
-
.filter(l => l.line.toLowerCase().includes(queryLower))
|
|
434
|
-
.slice(0, 3);
|
|
435
|
-
|
|
436
|
-
results.push({
|
|
437
|
-
type,
|
|
438
|
-
file: file.path,
|
|
439
|
-
matchType: 'content',
|
|
440
|
-
matches: matchingLines.map(m => ({
|
|
441
|
-
line: m.num,
|
|
442
|
-
preview: utils.truncate(m.line.trim(), 100)
|
|
443
|
-
}))
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if (results.length >= limit) break;
|
|
449
|
-
}
|
|
450
|
-
} else if (loaded.content) {
|
|
451
|
-
// Single file - search content
|
|
452
|
-
const content = loaded.content;
|
|
453
|
-
if (content.toLowerCase().includes(queryLower)) {
|
|
454
|
-
const lines = content.split('\n');
|
|
455
|
-
const matchingLines = lines
|
|
456
|
-
.map((line, i) => ({ line, num: i + 1 }))
|
|
457
|
-
.filter(l => l.line.toLowerCase().includes(queryLower))
|
|
458
|
-
.slice(0, 5);
|
|
459
|
-
|
|
460
|
-
results.push({
|
|
461
|
-
type,
|
|
462
|
-
file: loaded.path,
|
|
463
|
-
matchType: 'content',
|
|
464
|
-
matches: matchingLines.map(m => ({
|
|
465
|
-
line: m.num,
|
|
466
|
-
preview: utils.truncate(m.line.trim(), 100)
|
|
467
|
-
}))
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (results.length >= limit) break;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return results.slice(0, limit);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Build or update the context index
|
|
480
|
-
* @param {object} [options] - Options
|
|
481
|
-
* @returns {object} Index data
|
|
482
|
-
*/
|
|
483
|
-
function buildIndex(options = {}) {
|
|
484
|
-
const cfg = options.config || config.load();
|
|
485
|
-
const projectRoot = cfg._projectRoot;
|
|
486
|
-
|
|
487
|
-
ensureDirectories(projectRoot);
|
|
488
|
-
|
|
489
|
-
const index = {
|
|
490
|
-
version: '1.0',
|
|
491
|
-
lastUpdated: new Date().toISOString(),
|
|
492
|
-
projectRoot: projectRoot,
|
|
493
|
-
files: [],
|
|
494
|
-
agents: {}
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
// Index all context files
|
|
498
|
-
const contextList = list({ config: cfg });
|
|
499
|
-
for (const ctx of contextList) {
|
|
500
|
-
if (ctx.exists) {
|
|
501
|
-
index.files.push({
|
|
502
|
-
type: ctx.type,
|
|
503
|
-
path: ctx.path,
|
|
504
|
-
isDirectory: ctx.isDirectory,
|
|
505
|
-
fileCount: ctx.fileCount,
|
|
506
|
-
size: ctx.size,
|
|
507
|
-
modified: ctx.modified
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Build agent context mappings
|
|
513
|
-
for (const [agent, types] of Object.entries(AGENT_CONTEXT_MAP)) {
|
|
514
|
-
const availableTypes = types.filter(t => {
|
|
515
|
-
const ctx = contextList.find(c => c.type === t);
|
|
516
|
-
return ctx && ctx.exists;
|
|
517
|
-
});
|
|
518
|
-
index.agents[agent] = availableTypes;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Save index
|
|
522
|
-
const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index);
|
|
523
|
-
utils.writeFile(indexPath, JSON.stringify(index, null, 2));
|
|
524
|
-
|
|
525
|
-
return index;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Load the context index
|
|
530
|
-
* @param {object} [options] - Options
|
|
531
|
-
* @returns {object|null} Index data or null
|
|
532
|
-
*/
|
|
533
|
-
function loadIndex(options = {}) {
|
|
534
|
-
const cfg = options.config || config.load();
|
|
535
|
-
const projectRoot = cfg._projectRoot;
|
|
536
|
-
const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index);
|
|
537
|
-
|
|
538
|
-
if (!utils.fileExists(indexPath)) {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
try {
|
|
543
|
-
const content = utils.readFile(indexPath);
|
|
544
|
-
return JSON.parse(content);
|
|
545
|
-
} catch {
|
|
546
|
-
return null;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Get context locations configuration
|
|
552
|
-
* @returns {object} Context locations
|
|
553
|
-
*/
|
|
554
|
-
function getLocations() {
|
|
555
|
-
return { ...CONTEXT_LOCATIONS };
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Get agent context mapping
|
|
560
|
-
* @returns {object} Agent context map
|
|
561
|
-
*/
|
|
562
|
-
function getAgentMap() {
|
|
563
|
-
return { ...AGENT_CONTEXT_MAP };
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
module.exports = {
|
|
567
|
-
load,
|
|
568
|
-
loadFile,
|
|
569
|
-
loadDirectory,
|
|
570
|
-
getAgentContext,
|
|
571
|
-
list,
|
|
572
|
-
search,
|
|
573
|
-
buildIndex,
|
|
574
|
-
loadIndex,
|
|
575
|
-
ensureDirectories,
|
|
576
|
-
getLocations,
|
|
577
|
-
getAgentMap,
|
|
578
|
-
CONTEXT_LOCATIONS,
|
|
579
|
-
AGENT_CONTEXT_MAP
|
|
580
|
-
};
|
package/core/context.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Context Types
|
|
3
|
-
* @module core/context
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface ProjectContext {
|
|
7
|
-
project: {
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
version: string;
|
|
11
|
-
};
|
|
12
|
-
stack: {
|
|
13
|
-
framework: string;
|
|
14
|
-
language: string;
|
|
15
|
-
database: string;
|
|
16
|
-
hosting: string;
|
|
17
|
-
};
|
|
18
|
-
paths: {
|
|
19
|
-
root: string;
|
|
20
|
-
config: string;
|
|
21
|
-
context: string;
|
|
22
|
-
};
|
|
23
|
-
plugins: Record<string, { enabled: boolean; provider?: string }>;
|
|
24
|
-
agents: string[];
|
|
25
|
-
skills: string[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ValidationResult {
|
|
29
|
-
valid: boolean;
|
|
30
|
-
errors: string[];
|
|
31
|
-
warnings: string[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get project context
|
|
36
|
-
* @param projectRoot - Optional project root path
|
|
37
|
-
* @returns Project context object
|
|
38
|
-
*/
|
|
39
|
-
export function get(projectRoot?: string): ProjectContext;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Validate project context
|
|
43
|
-
* @param context - Context to validate
|
|
44
|
-
* @returns Validation result
|
|
45
|
-
*/
|
|
46
|
-
export function validate(context: unknown): ValidationResult;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Load context from CLAUDE.md file
|
|
50
|
-
* @param filePath - Path to CLAUDE.md
|
|
51
|
-
* @returns Parsed context or null
|
|
52
|
-
*/
|
|
53
|
-
export function load(filePath: string): ProjectContext | null;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Save context to CLAUDE.md file
|
|
57
|
-
* @param context - Context to save
|
|
58
|
-
* @param filePath - Path to save to
|
|
59
|
-
* @returns True if saved successfully
|
|
60
|
-
*/
|
|
61
|
-
export function save(context: ProjectContext, filePath: string): boolean;
|