@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
package/src/cli/mcp.ts
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring MCP Command
|
|
3
|
+
* Control the MCP server
|
|
4
|
+
*
|
|
5
|
+
* @package bootspring
|
|
6
|
+
* @module cli/mcp
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
// Import JS modules with type interfaces
|
|
12
|
+
interface ConfigModule {
|
|
13
|
+
load(): {
|
|
14
|
+
_projectRoot: string;
|
|
15
|
+
_configPath?: string;
|
|
16
|
+
project?: { name: string };
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ContextModule {
|
|
21
|
+
get(options: { config: unknown }): {
|
|
22
|
+
state: { phase: string; health: string };
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ContextDetectorModule {
|
|
27
|
+
detectContext(prompt: string): {
|
|
28
|
+
matches: Array<unknown>;
|
|
29
|
+
suggestedAgents: string[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PromptEnhancerModule {
|
|
34
|
+
enhance(prompt: string, detection: unknown, options: unknown): {
|
|
35
|
+
category: string;
|
|
36
|
+
} | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
|
+
const config = require('../../core/config') as ConfigModule;
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
|
+
const utils = require('../../core/utils') as typeof import('../core/utils');
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Start MCP server (for direct invocation by Claude Code)
|
|
46
|
+
*/
|
|
47
|
+
function startServer(): void {
|
|
48
|
+
// When called directly by MCP, just require the server module
|
|
49
|
+
// This allows Claude Code to invoke it via stdio
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
51
|
+
require('../../mcp/server.js');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Show MCP status and configuration info
|
|
56
|
+
*/
|
|
57
|
+
function showStatus(): void {
|
|
58
|
+
const cfg = config.load();
|
|
59
|
+
|
|
60
|
+
console.log(`
|
|
61
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP Server${utils.COLORS.reset}
|
|
62
|
+
${utils.COLORS.dim}Model Context Protocol integration${utils.COLORS.reset}
|
|
63
|
+
|
|
64
|
+
${utils.COLORS.bold}Status${utils.COLORS.reset}
|
|
65
|
+
The MCP server runs on-demand when Claude Code invokes it.
|
|
66
|
+
It does not run as a persistent background service.
|
|
67
|
+
|
|
68
|
+
${utils.COLORS.bold}Configuration${utils.COLORS.reset}
|
|
69
|
+
Project Root: ${cfg._projectRoot}
|
|
70
|
+
Config File: ${cfg._configPath || 'not found'}
|
|
71
|
+
|
|
72
|
+
${utils.COLORS.bold}Available Tools${utils.COLORS.reset}
|
|
73
|
+
${utils.COLORS.cyan}bootspring_context${utils.COLORS.reset} - Show/validate project context
|
|
74
|
+
${utils.COLORS.cyan}bootspring_agent${utils.COLORS.reset} - Work with specialized agents
|
|
75
|
+
${utils.COLORS.cyan}bootspring_todo${utils.COLORS.reset} - Manage todo items
|
|
76
|
+
${utils.COLORS.cyan}bootspring_skill${utils.COLORS.reset} - Search code patterns
|
|
77
|
+
${utils.COLORS.cyan}bootspring_quality${utils.COLORS.reset} - Run quality gates
|
|
78
|
+
${utils.COLORS.cyan}bootspring_generate${utils.COLORS.reset} - Regenerate context
|
|
79
|
+
${utils.COLORS.cyan}bootspring_plugin${utils.COLORS.reset} - Manage plugins
|
|
80
|
+
${utils.COLORS.cyan}bootspring_dashboard${utils.COLORS.reset} - Dashboard info
|
|
81
|
+
|
|
82
|
+
${utils.COLORS.bold}Resources${utils.COLORS.reset}
|
|
83
|
+
${utils.COLORS.dim}bootspring://context/project${utils.COLORS.reset} - Project context JSON
|
|
84
|
+
${utils.COLORS.dim}bootspring://context/claude.md${utils.COLORS.reset} - Generated AI context
|
|
85
|
+
${utils.COLORS.dim}bootspring://context/todo.md${utils.COLORS.reset} - Todo list
|
|
86
|
+
${utils.COLORS.dim}bootspring://agents/list${utils.COLORS.reset} - Available agents
|
|
87
|
+
|
|
88
|
+
${utils.COLORS.bold}Setup${utils.COLORS.reset}
|
|
89
|
+
Ensure .mcp.json exists with:
|
|
90
|
+
${utils.COLORS.dim}{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"bootspring": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["bootspring", "mcp"],
|
|
95
|
+
"env": {}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}${utils.COLORS.reset}
|
|
99
|
+
|
|
100
|
+
${utils.COLORS.bold}Test${utils.COLORS.reset}
|
|
101
|
+
Run: ${utils.COLORS.cyan}bootspring mcp test${utils.COLORS.reset}
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Test MCP tools
|
|
107
|
+
*/
|
|
108
|
+
async function testMcp(): Promise<void> {
|
|
109
|
+
console.log(`
|
|
110
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP Test${utils.COLORS.reset}
|
|
111
|
+
`);
|
|
112
|
+
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
114
|
+
const context = require('../../core/context') as ContextModule;
|
|
115
|
+
|
|
116
|
+
// Test config loading
|
|
117
|
+
const spinner1 = utils.createSpinner('Testing config loader').start();
|
|
118
|
+
try {
|
|
119
|
+
const cfg = config.load();
|
|
120
|
+
if (cfg.project) {
|
|
121
|
+
spinner1.succeed(`Config loaded: ${cfg.project.name}`);
|
|
122
|
+
} else {
|
|
123
|
+
spinner1.warn('Config loaded but missing project info');
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
spinner1.fail(`Config error: ${(error as Error).message}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Test context
|
|
130
|
+
const spinner2 = utils.createSpinner('Testing context detector').start();
|
|
131
|
+
try {
|
|
132
|
+
const cfg = config.load();
|
|
133
|
+
const ctx = context.get({ config: cfg });
|
|
134
|
+
spinner2.succeed(`Context loaded: phase=${ctx.state.phase}, health=${ctx.state.health}`);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
spinner2.fail(`Context error: ${(error as Error).message}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Test hooks
|
|
140
|
+
const spinner3 = utils.createSpinner('Testing context detection hooks').start();
|
|
141
|
+
try {
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
143
|
+
const detector = require('../../hooks/context-detector') as ContextDetectorModule;
|
|
144
|
+
const result = detector.detectContext('implement user authentication with Clerk');
|
|
145
|
+
if (result.matches.length > 0) {
|
|
146
|
+
spinner3.succeed(`Detection works: found ${result.matches.length} matches (${result.suggestedAgents.join(', ')})`);
|
|
147
|
+
} else {
|
|
148
|
+
spinner3.warn('Detection works but found no matches');
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
spinner3.fail(`Detection error: ${(error as Error).message}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Test prompt enhancer
|
|
155
|
+
const spinner4 = utils.createSpinner('Testing prompt enhancer').start();
|
|
156
|
+
try {
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
158
|
+
const detector = require('../../hooks/context-detector') as ContextDetectorModule;
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
160
|
+
const enhancer = require('../../hooks/prompt-enhancer') as PromptEnhancerModule;
|
|
161
|
+
const detection = detector.detectContext('create a database schema for users');
|
|
162
|
+
const enhancement = enhancer.enhance('create a database schema', detection, {});
|
|
163
|
+
if (enhancement) {
|
|
164
|
+
spinner4.succeed(`Enhancer works: category=${enhancement.category}`);
|
|
165
|
+
} else {
|
|
166
|
+
spinner4.warn('Enhancer works but produced no enhancement');
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
spinner4.fail(`Enhancer error: ${(error as Error).message}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`
|
|
173
|
+
${utils.COLORS.bold}Summary${utils.COLORS.reset}
|
|
174
|
+
All core MCP components are functional.
|
|
175
|
+
The server will work when invoked by Claude Code.
|
|
176
|
+
|
|
177
|
+
${utils.COLORS.dim}Tip: Use the tools via Claude Code for full integration testing${utils.COLORS.reset}
|
|
178
|
+
`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Generate .mcp.json configuration
|
|
183
|
+
*/
|
|
184
|
+
function generateConfig(): void {
|
|
185
|
+
const cfg = config.load();
|
|
186
|
+
const mcpPath = path.join(cfg._projectRoot, '.mcp.json');
|
|
187
|
+
|
|
188
|
+
const mcpConfig = {
|
|
189
|
+
mcpServers: {
|
|
190
|
+
bootspring: {
|
|
191
|
+
command: 'npx',
|
|
192
|
+
args: ['bootspring', 'mcp'],
|
|
193
|
+
env: {}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const spinner = utils.createSpinner('Generating .mcp.json').start();
|
|
199
|
+
|
|
200
|
+
if (utils.writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2))) {
|
|
201
|
+
spinner.succeed(`Created ${mcpPath}`);
|
|
202
|
+
console.log(`
|
|
203
|
+
${utils.COLORS.dim}Contents:${utils.COLORS.reset}
|
|
204
|
+
${JSON.stringify(mcpConfig, null, 2)}
|
|
205
|
+
`);
|
|
206
|
+
} else {
|
|
207
|
+
spinner.fail('Failed to create .mcp.json');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Show MCP help
|
|
213
|
+
*/
|
|
214
|
+
function showHelp(): void {
|
|
215
|
+
console.log(`
|
|
216
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring MCP${utils.COLORS.reset}
|
|
217
|
+
${utils.COLORS.dim}Model Context Protocol server control${utils.COLORS.reset}
|
|
218
|
+
|
|
219
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
220
|
+
bootspring mcp [command]
|
|
221
|
+
|
|
222
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
223
|
+
${utils.COLORS.cyan}(default)${utils.COLORS.reset} Start MCP server (used by Claude Code)
|
|
224
|
+
${utils.COLORS.cyan}status${utils.COLORS.reset} Show MCP configuration and tools
|
|
225
|
+
${utils.COLORS.cyan}test${utils.COLORS.reset} Test MCP components
|
|
226
|
+
${utils.COLORS.cyan}config${utils.COLORS.reset} Generate .mcp.json file
|
|
227
|
+
${utils.COLORS.cyan}help${utils.COLORS.reset} Show this help
|
|
228
|
+
|
|
229
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
230
|
+
bootspring mcp status # Check configuration
|
|
231
|
+
bootspring mcp test # Test components
|
|
232
|
+
bootspring mcp config # Generate .mcp.json
|
|
233
|
+
`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Run MCP command
|
|
238
|
+
*/
|
|
239
|
+
export async function run(args: string[]): Promise<void> {
|
|
240
|
+
const subcommand = args[0];
|
|
241
|
+
|
|
242
|
+
// Default: start the MCP server (for Claude Code invocation)
|
|
243
|
+
if (!subcommand) {
|
|
244
|
+
startServer();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
switch (subcommand) {
|
|
249
|
+
case 'status':
|
|
250
|
+
showStatus();
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case 'test':
|
|
254
|
+
await testMcp();
|
|
255
|
+
break;
|
|
256
|
+
|
|
257
|
+
case 'config':
|
|
258
|
+
case 'init':
|
|
259
|
+
case 'setup':
|
|
260
|
+
generateConfig();
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case 'help':
|
|
264
|
+
case '-h':
|
|
265
|
+
case '--help':
|
|
266
|
+
showHelp();
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
default:
|
|
270
|
+
utils.print.error(`Unknown subcommand: ${subcommand}`);
|
|
271
|
+
showHelp();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export { startServer, showStatus, testMcp };
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bootspring Memory Command
|
|
5
|
+
* View and manage git-based project memory
|
|
6
|
+
*
|
|
7
|
+
* @package bootspring
|
|
8
|
+
* @module cli/memory
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
|
|
13
|
+
// Colors
|
|
14
|
+
const c = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
bold: '\x1b[1m',
|
|
17
|
+
dim: '\x1b[2m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
blue: '\x1b[34m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
cyan: '\x1b[36m',
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
magenta: '\x1b[35m'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Type definitions for git-memory module
|
|
27
|
+
interface Learning {
|
|
28
|
+
hash: string;
|
|
29
|
+
summary: string;
|
|
30
|
+
date: string;
|
|
31
|
+
details?: string;
|
|
32
|
+
categories: string[];
|
|
33
|
+
file?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ExtractResult {
|
|
37
|
+
learnings: Learning[];
|
|
38
|
+
total: number;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface CategoryConfig {
|
|
43
|
+
icon: string;
|
|
44
|
+
label: string;
|
|
45
|
+
patterns: RegExp[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface RepoStats {
|
|
49
|
+
totalCommits: number;
|
|
50
|
+
contributors: number;
|
|
51
|
+
weeklyPace: number;
|
|
52
|
+
firstCommit: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface GitMemoryModule {
|
|
56
|
+
isGitRepo(): boolean;
|
|
57
|
+
extractLearnings(options?: { limit?: number; since?: string }): ExtractResult;
|
|
58
|
+
groupByCategory(learnings: Learning[]): Record<string, Learning[]>;
|
|
59
|
+
searchLearnings(learnings: Learning[], query: string): Learning[];
|
|
60
|
+
getLearningsForFiles(files: string[]): Learning[];
|
|
61
|
+
toMarkdown(learnings: Learning[], options?: { title?: string; maxPerCategory?: number }): string;
|
|
62
|
+
getRepoStats(): RepoStats | null;
|
|
63
|
+
MEMORY_CATEGORIES: Record<string, CategoryConfig>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
67
|
+
const gitMemory = require('../../intelligence/git-memory') as GitMemoryModule;
|
|
68
|
+
|
|
69
|
+
interface MemoryOptions {
|
|
70
|
+
limit?: number;
|
|
71
|
+
since?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Display learnings summary
|
|
76
|
+
*/
|
|
77
|
+
function showSummary(options: MemoryOptions = {}): void {
|
|
78
|
+
const { limit = 30, since = '3 months ago' } = options;
|
|
79
|
+
|
|
80
|
+
console.log(`\n${c.cyan}${c.bold}⚡ Project Memory${c.reset}\n`);
|
|
81
|
+
|
|
82
|
+
if (!gitMemory.isGitRepo()) {
|
|
83
|
+
console.log(`${c.red}Not a git repository${c.reset}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const result = gitMemory.extractLearnings({ limit, since });
|
|
88
|
+
|
|
89
|
+
if (result.error) {
|
|
90
|
+
console.log(`${c.red}Error: ${result.error}${c.reset}`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (result.learnings.length === 0) {
|
|
95
|
+
console.log(`${c.yellow}No significant learnings found in recent commits${c.reset}`);
|
|
96
|
+
console.log(`${c.dim}Try using more descriptive commit messages${c.reset}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Group by category
|
|
101
|
+
const grouped = gitMemory.groupByCategory(result.learnings);
|
|
102
|
+
|
|
103
|
+
console.log(`${c.dim}Extracted ${result.learnings.length} learnings from ${result.total} commits${c.reset}\n`);
|
|
104
|
+
|
|
105
|
+
for (const [category, items] of Object.entries(grouped)) {
|
|
106
|
+
const config = gitMemory.MEMORY_CATEGORIES[category] || { icon: '📝', label: category };
|
|
107
|
+
|
|
108
|
+
console.log(`${c.bold}${config.icon} ${config.label}${c.reset} (${items.length})`);
|
|
109
|
+
|
|
110
|
+
for (const item of items.slice(0, 3)) {
|
|
111
|
+
console.log(` ${c.dim}[${item.hash}]${c.reset} ${item.summary}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (items.length > 3) {
|
|
115
|
+
console.log(` ${c.dim}... and ${items.length - 3} more${c.reset}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log('');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Search learnings
|
|
124
|
+
*/
|
|
125
|
+
function searchMemory(query: string): void {
|
|
126
|
+
console.log(`\n${c.cyan}${c.bold}⚡ Search Memory${c.reset}\n`);
|
|
127
|
+
|
|
128
|
+
if (!query) {
|
|
129
|
+
console.log(`${c.red}Usage: bootspring memory search <query>${c.reset}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = gitMemory.extractLearnings({ limit: 100 });
|
|
134
|
+
const matches = gitMemory.searchLearnings(result.learnings, query);
|
|
135
|
+
|
|
136
|
+
console.log(`${c.dim}Found ${matches.length} matches for "${query}"${c.reset}\n`);
|
|
137
|
+
|
|
138
|
+
for (const match of matches.slice(0, 15)) {
|
|
139
|
+
const icons = match.categories
|
|
140
|
+
.map(cat => gitMemory.MEMORY_CATEGORIES[cat]?.icon || '📝')
|
|
141
|
+
.join('');
|
|
142
|
+
|
|
143
|
+
console.log(`${icons} ${c.bold}${match.summary}${c.reset}`);
|
|
144
|
+
console.log(` ${c.dim}[${match.hash}] ${match.date}${c.reset}`);
|
|
145
|
+
|
|
146
|
+
if (match.details) {
|
|
147
|
+
const firstLine = match.details.split('\n')[0];
|
|
148
|
+
if (firstLine) {
|
|
149
|
+
const preview = firstLine.slice(0, 80);
|
|
150
|
+
console.log(` ${c.dim}${preview}${c.reset}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (matches.length > 15) {
|
|
158
|
+
console.log(`${c.dim}... and ${matches.length - 15} more matches${c.reset}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Show learnings for specific files
|
|
164
|
+
*/
|
|
165
|
+
function showForFiles(files: string[]): void {
|
|
166
|
+
console.log(`\n${c.cyan}${c.bold}⚡ File Memory${c.reset}\n`);
|
|
167
|
+
|
|
168
|
+
if (!files || files.length === 0) {
|
|
169
|
+
console.log(`${c.red}Usage: bootspring memory files <file1> [file2...]${c.reset}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const learnings = gitMemory.getLearningsForFiles(files);
|
|
174
|
+
|
|
175
|
+
if (learnings.length === 0) {
|
|
176
|
+
console.log(`${c.yellow}No learnings found for these files${c.reset}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(`${c.dim}Learnings related to: ${files.join(', ')}${c.reset}\n`);
|
|
181
|
+
|
|
182
|
+
for (const learning of learnings) {
|
|
183
|
+
const icons = learning.categories
|
|
184
|
+
.map(cat => gitMemory.MEMORY_CATEGORIES[cat]?.icon || '📝')
|
|
185
|
+
.join('');
|
|
186
|
+
|
|
187
|
+
console.log(`${icons} ${c.bold}${learning.summary}${c.reset}`);
|
|
188
|
+
console.log(` ${c.dim}[${learning.hash}] File: ${learning.file}${c.reset}`);
|
|
189
|
+
console.log('');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Export learnings to markdown
|
|
195
|
+
*/
|
|
196
|
+
function exportMarkdown(outputPath?: string): void {
|
|
197
|
+
const result = gitMemory.extractLearnings({ limit: 50 });
|
|
198
|
+
|
|
199
|
+
if (result.error) {
|
|
200
|
+
console.log(`${c.red}Error: ${result.error}${c.reset}`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const markdown = gitMemory.toMarkdown(result.learnings, {
|
|
205
|
+
title: 'Project Learnings',
|
|
206
|
+
maxPerCategory: 10
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if (outputPath) {
|
|
210
|
+
fs.writeFileSync(outputPath, markdown);
|
|
211
|
+
console.log(`${c.green}Exported to ${outputPath}${c.reset}`);
|
|
212
|
+
} else {
|
|
213
|
+
console.log(markdown);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Show repo stats
|
|
219
|
+
*/
|
|
220
|
+
function showStats(): void {
|
|
221
|
+
console.log(`\n${c.cyan}${c.bold}⚡ Repository Stats${c.reset}\n`);
|
|
222
|
+
|
|
223
|
+
const stats = gitMemory.getRepoStats();
|
|
224
|
+
|
|
225
|
+
if (!stats) {
|
|
226
|
+
console.log(`${c.red}Not a git repository${c.reset}`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log(` ${c.bold}Total Commits:${c.reset} ${stats.totalCommits}`);
|
|
231
|
+
console.log(` ${c.bold}Contributors:${c.reset} ${stats.contributors}`);
|
|
232
|
+
console.log(` ${c.bold}Weekly Activity:${c.reset} ${stats.weeklyPace} commits`);
|
|
233
|
+
console.log(` ${c.bold}First Commit:${c.reset} ${stats.firstCommit || 'unknown'}`);
|
|
234
|
+
console.log('');
|
|
235
|
+
|
|
236
|
+
// Show category breakdown
|
|
237
|
+
const result = gitMemory.extractLearnings({ limit: 100 });
|
|
238
|
+
const grouped = gitMemory.groupByCategory(result.learnings);
|
|
239
|
+
|
|
240
|
+
console.log(`${c.bold}Learning Categories:${c.reset}\n`);
|
|
241
|
+
|
|
242
|
+
for (const [category, items] of Object.entries(grouped).sort((a, b) => b[1].length - a[1].length)) {
|
|
243
|
+
const config = gitMemory.MEMORY_CATEGORIES[category] || { icon: '📝', label: category };
|
|
244
|
+
const bar = '█'.repeat(Math.min(items.length, 20));
|
|
245
|
+
console.log(` ${config.icon} ${config.label.padEnd(20)} ${c.cyan}${bar}${c.reset} ${items.length}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
console.log('');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Show help
|
|
253
|
+
*/
|
|
254
|
+
function showHelp(): void {
|
|
255
|
+
console.log(`
|
|
256
|
+
${c.bold}Bootspring Memory${c.reset}
|
|
257
|
+
|
|
258
|
+
Extract and search project learnings from git history.
|
|
259
|
+
|
|
260
|
+
${c.cyan}Usage:${c.reset}
|
|
261
|
+
bootspring memory <command> [options]
|
|
262
|
+
|
|
263
|
+
${c.cyan}Commands:${c.reset}
|
|
264
|
+
summary Show categorized learning summary (default)
|
|
265
|
+
search <query> Search learnings by keyword
|
|
266
|
+
files <file...> Show learnings for specific files
|
|
267
|
+
export [path] Export learnings to markdown
|
|
268
|
+
stats Show repository statistics
|
|
269
|
+
categories List memory categories
|
|
270
|
+
help Show this help
|
|
271
|
+
|
|
272
|
+
${c.cyan}Options:${c.reset}
|
|
273
|
+
--limit <n> Number of commits to analyze (default: 30)
|
|
274
|
+
--since <date> How far back to look (default: "3 months ago")
|
|
275
|
+
|
|
276
|
+
${c.cyan}Examples:${c.reset}
|
|
277
|
+
bootspring memory summary
|
|
278
|
+
bootspring memory search "authentication"
|
|
279
|
+
bootspring memory files src/api/auth.ts
|
|
280
|
+
bootspring memory export LEARNINGS.md
|
|
281
|
+
bootspring memory stats
|
|
282
|
+
|
|
283
|
+
${c.cyan}Categories:${c.reset}
|
|
284
|
+
${Object.entries(gitMemory.MEMORY_CATEGORIES)
|
|
285
|
+
.map(([key, cfg]) => ` ${cfg.icon} ${key.padEnd(15)} ${cfg.label}`)
|
|
286
|
+
.join('\n')}
|
|
287
|
+
`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Main CLI handler
|
|
292
|
+
*/
|
|
293
|
+
export async function run(args: string[]): Promise<void> {
|
|
294
|
+
const command = args[0] || 'summary';
|
|
295
|
+
|
|
296
|
+
// Parse options
|
|
297
|
+
const options: MemoryOptions = {};
|
|
298
|
+
for (let i = 0; i < args.length; i++) {
|
|
299
|
+
const nextArg = args[i + 1];
|
|
300
|
+
if (args[i] === '--limit' && nextArg) {
|
|
301
|
+
options.limit = parseInt(nextArg, 10);
|
|
302
|
+
}
|
|
303
|
+
if (args[i] === '--since' && nextArg) {
|
|
304
|
+
options.since = nextArg;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
switch (command) {
|
|
309
|
+
case 'summary':
|
|
310
|
+
case 'show':
|
|
311
|
+
showSummary(options);
|
|
312
|
+
break;
|
|
313
|
+
|
|
314
|
+
case 'search':
|
|
315
|
+
searchMemory(args.slice(1).filter(a => !a.startsWith('--')).join(' '));
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case 'files':
|
|
319
|
+
case 'file':
|
|
320
|
+
showForFiles(args.slice(1).filter(a => !a.startsWith('--')));
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
case 'export':
|
|
324
|
+
exportMarkdown(args[1]);
|
|
325
|
+
break;
|
|
326
|
+
|
|
327
|
+
case 'stats':
|
|
328
|
+
showStats();
|
|
329
|
+
break;
|
|
330
|
+
|
|
331
|
+
case 'categories':
|
|
332
|
+
console.log(`\n${c.bold}Memory Categories:${c.reset}\n`);
|
|
333
|
+
for (const [key, cfg] of Object.entries(gitMemory.MEMORY_CATEGORIES)) {
|
|
334
|
+
console.log(` ${cfg.icon} ${key.padEnd(15)} ${c.dim}${cfg.label}${c.reset}`);
|
|
335
|
+
console.log(` ${c.dim}Patterns: ${cfg.patterns.slice(0, 3).map(p => p.source).join(', ')}...${c.reset}`);
|
|
336
|
+
}
|
|
337
|
+
console.log('');
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
case 'help':
|
|
341
|
+
case '--help':
|
|
342
|
+
case '-h':
|
|
343
|
+
default:
|
|
344
|
+
showHelp();
|
|
345
|
+
}
|
|
346
|
+
}
|