@girardmedia/bootspring 2.0.21 → 2.0.23

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.
Files changed (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
@@ -0,0 +1,694 @@
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
+ import * as fs from 'fs';
19
+ import * as path from 'path';
20
+
21
+ // Module interfaces
22
+ interface ConfigModule {
23
+ load: () => Config | null;
24
+ }
25
+
26
+ interface Config {
27
+ _projectRoot: string;
28
+ }
29
+
30
+ interface Utils {
31
+ fileExists: (path: string) => boolean;
32
+ readFile: (path: string) => string | null;
33
+ ensureDir: (dir: string) => void;
34
+ truncate: (text: string, maxLength: number) => string;
35
+ writeFile: (path: string, content: string) => boolean;
36
+ formatRelativeTime: (date: Date) => string;
37
+ }
38
+
39
+ // Lazy-loaded modules
40
+ let _config: ConfigModule | null = null;
41
+ let _utils: Utils | null = null;
42
+
43
+ function getConfig(): ConfigModule {
44
+ if (!_config) {
45
+ _config = require('./config') as ConfigModule;
46
+ }
47
+ return _config;
48
+ }
49
+
50
+ function getUtils(): Utils {
51
+ if (!_utils) {
52
+ _utils = require('./utils') as Utils;
53
+ }
54
+ return _utils;
55
+ }
56
+
57
+ // Type definitions
58
+ export interface ContextFileMetadata {
59
+ size: number;
60
+ modified: Date;
61
+ modifiedRelative: string;
62
+ extension?: string | undefined;
63
+ }
64
+
65
+ export interface SingleFileContext {
66
+ exists: boolean;
67
+ type: string;
68
+ path: string;
69
+ fullPath?: string | undefined;
70
+ content: string | null;
71
+ metadata?: ContextFileMetadata | undefined;
72
+ error?: string | undefined;
73
+ }
74
+
75
+ export interface DirectoryFile {
76
+ name: string;
77
+ path: string;
78
+ fullPath: string;
79
+ size: number;
80
+ modified: Date;
81
+ extension: string;
82
+ }
83
+
84
+ export interface DirectoryContext {
85
+ exists: boolean;
86
+ type: string;
87
+ path: string;
88
+ files: DirectoryFile[];
89
+ fileCount?: number | undefined;
90
+ }
91
+
92
+ export interface AgentContextFile {
93
+ type: string;
94
+ isDirectory: boolean;
95
+ path?: string | undefined;
96
+ content?: string | undefined;
97
+ metadata?: ContextFileMetadata | undefined;
98
+ fileCount?: number | undefined;
99
+ files?: DirectoryFile[] | undefined;
100
+ }
101
+
102
+ export interface AgentContext {
103
+ agent: string;
104
+ files: AgentContextFile[];
105
+ summary: string[];
106
+ }
107
+
108
+ export interface ContextStatus {
109
+ type: string;
110
+ path: string;
111
+ exists: boolean;
112
+ isDirectory?: boolean | undefined;
113
+ fileCount?: number | undefined;
114
+ size?: number | undefined;
115
+ modified?: Date | undefined;
116
+ modifiedRelative?: string | undefined;
117
+ }
118
+
119
+ export interface SearchMatch {
120
+ line: number;
121
+ preview: string;
122
+ }
123
+
124
+ export interface SearchResult {
125
+ type: string;
126
+ file: string;
127
+ matchType: 'filename' | 'content';
128
+ preview?: string | undefined;
129
+ matches?: SearchMatch[] | undefined;
130
+ }
131
+
132
+ export interface IndexFile {
133
+ type: string;
134
+ path: string;
135
+ isDirectory: boolean;
136
+ fileCount?: number | undefined;
137
+ size?: number | undefined;
138
+ modified?: Date | undefined;
139
+ }
140
+
141
+ export interface ContextIndex {
142
+ version: string;
143
+ lastUpdated: string;
144
+ projectRoot: string;
145
+ files: IndexFile[];
146
+ agents: Record<string, string[]>;
147
+ }
148
+
149
+ export interface LoadOptions {
150
+ config?: Config | undefined;
151
+ }
152
+
153
+ export interface SearchOptions {
154
+ config?: Config | undefined;
155
+ types?: string[] | undefined;
156
+ limit?: number | undefined;
157
+ }
158
+
159
+ /**
160
+ * Default context file locations
161
+ */
162
+ export const CONTEXT_LOCATIONS: Record<string, string> = {
163
+ // Root level files
164
+ seed: 'SEED.md',
165
+ claude: 'CLAUDE.md',
166
+ todo: 'todo.md',
167
+ readme: 'README.md',
168
+ changelog: 'CHANGELOG.md',
169
+
170
+ // Planning folder
171
+ plan: 'planning/MASTER_PLAN.md',
172
+ prd: 'planning/PRD.md',
173
+ architecture: 'planning/ARCHITECTURE.md',
174
+ decisions: 'planning/DECISION_LOG.md',
175
+ roadmap: 'planning/ROADMAP.md',
176
+
177
+ // Business documents
178
+ business_plan: 'planning/BUSINESS_PLAN.md',
179
+ pitch_deck: 'planning/PITCH_DECK.md',
180
+ competitive: 'planning/COMPETITIVE_ANALYSIS.md',
181
+ market: 'planning/MARKET_RESEARCH.md',
182
+ financial: 'planning/FINANCIAL_MODEL.md',
183
+
184
+ // Legal documents
185
+ terms: 'legal/TERMS_OF_SERVICE.md',
186
+ privacy: 'legal/PRIVACY_POLICY.md',
187
+ legal_checklist: 'legal/LEGAL_CHECKLIST.md',
188
+
189
+ // MVP and code context
190
+ mvp_source: '.bootspring/mvp/source/',
191
+ mvp_references: '.bootspring/mvp/references/',
192
+
193
+ // Logs
194
+ logs: '.bootspring/logs/',
195
+ context_index: '.bootspring/context-index.json'
196
+ };
197
+
198
+ /**
199
+ * Agent to context mapping
200
+ */
201
+ export const AGENT_CONTEXT_MAP: Record<string, string[]> = {
202
+ // Technical agents
203
+ 'frontend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
204
+ 'backend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
205
+ 'database-expert': ['seed', 'architecture', 'mvp_source'],
206
+ 'api-expert': ['seed', 'architecture', 'prd'],
207
+ 'security-expert': ['architecture', 'legal_checklist', 'prd'],
208
+ 'testing-expert': ['architecture', 'prd'],
209
+ 'performance-expert': ['architecture', 'mvp_source'],
210
+ 'devops-expert': ['architecture', 'plan'],
211
+ 'ui-ux-expert': ['seed', 'prd', 'mvp_source'],
212
+ 'code-review-expert': ['architecture', 'mvp_source'],
213
+
214
+ // Business agents
215
+ 'business-strategy-expert': ['seed', 'business_plan', 'competitive', 'plan'],
216
+ 'financial-expert': ['seed', 'business_plan', 'financial'],
217
+ 'fundraising-expert': ['pitch_deck', 'financial', 'business_plan'],
218
+ 'legal-expert': ['terms', 'privacy', 'legal_checklist'],
219
+ 'marketing-expert': ['business_plan', 'competitive', 'plan'],
220
+ 'sales-expert': ['business_plan', 'competitive', 'prd'],
221
+ 'growth-expert': ['plan', 'competitive', 'market'],
222
+ 'operations-expert': ['plan', 'architecture'],
223
+ 'competitive-analysis-expert': ['competitive', 'market', 'business_plan'],
224
+ 'market-research-expert': ['market', 'competitive', 'business_plan'],
225
+ 'investor-relations-expert': ['pitch_deck', 'financial', 'plan'],
226
+
227
+ // Research agents
228
+ 'research-expert': ['plan', 'architecture', 'prd'],
229
+ 'data-modeling-expert': ['architecture', 'prd', 'mvp_source'],
230
+
231
+ // Technical specialists
232
+ 'vercel-expert': ['architecture', 'prd', 'plan'],
233
+ 'architecture-expert': ['architecture', 'prd', 'plan', 'mvp_source'],
234
+ 'ai-integration-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
235
+ 'auth-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
236
+ 'email-expert': ['seed', 'prd', 'mvp_source'],
237
+ 'monitoring-expert': ['architecture', 'plan'],
238
+ 'payment-expert': ['seed', 'business_plan', 'prd', 'mvp_source'],
239
+ 'railway-expert': ['architecture', 'plan'],
240
+
241
+ // Product and content
242
+ 'content-expert': ['seed', 'prd', 'plan'],
243
+ 'product-expert': ['seed', 'prd', 'plan', 'roadmap'],
244
+ 'mobile-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
245
+ 'infrastructure-expert': ['architecture', 'plan'],
246
+
247
+ // Business specialists
248
+ 'partnerships-expert': ['business_plan', 'competitive', 'plan'],
249
+ 'private-equity-expert': ['pitch_deck', 'financial', 'business_plan']
250
+ };
251
+
252
+ /**
253
+ * Get the bootspring directory path
254
+ */
255
+ export function getBootspringDir(projectRoot: string): string {
256
+ return path.join(projectRoot, '.bootspring');
257
+ }
258
+
259
+ /**
260
+ * Ensure bootspring directories exist
261
+ */
262
+ export function ensureDirectories(projectRoot: string): void {
263
+ const utils = getUtils();
264
+ const bootspringDir = getBootspringDir(projectRoot);
265
+ const dirs = [
266
+ bootspringDir,
267
+ path.join(bootspringDir, 'logs'),
268
+ path.join(bootspringDir, 'logs', 'agents'),
269
+ path.join(bootspringDir, 'logs', 'code'),
270
+ path.join(bootspringDir, 'logs', 'decisions'),
271
+ path.join(bootspringDir, 'logs', 'fixes'),
272
+ path.join(bootspringDir, 'logs', 'business'),
273
+ path.join(bootspringDir, 'logs', 'meetings'),
274
+ path.join(bootspringDir, 'mvp'),
275
+ path.join(bootspringDir, 'mvp', 'source'),
276
+ path.join(bootspringDir, 'mvp', 'references')
277
+ ];
278
+
279
+ for (const dir of dirs) {
280
+ utils.ensureDir(dir);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Load a specific context file
286
+ */
287
+ export function load(contextType: string, options: LoadOptions = {}): SingleFileContext | DirectoryContext {
288
+ const config = getConfig();
289
+ const utils = getUtils();
290
+
291
+ const cfg = options.config || config.load();
292
+ const projectRoot = cfg?._projectRoot || process.cwd();
293
+
294
+ const relativePath = CONTEXT_LOCATIONS[contextType];
295
+ if (!relativePath) {
296
+ return { exists: false, type: contextType, path: '', content: null, error: `Unknown context type: ${contextType}` };
297
+ }
298
+
299
+ const fullPath = path.join(projectRoot, relativePath);
300
+
301
+ // Check if it's a directory (like mvp_source)
302
+ if (relativePath.endsWith('/')) {
303
+ return loadDirectory(fullPath, contextType);
304
+ }
305
+
306
+ // Load single file
307
+ if (!utils.fileExists(fullPath)) {
308
+ return {
309
+ exists: false,
310
+ type: contextType,
311
+ path: relativePath,
312
+ content: null
313
+ };
314
+ }
315
+
316
+ const content = utils.readFile(fullPath);
317
+ const stats = fs.statSync(fullPath);
318
+
319
+ return {
320
+ exists: true,
321
+ type: contextType,
322
+ path: relativePath,
323
+ fullPath: fullPath,
324
+ content: content,
325
+ metadata: {
326
+ size: stats.size,
327
+ modified: stats.mtime,
328
+ modifiedRelative: utils.formatRelativeTime(stats.mtime)
329
+ }
330
+ };
331
+ }
332
+
333
+ /**
334
+ * Load all files from a directory
335
+ */
336
+ export function loadDirectory(dirPath: string, contextType: string): DirectoryContext {
337
+ const utils = getUtils();
338
+
339
+ if (!utils.fileExists(dirPath)) {
340
+ return {
341
+ exists: false,
342
+ type: contextType,
343
+ path: dirPath,
344
+ files: []
345
+ };
346
+ }
347
+
348
+ const files: DirectoryFile[] = [];
349
+
350
+ function walkDir(dir: string, relativeTo: string): void {
351
+ try {
352
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
353
+
354
+ for (const entry of entries) {
355
+ const fullPath = path.join(dir, entry.name);
356
+ const relativePath = path.relative(relativeTo, fullPath);
357
+
358
+ // Skip hidden files and common excludes
359
+ if (entry.name.startsWith('.') ||
360
+ entry.name === 'node_modules' ||
361
+ entry.name === 'dist' ||
362
+ entry.name === 'build') {
363
+ continue;
364
+ }
365
+
366
+ if (entry.isDirectory()) {
367
+ walkDir(fullPath, relativeTo);
368
+ } else if (entry.isFile()) {
369
+ const stats = fs.statSync(fullPath);
370
+ files.push({
371
+ name: entry.name,
372
+ path: relativePath,
373
+ fullPath: fullPath,
374
+ size: stats.size,
375
+ modified: stats.mtime,
376
+ extension: path.extname(entry.name)
377
+ });
378
+ }
379
+ }
380
+ } catch {
381
+ // Directory read error, skip
382
+ }
383
+ }
384
+
385
+ walkDir(dirPath, dirPath);
386
+
387
+ return {
388
+ exists: true,
389
+ type: contextType,
390
+ path: dirPath,
391
+ files: files,
392
+ fileCount: files.length
393
+ };
394
+ }
395
+
396
+ /**
397
+ * Load a specific file from a directory context
398
+ */
399
+ export function loadFile(contextType: string, filePath: string, options: LoadOptions = {}): SingleFileContext {
400
+ const config = getConfig();
401
+ const utils = getUtils();
402
+
403
+ const cfg = options.config || config.load();
404
+ const projectRoot = cfg?._projectRoot || process.cwd();
405
+
406
+ const basePath = CONTEXT_LOCATIONS[contextType];
407
+ if (!basePath) {
408
+ return { exists: false, type: contextType, path: filePath, content: null, error: `Unknown context type: ${contextType}` };
409
+ }
410
+
411
+ const fullPath = path.join(projectRoot, basePath, filePath);
412
+
413
+ if (!utils.fileExists(fullPath)) {
414
+ return {
415
+ exists: false,
416
+ type: contextType,
417
+ path: filePath,
418
+ content: null
419
+ };
420
+ }
421
+
422
+ const content = utils.readFile(fullPath);
423
+ const stats = fs.statSync(fullPath);
424
+
425
+ return {
426
+ exists: true,
427
+ type: contextType,
428
+ path: filePath,
429
+ fullPath: fullPath,
430
+ content: content,
431
+ metadata: {
432
+ size: stats.size,
433
+ modified: stats.mtime,
434
+ modifiedRelative: utils.formatRelativeTime(stats.mtime),
435
+ extension: path.extname(fullPath)
436
+ }
437
+ };
438
+ }
439
+
440
+ /**
441
+ * Get all context for a specific agent
442
+ */
443
+ export function getAgentContext(agentName: string, options: LoadOptions = {}): AgentContext {
444
+ const contextTypes = AGENT_CONTEXT_MAP[agentName] || [];
445
+ const context: AgentContext = {
446
+ agent: agentName,
447
+ files: [],
448
+ summary: []
449
+ };
450
+
451
+ for (const contextType of contextTypes) {
452
+ const loaded = load(contextType, options);
453
+ if (loaded.exists) {
454
+ if ('files' in loaded && loaded.files) {
455
+ // Directory context
456
+ context.files.push({
457
+ type: contextType,
458
+ isDirectory: true,
459
+ fileCount: loaded.fileCount,
460
+ files: loaded.files.slice(0, 10) // Limit to first 10 files
461
+ });
462
+ context.summary.push(`${contextType}: ${loaded.fileCount} files`);
463
+ } else if ('content' in loaded) {
464
+ // Single file context
465
+ context.files.push({
466
+ type: contextType,
467
+ isDirectory: false,
468
+ path: loaded.path,
469
+ content: loaded.content || undefined,
470
+ metadata: loaded.metadata
471
+ });
472
+ context.summary.push(`${contextType}: ${loaded.path}`);
473
+ }
474
+ }
475
+ }
476
+
477
+ return context;
478
+ }
479
+
480
+ /**
481
+ * List all available context types and their status
482
+ */
483
+ export function list(options: LoadOptions = {}): ContextStatus[] {
484
+ const config = getConfig();
485
+ const utils = getUtils();
486
+
487
+ const cfg = options.config || config.load();
488
+ const projectRoot = cfg?._projectRoot || process.cwd();
489
+ const results: ContextStatus[] = [];
490
+
491
+ for (const [type, relativePath] of Object.entries(CONTEXT_LOCATIONS)) {
492
+ const fullPath = path.join(projectRoot, relativePath);
493
+ const exists = utils.fileExists(fullPath);
494
+
495
+ const result: ContextStatus = {
496
+ type,
497
+ path: relativePath,
498
+ exists
499
+ };
500
+
501
+ if (exists) {
502
+ if (relativePath.endsWith('/')) {
503
+ // Directory
504
+ const dirContext = loadDirectory(fullPath, type);
505
+ result.isDirectory = true;
506
+ result.fileCount = dirContext.fileCount;
507
+ } else {
508
+ // File
509
+ const stats = fs.statSync(fullPath);
510
+ result.isDirectory = false;
511
+ result.size = stats.size;
512
+ result.modified = stats.mtime;
513
+ result.modifiedRelative = utils.formatRelativeTime(stats.mtime);
514
+ }
515
+ }
516
+
517
+ results.push(result);
518
+ }
519
+
520
+ return results;
521
+ }
522
+
523
+ /**
524
+ * Search across all context files
525
+ */
526
+ export function search(query: string, options: SearchOptions = {}): SearchResult[] {
527
+ const config = getConfig();
528
+ const utils = getUtils();
529
+
530
+ const cfg = options.config || config.load();
531
+ const types = options.types || Object.keys(CONTEXT_LOCATIONS);
532
+ const limit = options.limit || 20;
533
+ const results: SearchResult[] = [];
534
+ const queryLower = query.toLowerCase();
535
+
536
+ for (const type of types) {
537
+ if (!CONTEXT_LOCATIONS[type]) continue;
538
+
539
+ const loaded = load(type, { config: cfg || undefined });
540
+ if (!loaded.exists) continue;
541
+
542
+ if ('files' in loaded && loaded.files) {
543
+ // Directory - search file names and contents
544
+ for (const file of loaded.files) {
545
+ // Search in filename
546
+ if (file.name.toLowerCase().includes(queryLower)) {
547
+ results.push({
548
+ type,
549
+ file: file.path,
550
+ matchType: 'filename',
551
+ preview: file.name
552
+ });
553
+ }
554
+
555
+ // Search in content for text files
556
+ if (['.md', '.txt', '.js', '.ts', '.json', '.yaml', '.yml'].includes(file.extension)) {
557
+ const content = utils.readFile(file.fullPath);
558
+ if (content && content.toLowerCase().includes(queryLower)) {
559
+ const lines = content.split('\n');
560
+ const matchingLines = lines
561
+ .map((line, i) => ({ line, num: i + 1 }))
562
+ .filter(l => l.line.toLowerCase().includes(queryLower))
563
+ .slice(0, 3);
564
+
565
+ results.push({
566
+ type,
567
+ file: file.path,
568
+ matchType: 'content',
569
+ matches: matchingLines.map(m => ({
570
+ line: m.num,
571
+ preview: utils.truncate(m.line.trim(), 100)
572
+ }))
573
+ });
574
+ }
575
+ }
576
+
577
+ if (results.length >= limit) break;
578
+ }
579
+ } else if ('content' in loaded && loaded.content) {
580
+ // Single file - search content
581
+ const content = loaded.content;
582
+ if (content.toLowerCase().includes(queryLower)) {
583
+ const lines = content.split('\n');
584
+ const matchingLines = lines
585
+ .map((line, i) => ({ line, num: i + 1 }))
586
+ .filter(l => l.line.toLowerCase().includes(queryLower))
587
+ .slice(0, 5);
588
+
589
+ results.push({
590
+ type,
591
+ file: loaded.path,
592
+ matchType: 'content',
593
+ matches: matchingLines.map(m => ({
594
+ line: m.num,
595
+ preview: utils.truncate(m.line.trim(), 100)
596
+ }))
597
+ });
598
+ }
599
+ }
600
+
601
+ if (results.length >= limit) break;
602
+ }
603
+
604
+ return results.slice(0, limit);
605
+ }
606
+
607
+ /**
608
+ * Build or update the context index
609
+ */
610
+ export function buildIndex(options: LoadOptions = {}): ContextIndex {
611
+ const config = getConfig();
612
+ const utils = getUtils();
613
+
614
+ const cfg = options.config || config.load();
615
+ const projectRoot = cfg?._projectRoot || process.cwd();
616
+
617
+ ensureDirectories(projectRoot);
618
+
619
+ const index: ContextIndex = {
620
+ version: '1.0',
621
+ lastUpdated: new Date().toISOString(),
622
+ projectRoot: projectRoot,
623
+ files: [],
624
+ agents: {}
625
+ };
626
+
627
+ // Index all context files
628
+ const contextList = list({ config: cfg || undefined });
629
+ for (const ctx of contextList) {
630
+ if (ctx.exists) {
631
+ index.files.push({
632
+ type: ctx.type,
633
+ path: ctx.path,
634
+ isDirectory: ctx.isDirectory || false,
635
+ fileCount: ctx.fileCount,
636
+ size: ctx.size,
637
+ modified: ctx.modified
638
+ });
639
+ }
640
+ }
641
+
642
+ // Build agent context mappings
643
+ for (const [agent, types] of Object.entries(AGENT_CONTEXT_MAP)) {
644
+ const availableTypes = types.filter(t => {
645
+ const ctx = contextList.find(c => c.type === t);
646
+ return ctx && ctx.exists;
647
+ });
648
+ index.agents[agent] = availableTypes;
649
+ }
650
+
651
+ // Save index
652
+ const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index || '.bootspring/context-index.json');
653
+ utils.writeFile(indexPath, JSON.stringify(index, null, 2));
654
+
655
+ return index;
656
+ }
657
+
658
+ /**
659
+ * Load the context index
660
+ */
661
+ export function loadIndex(options: LoadOptions = {}): ContextIndex | null {
662
+ const config = getConfig();
663
+ const utils = getUtils();
664
+
665
+ const cfg = options.config || config.load();
666
+ const projectRoot = cfg?._projectRoot || process.cwd();
667
+ const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index || '.bootspring/context-index.json');
668
+
669
+ if (!utils.fileExists(indexPath)) {
670
+ return null;
671
+ }
672
+
673
+ try {
674
+ const content = utils.readFile(indexPath);
675
+ if (!content) return null;
676
+ return JSON.parse(content) as ContextIndex;
677
+ } catch {
678
+ return null;
679
+ }
680
+ }
681
+
682
+ /**
683
+ * Get context locations configuration
684
+ */
685
+ export function getLocations(): Record<string, string> {
686
+ return { ...CONTEXT_LOCATIONS };
687
+ }
688
+
689
+ /**
690
+ * Get agent context mapping
691
+ */
692
+ export function getAgentMap(): Record<string, string[]> {
693
+ return { ...AGENT_CONTEXT_MAP };
694
+ }