@agentbrain/mcp-server 1.4.70 → 1.4.76
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/dist/index.js +67 -20
- package/dist/index.js.map +1 -1
- package/dist/tools/generate-context.d.ts +24 -0
- package/dist/tools/generate-context.d.ts.map +1 -0
- package/dist/tools/generate-context.js +73 -0
- package/dist/tools/generate-context.js.map +1 -0
- package/dist/tools/load-blueprint.d.ts +19 -0
- package/dist/tools/load-blueprint.d.ts.map +1 -0
- package/dist/tools/load-blueprint.js +152 -0
- package/dist/tools/load-blueprint.js.map +1 -0
- package/dist/tools/load-context.d.ts.map +1 -1
- package/dist/tools/load-context.js +190 -49
- package/dist/tools/load-context.js.map +1 -1
- package/dist/tools/load-decisions.d.ts +19 -0
- package/dist/tools/load-decisions.d.ts.map +1 -0
- package/dist/tools/load-decisions.js +138 -0
- package/dist/tools/load-decisions.js.map +1 -0
- package/dist/tools/load-guardrails.d.ts +19 -0
- package/dist/tools/load-guardrails.d.ts.map +1 -0
- package/dist/tools/load-guardrails.js +149 -0
- package/dist/tools/load-guardrails.js.map +1 -0
- package/dist/tools/load-standards.d.ts.map +1 -1
- package/dist/tools/load-standards.js +135 -4
- package/dist/tools/load-standards.js.map +1 -1
- package/dist/tools/preflight-check.d.ts +24 -0
- package/dist/tools/preflight-check.d.ts.map +1 -0
- package/dist/tools/preflight-check.js +279 -0
- package/dist/tools/preflight-check.js.map +1 -0
- package/dist/tools/save-context.d.ts.map +1 -1
- package/dist/tools/save-context.js +40 -10
- package/dist/tools/save-context.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// MCP tool: load_guardrails - Load workspace Guardrails from cloud
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
// ══════════════════════════════════════════════════════════════
|
|
5
|
+
// Environment & Configuration
|
|
6
|
+
// ══════════════════════════════════════════════════════════════
|
|
7
|
+
const BASE_URL = process.env.AGENTBRAIN_API_BASE_URL?.replace(/\/$/, '') ?? 'https://www.useagentbrain.com/api/v1';
|
|
8
|
+
const GUARDRAILS_URL = `${BASE_URL}/guardrails`;
|
|
9
|
+
/**
|
|
10
|
+
* Expand path: handles ~, relative paths, etc.
|
|
11
|
+
*/
|
|
12
|
+
function expandPath(path) {
|
|
13
|
+
if (path.startsWith('~/') || path === '~') {
|
|
14
|
+
return path.replace('~', homedir());
|
|
15
|
+
}
|
|
16
|
+
if (!path.startsWith('/')) {
|
|
17
|
+
return join(process.cwd(), path);
|
|
18
|
+
}
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
// ══════════════════════════════════════════════════════════════
|
|
22
|
+
// Schema
|
|
23
|
+
// ══════════════════════════════════════════════════════════════
|
|
24
|
+
export const loadGuardrailsSchema = {
|
|
25
|
+
name: 'load_guardrails',
|
|
26
|
+
description: 'Load workspace Guardrails — protected files, sensitive data rules, team boundaries, and invariants. Load before any file modification.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
repo_path: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Absolute path to repository',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['repo_path'],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
// ══════════════════════════════════════════════════════════════
|
|
39
|
+
// Formatting
|
|
40
|
+
// ══════════════════════════════════════════════════════════════
|
|
41
|
+
function formatGuardrails(content) {
|
|
42
|
+
const workspaceName = content.workspace_name || 'Workspace';
|
|
43
|
+
let output = `# Guardrails — ${workspaceName}\n\n`;
|
|
44
|
+
// Protected Files
|
|
45
|
+
if (content.protected_files && content.protected_files.length > 0) {
|
|
46
|
+
output += '## Protected Files\n';
|
|
47
|
+
output += '⚠️ Never modify these files:\n';
|
|
48
|
+
for (const file of content.protected_files) {
|
|
49
|
+
output += `- ${file.path}: ${file.reason}\n`;
|
|
50
|
+
}
|
|
51
|
+
output += '\n';
|
|
52
|
+
}
|
|
53
|
+
// Sensitive Data
|
|
54
|
+
if (content.sensitive_data && content.sensitive_data.length > 0) {
|
|
55
|
+
output += '## Sensitive Data\n';
|
|
56
|
+
output += '🔒 Never log or expose these fields:\n';
|
|
57
|
+
for (const data of content.sensitive_data) {
|
|
58
|
+
output += `- ${data.field}: ${data.rule}\n`;
|
|
59
|
+
}
|
|
60
|
+
output += '\n';
|
|
61
|
+
}
|
|
62
|
+
// Team Boundaries
|
|
63
|
+
if (content.team_boundaries && content.team_boundaries.length > 0) {
|
|
64
|
+
output += '## Team Boundaries\n';
|
|
65
|
+
output += '👥 Requires approval before changes:\n';
|
|
66
|
+
for (const boundary of content.team_boundaries) {
|
|
67
|
+
output += `- ${boundary.path} → ${boundary.owner}: ${boundary.rule}\n`;
|
|
68
|
+
}
|
|
69
|
+
output += '\n';
|
|
70
|
+
}
|
|
71
|
+
// Invariants
|
|
72
|
+
if (content.invariants && content.invariants.length > 0) {
|
|
73
|
+
output += '## Invariants\n';
|
|
74
|
+
output += '🔴 Must always be true:\n';
|
|
75
|
+
for (const invariant of content.invariants) {
|
|
76
|
+
output += `- ${invariant}\n`;
|
|
77
|
+
}
|
|
78
|
+
output += '\n';
|
|
79
|
+
}
|
|
80
|
+
// Refactor Rules
|
|
81
|
+
if (content.refactor_scope || content.ask_before_refactoring !== undefined || content.refactor_notes) {
|
|
82
|
+
output += '## Refactor Rules\n';
|
|
83
|
+
if (content.refactor_scope) {
|
|
84
|
+
output += `Scope: ${content.refactor_scope}\n`;
|
|
85
|
+
}
|
|
86
|
+
if (content.ask_before_refactoring !== undefined) {
|
|
87
|
+
output += `Ask before refactoring outside scope: ${content.ask_before_refactoring ? 'yes' : 'no'}\n`;
|
|
88
|
+
}
|
|
89
|
+
if (content.refactor_notes) {
|
|
90
|
+
output += `${content.refactor_notes}\n`;
|
|
91
|
+
}
|
|
92
|
+
output += '\n';
|
|
93
|
+
}
|
|
94
|
+
return output;
|
|
95
|
+
}
|
|
96
|
+
function isContentEmpty(content) {
|
|
97
|
+
const hasProtectedFiles = content.protected_files && content.protected_files.length > 0;
|
|
98
|
+
const hasSensitiveData = content.sensitive_data && content.sensitive_data.length > 0;
|
|
99
|
+
const hasTeamBoundaries = content.team_boundaries && content.team_boundaries.length > 0;
|
|
100
|
+
const hasInvariants = content.invariants && content.invariants.length > 0;
|
|
101
|
+
const hasRefactorRules = !!(content.refactor_scope || content.ask_before_refactoring !== undefined || content.refactor_notes);
|
|
102
|
+
return !(hasProtectedFiles || hasSensitiveData || hasTeamBoundaries || hasInvariants || hasRefactorRules);
|
|
103
|
+
}
|
|
104
|
+
// ══════════════════════════════════════════════════════════════
|
|
105
|
+
// Main Tool Function
|
|
106
|
+
// ══════════════════════════════════════════════════════════════
|
|
107
|
+
export async function loadGuardrails(input) {
|
|
108
|
+
const apiKey = process.env.AGENTBRAIN_API_KEY;
|
|
109
|
+
if (!apiKey) {
|
|
110
|
+
return `Guardrails requires an API key.
|
|
111
|
+
Add AGENTBRAIN_API_KEY to your MCP config env block.
|
|
112
|
+
Get your key at useagentbrain.com/settings`;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(GUARDRAILS_URL, {
|
|
116
|
+
method: 'GET',
|
|
117
|
+
headers: {
|
|
118
|
+
'x-api-key': apiKey,
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if (res.status === 401) {
|
|
123
|
+
return 'Invalid AGENTBRAIN_API_KEY. Check your MCP config env block.';
|
|
124
|
+
}
|
|
125
|
+
if (res.status === 404) {
|
|
126
|
+
return `No Guardrails found for this workspace.
|
|
127
|
+
Create one at useagentbrain.com/settings → Guardrails tab.`;
|
|
128
|
+
}
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
const json = (await res.json());
|
|
131
|
+
return json.error ?? 'Failed to load Guardrails from AgentBrain.';
|
|
132
|
+
}
|
|
133
|
+
const json = (await res.json());
|
|
134
|
+
if (!json.data) {
|
|
135
|
+
return `No Guardrails found for this workspace.
|
|
136
|
+
Create one at useagentbrain.com/settings → Guardrails tab.`;
|
|
137
|
+
}
|
|
138
|
+
const content = json.data.content;
|
|
139
|
+
if (isContentEmpty(content)) {
|
|
140
|
+
return `Guardrails exists but has no content yet.
|
|
141
|
+
Fill it out at useagentbrain.com/settings → Guardrails tab.`;
|
|
142
|
+
}
|
|
143
|
+
return formatGuardrails(content);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return err instanceof Error ? err.message : 'Failed to load Guardrails.';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=load-guardrails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-guardrails.js","sourceRoot":"","sources":["../../src/tools/load-guardrails.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-standards.d.ts","sourceRoot":"","sources":["../../src/tools/load-standards.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"load-standards.d.ts","sourceRoot":"","sources":["../../src/tools/load-standards.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAyBnD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,WAAW,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,WAAW,CAAA;CACnB;AAiID,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsD3F;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;CAmB/B,CAAA"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
// MCP tool: load_standards - read standards file
|
|
1
|
+
// MCP tool: load_standards - read standards file (cloud or local)
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { AGENT_FILE_PATHS } from '@agentbrain/core';
|
|
7
|
+
// ══════════════════════════════════════════════════════════════
|
|
8
|
+
// Environment & Configuration
|
|
9
|
+
// ══════════════════════════════════════════════════════════════
|
|
10
|
+
const BASE_URL = process.env.AGENTBRAIN_API_BASE_URL?.replace(/\/$/, '') ?? 'https://www.useagentbrain.com/api/v1';
|
|
11
|
+
const STANDARDS_URL = `${BASE_URL}/standards`;
|
|
7
12
|
/**
|
|
8
13
|
* Expand path: handles ~, relative paths, etc.
|
|
9
14
|
*/
|
|
@@ -13,14 +18,140 @@ function expandPath(path) {
|
|
|
13
18
|
}
|
|
14
19
|
return resolve(path);
|
|
15
20
|
}
|
|
21
|
+
// ══════════════════════════════════════════════════════════════
|
|
22
|
+
// Formatting
|
|
23
|
+
// ══════════════════════════════════════════════════════════════
|
|
24
|
+
function formatStandards(content) {
|
|
25
|
+
const workspaceName = content.workspace_name || 'Workspace';
|
|
26
|
+
let output = `# Standards — ${workspaceName}\n\n`;
|
|
27
|
+
// Test Strategy
|
|
28
|
+
if (content.test_strategy) {
|
|
29
|
+
const ts = content.test_strategy;
|
|
30
|
+
if (ts.framework || ts.always_test || ts.never_test || ts.coverage_goal || ts.required) {
|
|
31
|
+
output += '## Test Strategy\n';
|
|
32
|
+
if (ts.framework)
|
|
33
|
+
output += `Framework: ${ts.framework}\n`;
|
|
34
|
+
if (ts.always_test)
|
|
35
|
+
output += `Always test: ${ts.always_test}\n`;
|
|
36
|
+
if (ts.never_test)
|
|
37
|
+
output += `Never test: ${ts.never_test}\n`;
|
|
38
|
+
if (ts.coverage_goal)
|
|
39
|
+
output += `Coverage goal: ${ts.coverage_goal}\n`;
|
|
40
|
+
if (ts.required)
|
|
41
|
+
output += `Required: ${ts.required}\n`;
|
|
42
|
+
output += '\n';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// PR Requirements
|
|
46
|
+
if (content.pr_requirements) {
|
|
47
|
+
const pr = content.pr_requirements;
|
|
48
|
+
if (pr.max_pr_size || pr.commit_format || pr.description_required !== undefined || pr.tests_required !== undefined) {
|
|
49
|
+
output += '## PR Requirements\n';
|
|
50
|
+
if (pr.max_pr_size)
|
|
51
|
+
output += `Max PR size: ${pr.max_pr_size}\n`;
|
|
52
|
+
if (pr.commit_format)
|
|
53
|
+
output += `Commit format: ${pr.commit_format}\n`;
|
|
54
|
+
if (pr.description_required !== undefined)
|
|
55
|
+
output += `Description required: ${pr.description_required ? 'yes' : 'no'}\n`;
|
|
56
|
+
if (pr.tests_required !== undefined)
|
|
57
|
+
output += `Tests required: ${pr.tests_required ? 'yes' : 'no'}\n`;
|
|
58
|
+
output += '\n';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Code Style
|
|
62
|
+
if (content.code_style) {
|
|
63
|
+
const cs = content.code_style;
|
|
64
|
+
if (cs.max_function_length || cs.naming_conventions || cs.comment_style || cs.forbidden_patterns) {
|
|
65
|
+
output += '## Code Style\n';
|
|
66
|
+
if (cs.max_function_length)
|
|
67
|
+
output += `Max function length: ${cs.max_function_length}\n`;
|
|
68
|
+
if (cs.naming_conventions)
|
|
69
|
+
output += `Naming conventions: ${cs.naming_conventions}\n`;
|
|
70
|
+
if (cs.comment_style)
|
|
71
|
+
output += `Comment style: ${cs.comment_style}\n`;
|
|
72
|
+
if (cs.forbidden_patterns)
|
|
73
|
+
output += `Forbidden patterns: ${cs.forbidden_patterns}\n`;
|
|
74
|
+
output += '\n';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Definition of Done
|
|
78
|
+
if (content.definition_of_done && content.definition_of_done.length > 0) {
|
|
79
|
+
output += '## Definition of Done\n';
|
|
80
|
+
output += 'Before marking any task complete, verify ALL of these:\n';
|
|
81
|
+
for (const item of content.definition_of_done) {
|
|
82
|
+
output += `- ${item}\n`;
|
|
83
|
+
}
|
|
84
|
+
output += '\n';
|
|
85
|
+
}
|
|
86
|
+
// Run Commands
|
|
87
|
+
if (content.run_commands) {
|
|
88
|
+
const rc = content.run_commands;
|
|
89
|
+
if (rc.dev || rc.test || rc.build || rc.lint || rc.typecheck) {
|
|
90
|
+
output += '## Run Commands\n';
|
|
91
|
+
if (rc.dev)
|
|
92
|
+
output += `Dev: ${rc.dev}\n`;
|
|
93
|
+
if (rc.test)
|
|
94
|
+
output += `Test: ${rc.test}\n`;
|
|
95
|
+
if (rc.build)
|
|
96
|
+
output += `Build: ${rc.build}\n`;
|
|
97
|
+
if (rc.lint)
|
|
98
|
+
output += `Lint: ${rc.lint}\n`;
|
|
99
|
+
if (rc.typecheck)
|
|
100
|
+
output += `Type check: ${rc.typecheck}\n`;
|
|
101
|
+
output += '\n';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
function isContentEmpty(content) {
|
|
107
|
+
const hasTestStrategy = content.test_strategy && Object.values(content.test_strategy).some(v => v);
|
|
108
|
+
const hasPRRequirements = content.pr_requirements && Object.values(content.pr_requirements).some(v => v !== undefined);
|
|
109
|
+
const hasCodeStyle = content.code_style && Object.values(content.code_style).some(v => v);
|
|
110
|
+
const hasDefinitionOfDone = content.definition_of_done && content.definition_of_done.length > 0;
|
|
111
|
+
const hasRunCommands = content.run_commands && Object.values(content.run_commands).some(v => v);
|
|
112
|
+
return !(hasTestStrategy || hasPRRequirements || hasCodeStyle || hasDefinitionOfDone || hasRunCommands);
|
|
113
|
+
}
|
|
114
|
+
// ══════════════════════════════════════════════════════════════
|
|
115
|
+
// Main Tool Function
|
|
116
|
+
// ══════════════════════════════════════════════════════════════
|
|
16
117
|
export async function loadStandards(input) {
|
|
17
118
|
const { repo_path, agent } = input;
|
|
18
|
-
// Expand path to handle ~, relative paths, etc.
|
|
19
119
|
const expandedPath = expandPath(repo_path);
|
|
120
|
+
const apiKey = process.env.AGENTBRAIN_API_KEY;
|
|
121
|
+
// ── Cloud mode with fallback ──────────────────────────────
|
|
122
|
+
if (apiKey) {
|
|
123
|
+
try {
|
|
124
|
+
const res = await fetch(STANDARDS_URL, {
|
|
125
|
+
method: 'GET',
|
|
126
|
+
headers: {
|
|
127
|
+
'x-api-key': apiKey,
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
// If 200 with content, use cloud version
|
|
132
|
+
if (res.ok) {
|
|
133
|
+
const json = (await res.json());
|
|
134
|
+
if (json.data && !isContentEmpty(json.data.content)) {
|
|
135
|
+
return {
|
|
136
|
+
content: formatStandards(json.data.content),
|
|
137
|
+
filePath: 'cloud:standards',
|
|
138
|
+
agent,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// 404 or empty content → fall back to local file
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Network error → fall back to local file
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// ── Local disk mode (no API key OR cloud fallback) ────────
|
|
20
149
|
const relativePath = AGENT_FILE_PATHS[agent];
|
|
21
150
|
const filePath = join(expandedPath, relativePath);
|
|
22
151
|
if (!existsSync(filePath)) {
|
|
23
|
-
throw new Error(
|
|
152
|
+
throw new Error(apiKey
|
|
153
|
+
? `No Standards found. Create one at useagentbrain.com/settings → Standards tab or create a local standards file.`
|
|
154
|
+
: `Standards file not found: ${relativePath}. Run "agentbrain standards" to generate it.`);
|
|
24
155
|
}
|
|
25
156
|
const content = await readFile(filePath, 'utf-8');
|
|
26
157
|
return {
|
|
@@ -31,7 +162,7 @@ export async function loadStandards(input) {
|
|
|
31
162
|
}
|
|
32
163
|
export const loadStandardsSchema = {
|
|
33
164
|
name: 'load_standards',
|
|
34
|
-
description: 'Load coding standards
|
|
165
|
+
description: 'Load coding standards (test/PR requirements). With AGENTBRAIN_API_KEY set, fetches from cloud first, falls back to local file. Without API key, reads from disk only.',
|
|
35
166
|
inputSchema: {
|
|
36
167
|
type: 'object',
|
|
37
168
|
properties: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-standards.js","sourceRoot":"","sources":["../../src/tools/load-standards.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"load-standards.js","sourceRoot":"","sources":["../../src/tools/load-standards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface PreflightCheckInput {
|
|
2
|
+
repo_path: string;
|
|
3
|
+
task?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const preflightCheckSchema: {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: string;
|
|
10
|
+
properties: {
|
|
11
|
+
repo_path: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
task: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
required: string[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare function preflightCheck(input: PreflightCheckInput): Promise<string>;
|
|
24
|
+
//# sourceMappingURL=preflight-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight-check.d.ts","sourceRoot":"","sources":["../../src/tools/preflight-check.ts"],"names":[],"mappings":"AAoCA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAwBD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;CAyBhC,CAAA;AA8GD,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqJhF"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// MCP tool: preflight_check - Load everything before starting a task
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { loadContext } from './load-context.js';
|
|
5
|
+
import { loadBlueprint } from './load-blueprint.js';
|
|
6
|
+
import { loadGuardrails } from './load-guardrails.js';
|
|
7
|
+
import { loadStandards } from './load-standards.js';
|
|
8
|
+
import { loadDecisions } from './load-decisions.js';
|
|
9
|
+
// ══════════════════════════════════════════════════════════════
|
|
10
|
+
// Environment & Configuration
|
|
11
|
+
// ══════════════════════════════════════════════════════════════
|
|
12
|
+
const BASE_URL = process.env.AGENTBRAIN_API_BASE_URL?.replace(/\/$/, '') ?? 'https://www.useagentbrain.com/api/v1';
|
|
13
|
+
const SPECS_URL = `${BASE_URL}/specs`;
|
|
14
|
+
/**
|
|
15
|
+
* Expand path: handles ~, relative paths, etc.
|
|
16
|
+
*/
|
|
17
|
+
function expandPath(path) {
|
|
18
|
+
if (path.startsWith('~/') || path === '~') {
|
|
19
|
+
return path.replace('~', homedir());
|
|
20
|
+
}
|
|
21
|
+
if (!path.startsWith('/')) {
|
|
22
|
+
return join(process.cwd(), path);
|
|
23
|
+
}
|
|
24
|
+
return path;
|
|
25
|
+
}
|
|
26
|
+
// ══════════════════════════════════════════════════════════════
|
|
27
|
+
// Schema
|
|
28
|
+
// ══════════════════════════════════════════════════════════════
|
|
29
|
+
export const preflightCheckSchema = {
|
|
30
|
+
name: 'preflight_check',
|
|
31
|
+
description: 'Run before starting ANY task. Loads codebase context, workspace Playbook (Blueprint, Guardrails, Standards, Decisions), and active spec in one parallel call. Returns a targeted briefing for your task.\n\n' +
|
|
32
|
+
'Use this instead of calling load_context(), load_blueprint(), load_guardrails(), load_standards(), load_decisions(), and load_spec() separately.\n\n' +
|
|
33
|
+
'Usage:\n' +
|
|
34
|
+
'preflight_check({\n' +
|
|
35
|
+
' repo_path: "/path/to/repo",\n' +
|
|
36
|
+
' task: "what you are about to build"\n' +
|
|
37
|
+
'})',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
repo_path: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Absolute path to repository',
|
|
44
|
+
},
|
|
45
|
+
task: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Optional: What you are about to do (e.g., "implement Stripe webhook handler", "refactor auth module"). Used to filter relevant context.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ['repo_path'],
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
// ══════════════════════════════════════════════════════════════
|
|
54
|
+
// Helpers
|
|
55
|
+
// ══════════════════════════════════════════════════════════════
|
|
56
|
+
async function fetchActiveSpec(apiKey) {
|
|
57
|
+
try {
|
|
58
|
+
const res = await fetch(SPECS_URL, {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
headers: {
|
|
61
|
+
'x-api-key': apiKey,
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok)
|
|
66
|
+
return null;
|
|
67
|
+
const json = (await res.json());
|
|
68
|
+
if (!json.data)
|
|
69
|
+
return null;
|
|
70
|
+
const spec = json.data;
|
|
71
|
+
const approach = Array.isArray(spec.answers.approach)
|
|
72
|
+
? spec.answers.approach.map((a) => `- ${a}`).join('\n')
|
|
73
|
+
: spec.answers.approach;
|
|
74
|
+
const doneCriteria = Array.isArray(spec.answers.done_criteria)
|
|
75
|
+
? spec.answers.done_criteria.map((d) => `- [ ] ${d}`).join('\n')
|
|
76
|
+
: spec.answers.done_criteria;
|
|
77
|
+
return `**${spec.title}** (${spec.status})
|
|
78
|
+
|
|
79
|
+
Problem: ${spec.answers.problem}
|
|
80
|
+
|
|
81
|
+
Approach:
|
|
82
|
+
${approach}
|
|
83
|
+
|
|
84
|
+
Done Criteria:
|
|
85
|
+
${doneCriteria}`;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isRelevantSection(section, task) {
|
|
92
|
+
const taskLower = task.toLowerCase();
|
|
93
|
+
const sectionLower = section.toLowerCase();
|
|
94
|
+
// Auth-related tasks
|
|
95
|
+
if (taskLower.includes('auth') && sectionLower.includes('auth'))
|
|
96
|
+
return true;
|
|
97
|
+
// Billing/payment tasks
|
|
98
|
+
if ((taskLower.includes('billing') || taskLower.includes('stripe') || taskLower.includes('payment')) &&
|
|
99
|
+
(sectionLower.includes('billing') || sectionLower.includes('stripe') || sectionLower.includes('payment'))) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
// Database tasks
|
|
103
|
+
if ((taskLower.includes('database') || taskLower.includes('migration') || taskLower.includes('schema')) &&
|
|
104
|
+
(sectionLower.includes('database') || sectionLower.includes('migration') || sectionLower.includes('schema'))) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
// API tasks
|
|
108
|
+
if ((taskLower.includes('api') || taskLower.includes('endpoint') || taskLower.includes('route')) &&
|
|
109
|
+
(sectionLower.includes('api') || sectionLower.includes('endpoint') || sectionLower.includes('route'))) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
function filterContent(content, task) {
|
|
115
|
+
if (!task)
|
|
116
|
+
return content;
|
|
117
|
+
const lines = content.split('\n');
|
|
118
|
+
const filtered = [];
|
|
119
|
+
let currentSection = '';
|
|
120
|
+
let includeSection = false;
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
// New section header
|
|
123
|
+
if (line.startsWith('##')) {
|
|
124
|
+
currentSection = line;
|
|
125
|
+
includeSection = isRelevantSection(line, task);
|
|
126
|
+
if (includeSection) {
|
|
127
|
+
filtered.push(line);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (includeSection) {
|
|
131
|
+
// Include content from relevant sections
|
|
132
|
+
filtered.push(line);
|
|
133
|
+
}
|
|
134
|
+
else if (line.startsWith('#') || line.trim() === '') {
|
|
135
|
+
// Always include top-level headers and blank lines
|
|
136
|
+
filtered.push(line);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return filtered.join('\n');
|
|
140
|
+
}
|
|
141
|
+
// ══════════════════════════════════════════════════════════════
|
|
142
|
+
// Main Tool Function
|
|
143
|
+
// ══════════════════════════════════════════════════════════════
|
|
144
|
+
export async function preflightCheck(input) {
|
|
145
|
+
const { repo_path, task } = input;
|
|
146
|
+
const expandedPath = expandPath(repo_path);
|
|
147
|
+
const apiKey = process.env.AGENTBRAIN_API_KEY;
|
|
148
|
+
let output = '# Preflight Check ✓\n\n';
|
|
149
|
+
const errors = [];
|
|
150
|
+
// ── Parallel fetch all sources ────────────────────────────────
|
|
151
|
+
const results = await Promise.allSettled([
|
|
152
|
+
// Load slim context summary
|
|
153
|
+
loadContext({ repo_path, full: false }),
|
|
154
|
+
// Load workspace documents
|
|
155
|
+
apiKey ? loadBlueprint({ repo_path }) : Promise.resolve(null),
|
|
156
|
+
apiKey ? loadGuardrails({ repo_path }) : Promise.resolve(null),
|
|
157
|
+
apiKey ? loadStandards({ repo_path, agent: 'claude-code' }) : Promise.resolve(null),
|
|
158
|
+
apiKey ? loadDecisions({ repo_path }) : Promise.resolve(null),
|
|
159
|
+
// Load active spec
|
|
160
|
+
apiKey ? fetchActiveSpec(apiKey) : Promise.resolve(null),
|
|
161
|
+
]);
|
|
162
|
+
// Extract results
|
|
163
|
+
const [contextResult, blueprintResult, guardrailsResult, standardsResult, decisionsResult, specResult] = results;
|
|
164
|
+
const context = contextResult.status === 'fulfilled' ? contextResult.value.content : null;
|
|
165
|
+
const blueprint = blueprintResult.status === 'fulfilled' ? blueprintResult.value : null;
|
|
166
|
+
const guardrails = guardrailsResult.status === 'fulfilled' ? guardrailsResult.value : null;
|
|
167
|
+
const standards = standardsResult.status === 'fulfilled' ? standardsResult.value?.content : null;
|
|
168
|
+
const decisions = decisionsResult.status === 'fulfilled' ? decisionsResult.value : null;
|
|
169
|
+
const spec = specResult.status === 'fulfilled' ? specResult.value : null;
|
|
170
|
+
// Track errors
|
|
171
|
+
if (contextResult.status === 'rejected')
|
|
172
|
+
errors.push(`Context: ${contextResult.reason}`);
|
|
173
|
+
if (blueprintResult.status === 'rejected')
|
|
174
|
+
errors.push(`Blueprint: ${blueprintResult.reason}`);
|
|
175
|
+
if (guardrailsResult.status === 'rejected')
|
|
176
|
+
errors.push(`Guardrails: ${guardrailsResult.reason}`);
|
|
177
|
+
if (standardsResult.status === 'rejected')
|
|
178
|
+
errors.push(`Standards: ${standardsResult.reason}`);
|
|
179
|
+
if (decisionsResult.status === 'rejected')
|
|
180
|
+
errors.push(`Decisions: ${decisionsResult.reason}`);
|
|
181
|
+
// ── Build output ───────────────────────────────────────────────
|
|
182
|
+
// Active Spec
|
|
183
|
+
if (spec) {
|
|
184
|
+
output += '## Active Spec\n';
|
|
185
|
+
output += spec + '\n\n';
|
|
186
|
+
}
|
|
187
|
+
else if (apiKey) {
|
|
188
|
+
output += '## Active Spec\n';
|
|
189
|
+
output += 'No active spec. Create one at useagentbrain.com/specs\n\n';
|
|
190
|
+
}
|
|
191
|
+
// Codebase Context
|
|
192
|
+
if (context) {
|
|
193
|
+
// Extract branch info
|
|
194
|
+
const contextLines = context.split('\n');
|
|
195
|
+
const branchLineIndex = contextLines.findIndex((l) => l.includes('## Current Branch'));
|
|
196
|
+
const branch = branchLineIndex >= 0 ? contextLines[branchLineIndex + 1]?.trim() : 'unknown';
|
|
197
|
+
output += '## Codebase Context\n';
|
|
198
|
+
output += `Branch: ${branch}\n`;
|
|
199
|
+
// Extract 2-3 line summary from context
|
|
200
|
+
const lines = context.split('\n').filter((l) => l.trim() && !l.startsWith('#'));
|
|
201
|
+
const summary = lines.slice(0, 3).join('\n');
|
|
202
|
+
if (summary) {
|
|
203
|
+
output += summary + '\n';
|
|
204
|
+
}
|
|
205
|
+
output += '\n';
|
|
206
|
+
}
|
|
207
|
+
// How We Build Here (Blueprint)
|
|
208
|
+
if (blueprint && !blueprint.includes('requires an API key') && !blueprint.includes('no content yet')) {
|
|
209
|
+
output += '## How We Build Here\n';
|
|
210
|
+
const filtered = task ? filterContent(blueprint, task) : blueprint;
|
|
211
|
+
// Remove the top-level title
|
|
212
|
+
const content = filtered.replace(/^# Blueprint.*\n\n/, '');
|
|
213
|
+
output += content + '\n';
|
|
214
|
+
}
|
|
215
|
+
// Guardrails for This Task
|
|
216
|
+
if (guardrails && !guardrails.includes('requires an API key') && !guardrails.includes('no content yet')) {
|
|
217
|
+
const isRefactor = task?.toLowerCase().includes('refactor');
|
|
218
|
+
output += isRefactor ? '## Guardrails (Refactoring)\n' : '## Guardrails for This Task\n';
|
|
219
|
+
const filtered = task ? filterContent(guardrails, task) : guardrails;
|
|
220
|
+
// Remove the top-level title
|
|
221
|
+
const content = filtered.replace(/^# Guardrails.*\n\n/, '');
|
|
222
|
+
output += content + '\n';
|
|
223
|
+
}
|
|
224
|
+
// Decisions to Know
|
|
225
|
+
if (decisions && !decisions.includes('requires an API key') && !decisions.includes('No Decisions found')) {
|
|
226
|
+
output += '## Decisions to Know\n';
|
|
227
|
+
const filtered = task ? filterContent(decisions, task) : decisions;
|
|
228
|
+
// Remove the top-level title
|
|
229
|
+
const content = filtered.replace(/^# Decisions.*\n\n/, '');
|
|
230
|
+
output += content + '\n';
|
|
231
|
+
}
|
|
232
|
+
// Standards (emphasize for test/PR/review tasks)
|
|
233
|
+
const isTestOrPR = task?.toLowerCase().match(/test|pr|review|done/);
|
|
234
|
+
if (standards && isTestOrPR) {
|
|
235
|
+
output += '## Standards & Definition of Done\n';
|
|
236
|
+
const filtered = task ? filterContent(standards, task) : standards;
|
|
237
|
+
// Remove the top-level title
|
|
238
|
+
const content = filtered.replace(/^# Standards.*\n\n/, '');
|
|
239
|
+
output += content + '\n';
|
|
240
|
+
}
|
|
241
|
+
else if (standards) {
|
|
242
|
+
// Extract just Definition of Done for other tasks
|
|
243
|
+
const dodMatch = standards.match(/## Definition of Done\n([\s\S]*?)(?=\n##|\n$|$)/);
|
|
244
|
+
if (dodMatch) {
|
|
245
|
+
output += '## Definition of Done\n';
|
|
246
|
+
output += dodMatch[1].trim() + '\n\n';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// ── Footer ─────────────────────────────────────────────────────
|
|
250
|
+
// No API key message
|
|
251
|
+
if (!apiKey) {
|
|
252
|
+
output += '\n---\n\n';
|
|
253
|
+
output +=
|
|
254
|
+
'💡 Add AGENTBRAIN_API_KEY to your MCP config to load Blueprint, Guardrails, Standards, and Decisions.\n';
|
|
255
|
+
output += 'Get your key at useagentbrain.com/settings\n\n';
|
|
256
|
+
}
|
|
257
|
+
// No Playbook message
|
|
258
|
+
if (apiKey &&
|
|
259
|
+
blueprint?.includes('no content yet') &&
|
|
260
|
+
guardrails?.includes('no content yet') &&
|
|
261
|
+
decisions?.includes('No Decisions found')) {
|
|
262
|
+
output += '\n---\n\n';
|
|
263
|
+
output += '💡 No Playbook found for this workspace.\n';
|
|
264
|
+
output += 'Create one at useagentbrain.com/playbook to give your agent architectural guidance.\n\n';
|
|
265
|
+
}
|
|
266
|
+
// Errors
|
|
267
|
+
if (errors.length > 0) {
|
|
268
|
+
output += '\n---\n\n';
|
|
269
|
+
output += '⚠️ Some sources failed to load:\n';
|
|
270
|
+
for (const error of errors) {
|
|
271
|
+
output += `- ${error}\n`;
|
|
272
|
+
}
|
|
273
|
+
output += '\n';
|
|
274
|
+
}
|
|
275
|
+
output += '---\n';
|
|
276
|
+
output += 'Ready. Proceed with task.\n';
|
|
277
|
+
return output;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=preflight-check.js.map
|