@avesta-hq/prevention 0.5.0 → 0.6.0-pre.4
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/CLAUDE.md +5 -2
- package/bin/lib/settings.js +44 -4
- package/bin/lib/test-local.js +28 -24
- package/bin/lib/utils.js +16 -9
- package/package.json +2 -9
package/CLAUDE.md
CHANGED
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
## MCP Server Integration
|
|
70
70
|
|
|
71
|
-
This project uses Prevention as
|
|
71
|
+
This project uses Prevention as two MCP servers: a local server (12 tools for workflow orchestration) and a remote server (3 tools for content delivery — skills, prompts, catalog).
|
|
72
72
|
|
|
73
73
|
**When starting work:**
|
|
74
74
|
|
|
@@ -76,10 +76,13 @@ This project uses Prevention as an MCP server with 15 tools, 26 agents, and 34 s
|
|
|
76
76
|
2. The workflow tracks your phase (vision → plan → atdd → tdd → review → ship)
|
|
77
77
|
3. Gates enforce that prerequisites are met before advancing
|
|
78
78
|
|
|
79
|
-
**Key tools:**
|
|
79
|
+
**Key tools (local):**
|
|
80
80
|
|
|
81
81
|
- `avesta_get_status` — Current phase, gates, next action
|
|
82
82
|
- `avesta_dispatch` — Route tasks to the right workflow step
|
|
83
|
+
|
|
84
|
+
**Key tools (remote — `prevention-content`):**
|
|
85
|
+
|
|
83
86
|
- `avesta_get_catalog` — All available commands and skills
|
|
84
87
|
- `avesta_get_skill` — Deep knowledge on any practice (testing, architecture, CI/CD)
|
|
85
88
|
|
package/bin/lib/settings.js
CHANGED
|
@@ -131,7 +131,7 @@ function configureStatusLine(targetDir) {
|
|
|
131
131
|
writeSettings(targetDir, settings);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function configureMcpServer(targetDir, binaryPath) {
|
|
134
|
+
function configureMcpServer(targetDir, binaryPath, options = {}) {
|
|
135
135
|
const mcpJsonPath = path.join(targetDir, '.mcp.json');
|
|
136
136
|
let mcpConfig = {};
|
|
137
137
|
if (fs.existsSync(mcpJsonPath)) {
|
|
@@ -140,12 +140,52 @@ function configureMcpServer(targetDir, binaryPath) {
|
|
|
140
140
|
|
|
141
141
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// Local MCP server: workflow orchestration tools
|
|
144
|
+
// Pass through API/MCP URLs so the server process can reach them
|
|
145
|
+
const env = {};
|
|
146
|
+
if (process.env.AVESTA_API_URL) env.AVESTA_API_URL = process.env.AVESTA_API_URL;
|
|
147
|
+
if (process.env.AVESTA_MCP_URL) env.AVESTA_MCP_URL = process.env.AVESTA_MCP_URL;
|
|
148
|
+
const hasEnv = Object.keys(env).length > 0;
|
|
149
|
+
|
|
150
|
+
if (options.localSource) {
|
|
151
|
+
// Dev mode: run from source via tsx
|
|
152
|
+
const entry = { command: 'npx', args: ['tsx', options.localSource] };
|
|
153
|
+
if (hasEnv) entry.env = env;
|
|
154
|
+
mcpConfig.mcpServers['prevention'] = entry;
|
|
155
|
+
} else if (binaryPath) {
|
|
156
|
+
const entry = { command: path.resolve(binaryPath) };
|
|
157
|
+
if (hasEnv) entry.env = env;
|
|
158
|
+
mcpConfig.mcpServers['prevention'] = entry;
|
|
145
159
|
} else {
|
|
146
|
-
|
|
160
|
+
const entry = { command: 'npx', args: ['@avesta-hq/prevention', 'serve'] };
|
|
161
|
+
if (hasEnv) entry.env = env;
|
|
162
|
+
mcpConfig.mcpServers['prevention'] = entry;
|
|
147
163
|
}
|
|
148
164
|
|
|
165
|
+
// Remote MCP server: content delivery (skills, prompts, catalog)
|
|
166
|
+
// Pre-populate auth token from existing session if available
|
|
167
|
+
const remoteUrl = process.env.AVESTA_MCP_URL || 'https://prevention-production.up.railway.app/mcp';
|
|
168
|
+
const existingRemote = mcpConfig.mcpServers['prevention-content'];
|
|
169
|
+
const existingToken = existingRemote?.headers?.Authorization;
|
|
170
|
+
let authToken = existingToken || '';
|
|
171
|
+
if (!authToken) {
|
|
172
|
+
try {
|
|
173
|
+
const sessionPath = path.join(require('os').homedir(), '.avesta', 'session.json');
|
|
174
|
+
if (fs.existsSync(sessionPath)) {
|
|
175
|
+
const session = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
|
|
176
|
+
if (session.access_token) {
|
|
177
|
+
authToken = `Bearer ${session.access_token}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
}
|
|
182
|
+
const headers = authToken ? { Authorization: authToken } : {};
|
|
183
|
+
mcpConfig.mcpServers['prevention-content'] = {
|
|
184
|
+
type: 'http',
|
|
185
|
+
url: remoteUrl,
|
|
186
|
+
headers,
|
|
187
|
+
};
|
|
188
|
+
|
|
149
189
|
fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
150
190
|
}
|
|
151
191
|
|
package/bin/lib/test-local.js
CHANGED
|
@@ -19,6 +19,7 @@ const path = require('path');
|
|
|
19
19
|
const { execSync } = require('child_process');
|
|
20
20
|
const { PACKAGE_ROOT, COMMANDS_DIR, AGENTS_DIR, AVESTA_DIR, copyDir, getVersion } = require('./utils');
|
|
21
21
|
const {
|
|
22
|
+
configureMcpServer,
|
|
22
23
|
configureSessionStartHook,
|
|
23
24
|
configureEnforcementHooks,
|
|
24
25
|
configureStatusLine,
|
|
@@ -34,7 +35,7 @@ function testLocal(targetDir) {
|
|
|
34
35
|
console.log(` Target: ${targetDir}\n`);
|
|
35
36
|
|
|
36
37
|
// Step 1: Rebuild embedded assets
|
|
37
|
-
console.log(' [1/
|
|
38
|
+
console.log(' [1/6] Rebuilding catalog + embedded assets...');
|
|
38
39
|
try {
|
|
39
40
|
execSync('pnpm run prebuild:assets', { cwd: cdAgentRoot, stdio: 'pipe' });
|
|
40
41
|
console.log(' ✓ Assets rebuilt');
|
|
@@ -45,7 +46,7 @@ function testLocal(targetDir) {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// Step 2: Copy agents + commands
|
|
48
|
-
console.log(' [2/
|
|
49
|
+
console.log(' [2/6] Copying agents and commands...');
|
|
49
50
|
const claudeDir = path.join(targetDir, '.claude');
|
|
50
51
|
if (!fs.existsSync(claudeDir)) fs.mkdirSync(claudeDir, { recursive: true });
|
|
51
52
|
|
|
@@ -62,40 +63,42 @@ function testLocal(targetDir) {
|
|
|
62
63
|
const agentCount = fs.existsSync(AGENTS_DIR) ? fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md')).length : 0;
|
|
63
64
|
console.log(` ✓ Copied ${commandCount} commands, ${agentCount} agents`);
|
|
64
65
|
|
|
65
|
-
// Step 3: Configure MCP
|
|
66
|
-
console.log(' [3/
|
|
67
|
-
const mcpJsonPath = path.join(targetDir, '.mcp.json');
|
|
68
|
-
let mcpConfig = {};
|
|
69
|
-
if (fs.existsSync(mcpJsonPath)) {
|
|
70
|
-
try { mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8')); } catch {}
|
|
71
|
-
}
|
|
72
|
-
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
73
|
-
|
|
66
|
+
// Step 3: Configure MCP servers (local source + remote content)
|
|
67
|
+
console.log(' [3/6] Configuring MCP servers (local source + remote content)...');
|
|
74
68
|
const mcpServerPath = path.join(cdAgentRoot, 'src', 'mcp-server.ts');
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
80
|
-
console.log(` ✓ MCP server → npx tsx ${mcpServerPath}`);
|
|
69
|
+
configureMcpServer(targetDir, null, { localSource: mcpServerPath });
|
|
70
|
+
console.log(` ✓ Local MCP server → npx tsx ${mcpServerPath}`);
|
|
71
|
+
const remoteUrl = process.env.AVESTA_MCP_URL || 'https://prevention-production.up.railway.app/mcp';
|
|
72
|
+
console.log(` ✓ Remote MCP server → ${remoteUrl}`);
|
|
81
73
|
|
|
82
74
|
// Step 4: Configure hooks (pointing at local CLI)
|
|
83
|
-
console.log(' [4/
|
|
75
|
+
console.log(' [4/6] Configuring hooks...');
|
|
84
76
|
configureSessionStartHook(targetDir);
|
|
85
77
|
configureEnforcementHooks(targetDir);
|
|
86
78
|
configureStatusLine(targetDir);
|
|
87
79
|
ensureClaudeMdSection(targetDir);
|
|
88
80
|
|
|
89
|
-
// Step 5:
|
|
90
|
-
console.log(' [5/
|
|
81
|
+
// Step 5: Write version
|
|
82
|
+
console.log(' [5/6] Writing version...');
|
|
83
|
+
const versionFile = path.join(targetDir, '.avesta', 'version.json');
|
|
84
|
+
const versionDir = path.dirname(versionFile);
|
|
85
|
+
if (!fs.existsSync(versionDir)) fs.mkdirSync(versionDir, { recursive: true });
|
|
86
|
+
fs.writeFileSync(versionFile, JSON.stringify({ version: getVersion(), updated_at: new Date().toISOString() }, null, 2));
|
|
87
|
+
console.log(` ✓ Version ${getVersion()}`);
|
|
88
|
+
|
|
89
|
+
// Step 6: Show result
|
|
90
|
+
console.log(' [6/6] Verifying setup...');
|
|
91
91
|
const settings = JSON.parse(fs.readFileSync(path.join(claudeDir, 'settings.json'), 'utf8'));
|
|
92
92
|
const hookTypes = Object.keys(settings.hooks || {});
|
|
93
93
|
console.log(` ✓ Hooks configured: ${hookTypes.join(', ')}`);
|
|
94
94
|
|
|
95
|
+
const mcpJsonPath = path.join(targetDir, '.mcp.json');
|
|
95
96
|
const mcpFinal = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8'));
|
|
96
97
|
const mcpCmd = mcpFinal.mcpServers?.prevention?.command || '?';
|
|
97
98
|
const mcpArgs = (mcpFinal.mcpServers?.prevention?.args || []).join(' ');
|
|
98
|
-
console.log(` ✓ MCP
|
|
99
|
+
console.log(` ✓ Local MCP: ${mcpCmd} ${mcpArgs}`);
|
|
100
|
+
const remoteEntry = mcpFinal.mcpServers?.['prevention-content'];
|
|
101
|
+
console.log(` ✓ Remote MCP: ${remoteEntry?.url || 'not configured'}`);
|
|
99
102
|
|
|
100
103
|
console.log(`
|
|
101
104
|
✅ Local test environment ready! (v${getVersion()})
|
|
@@ -105,9 +108,10 @@ function testLocal(targetDir) {
|
|
|
105
108
|
|
|
106
109
|
To test:
|
|
107
110
|
1. Open Claude Code in ${targetDir}
|
|
108
|
-
2. Run
|
|
109
|
-
3.
|
|
110
|
-
4. Test
|
|
111
|
+
2. Run avesta login to authenticate (connects remote content server)
|
|
112
|
+
3. Run /avesta-init backend (or frontend)
|
|
113
|
+
4. Test skill enforcement: /avesta-red should block writes until skills loaded
|
|
114
|
+
5. Test gate enforcement: git commit should be blocked without review gate
|
|
111
115
|
|
|
112
116
|
To revert to published version:
|
|
113
117
|
node ${path.join(cdAgentRoot, 'bin', 'cli.js')} update
|
package/bin/lib/utils.js
CHANGED
|
@@ -70,16 +70,23 @@ function getPlatformBinaryName() {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function getLatestVersion() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
// Use package version to match the corresponding GitHub Release
|
|
74
|
+
const pkgVersion = `v${getVersion()}`;
|
|
75
|
+
|
|
76
|
+
// For stable releases, verify the release exists via GitHub latest redirect
|
|
77
|
+
// (handles partial release failures where npm published but binary upload failed)
|
|
78
|
+
if (!pkgVersion.includes('-pre')) {
|
|
79
|
+
try {
|
|
80
|
+
const result = execSync(
|
|
81
|
+
`curl -sI "https://github.com/${GITHUB_RELEASES_REPO}/releases/latest" | grep -i ^location:`,
|
|
82
|
+
{ encoding: 'utf8', timeout: 10000 }
|
|
83
|
+
);
|
|
84
|
+
const match = result.match(/\/tag\/(v[^\s\r\n]+)/);
|
|
85
|
+
if (match) return match[1];
|
|
86
|
+
} catch {}
|
|
82
87
|
}
|
|
88
|
+
|
|
89
|
+
return pkgVersion;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
function downloadBinary(targetDir) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avesta-hq/prevention",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0-pre.4",
|
|
4
4
|
"description": "XP/CD development agent commands for Claude Code - achieve Elite DORA metrics through disciplined TDD and Continuous Delivery practices",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -16,14 +16,7 @@
|
|
|
16
16
|
],
|
|
17
17
|
"author": "Avesta Technologies",
|
|
18
18
|
"license": "UNLICENSED",
|
|
19
|
-
"
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/avesta-hq/prevention.git"
|
|
22
|
-
},
|
|
23
|
-
"homepage": "https://github.com/avesta-hq/prevention#readme",
|
|
24
|
-
"bugs": {
|
|
25
|
-
"url": "https://github.com/avesta-hq/prevention/issues"
|
|
26
|
-
},
|
|
19
|
+
"homepage": "https://avestahq.com/prevention",
|
|
27
20
|
"bin": {
|
|
28
21
|
"prevention": "./bin/cli.js"
|
|
29
22
|
},
|