@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.
- package/bin/bootspring.js +5 -0
- package/cli/org.js +474 -0
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/preseed.js +9 -301
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/cli/seed.js +23 -1074
- package/core/api-client.js +77 -0
- package/core/entitlements.js +36 -0
- package/core/organizations.js +223 -0
- package/core/policies.js +51 -6
- package/core/policy-matrix.js +303 -0
- package/core/project-context.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +23 -0
- package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/intelligence/orchestrator.js +17 -512
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- 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
|
+
}
|