@avesta-hq/prevention 0.6.0-pre.13 → 0.6.0-pre.14

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.
@@ -99,7 +99,7 @@ process.stdin.on("end", () => {
99
99
 
100
100
  // Gate progress
101
101
  const GATE_ORDER = [
102
- "vision_approved", "plan_approved", "atdd_approved",
102
+ "vision_approved", "shape_approved", "plan_approved", "atdd_approved",
103
103
  "characterization_complete", "tdd_complete",
104
104
  "driver_complete", "review_approved",
105
105
  ];
@@ -1,9 +1,9 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { isInsideNodeModules, escapeRegExp } = require('./utils');
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { isInsideNodeModules, escapeRegExp } = require("./utils");
4
4
 
5
- const PREVENTION_SECTION_START = '<!-- prevention:start -->';
6
- const PREVENTION_SECTION_END = '<!-- prevention:end -->';
5
+ const PREVENTION_SECTION_START = "<!-- prevention:start -->";
6
+ const PREVENTION_SECTION_END = "<!-- prevention:end -->";
7
7
 
8
8
  const PREVENTION_CLAUDE_MD_SECTION = `${PREVENTION_SECTION_START}
9
9
  ## Prevention — MCP Server Integration
@@ -36,23 +36,23 @@ ${PREVENTION_SECTION_END}`;
36
36
  // ── Helpers ───────────────────────────────────────────────────────
37
37
 
38
38
  function readSettings(targetDir) {
39
- const settingsPath = path.join(targetDir, '.claude', 'settings.json');
39
+ const settingsPath = path.join(targetDir, ".claude", "settings.json");
40
40
  if (!fs.existsSync(settingsPath)) return {};
41
41
  try {
42
- return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
42
+ return JSON.parse(fs.readFileSync(settingsPath, "utf8"));
43
43
  } catch {
44
44
  return {};
45
45
  }
46
46
  }
47
47
 
48
48
  function writeSettings(targetDir, settings) {
49
- const claudeDir = path.join(targetDir, '.claude');
49
+ const claudeDir = path.join(targetDir, ".claude");
50
50
  if (!fs.existsSync(claudeDir)) {
51
51
  fs.mkdirSync(claudeDir, { recursive: true });
52
52
  }
53
53
  fs.writeFileSync(
54
- path.join(claudeDir, 'settings.json'),
55
- JSON.stringify(settings, null, 2) + '\n'
54
+ path.join(claudeDir, "settings.json"),
55
+ JSON.stringify(settings, null, 2) + "\n",
56
56
  );
57
57
  }
58
58
 
@@ -60,26 +60,29 @@ function findCliCommand(subcommand) {
60
60
  if (isInsideNodeModules()) {
61
61
  return `npx @avesta-hq/prevention ${subcommand}`;
62
62
  }
63
- return `node ${path.resolve(__dirname, '..', 'cli.js')} ${subcommand}`;
63
+ return `node ${path.resolve(__dirname, "..", "cli.js")} ${subcommand}`;
64
64
  }
65
65
 
66
66
  // ── Configurators ─────────────────────────────────────────────────
67
67
 
68
68
  function isPreventionHook(h) {
69
- return h.command && (h.command.includes('prevention') || h.command.includes('avesta'));
69
+ return (
70
+ h.command &&
71
+ (h.command.includes("prevention") || h.command.includes("avesta"))
72
+ );
70
73
  }
71
74
 
72
75
  function configureSessionStartHook(targetDir) {
73
76
  const settings = readSettings(targetDir);
74
77
  if (!settings.hooks) settings.hooks = {};
75
78
 
76
- const command = findCliCommand('session-start');
79
+ const command = findCliCommand("session-start");
77
80
 
78
81
  // Remove all existing prevention SessionStart hooks, then add one fresh
79
- const nonPrevention = (settings.hooks.SessionStart || []).filter(entry =>
80
- !(entry.hooks && entry.hooks.some(isPreventionHook))
82
+ const nonPrevention = (settings.hooks.SessionStart || []).filter(
83
+ (entry) => !(entry.hooks && entry.hooks.some(isPreventionHook)),
81
84
  );
82
- nonPrevention.push({ matcher: '', hooks: [{ type: 'command', command }] });
85
+ nonPrevention.push({ matcher: "", hooks: [{ type: "command", command }] });
83
86
  settings.hooks.SessionStart = nonPrevention;
84
87
 
85
88
  writeSettings(targetDir, settings);
@@ -89,36 +92,47 @@ function configureEnforcementHooks(targetDir) {
89
92
  const settings = readSettings(targetDir);
90
93
  if (!settings.hooks) settings.hooks = {};
91
94
 
92
- const preToolUseCmd = findCliCommand('hook pre-tool-use');
93
- const promptSubmitCmd = findCliCommand('hook prompt-submit');
94
- const subagentStartCmd = findCliCommand('hook subagent-start');
95
+ const preToolUseCmd = findCliCommand("hook pre-tool-use");
96
+ const promptSubmitCmd = findCliCommand("hook prompt-submit");
97
+ const subagentStartCmd = findCliCommand("hook subagent-start");
95
98
 
96
99
  // PreToolUse: remove all prevention hooks, add fresh ones
97
- const preToolUse = (settings.hooks.PreToolUse || []).filter(entry =>
98
- !(entry.hooks && entry.hooks.some(isPreventionHook))
100
+ const preToolUse = (settings.hooks.PreToolUse || []).filter(
101
+ (entry) => !(entry.hooks && entry.hooks.some(isPreventionHook)),
99
102
  );
100
103
  preToolUse.push(
101
- { matcher: 'Edit|Write', hooks: [{ type: 'command', command: preToolUseCmd }] },
102
- { matcher: 'Bash', hooks: [{ type: 'command', command: preToolUseCmd }] },
104
+ {
105
+ matcher: "Edit|Write",
106
+ hooks: [{ type: "command", command: preToolUseCmd }],
107
+ },
108
+ { matcher: "Bash", hooks: [{ type: "command", command: preToolUseCmd }] },
103
109
  );
104
110
  settings.hooks.PreToolUse = preToolUse;
105
111
 
106
112
  // UserPromptSubmit: remove all prevention hooks, add one fresh
107
- const promptHooks = (settings.hooks.UserPromptSubmit || []).filter(entry =>
108
- !(entry.hooks && entry.hooks.some(isPreventionHook))
113
+ const promptHooks = (settings.hooks.UserPromptSubmit || []).filter(
114
+ (entry) => !(entry.hooks && entry.hooks.some(isPreventionHook)),
109
115
  );
110
- promptHooks.push({ matcher: '', hooks: [{ type: 'command', command: promptSubmitCmd }] });
116
+ promptHooks.push({
117
+ matcher: "",
118
+ hooks: [{ type: "command", command: promptSubmitCmd }],
119
+ });
111
120
  settings.hooks.UserPromptSubmit = promptHooks;
112
121
 
113
122
  // SubagentStart: remove all prevention hooks, add one fresh
114
- const subagentHooks = (settings.hooks.SubagentStart || []).filter(entry =>
115
- !(entry.hooks && entry.hooks.some(isPreventionHook))
123
+ const subagentHooks = (settings.hooks.SubagentStart || []).filter(
124
+ (entry) => !(entry.hooks && entry.hooks.some(isPreventionHook)),
116
125
  );
117
- subagentHooks.push({ matcher: '', hooks: [{ type: 'command', command: subagentStartCmd }] });
126
+ subagentHooks.push({
127
+ matcher: "",
128
+ hooks: [{ type: "command", command: subagentStartCmd }],
129
+ });
118
130
  settings.hooks.SubagentStart = subagentHooks;
119
131
 
120
132
  writeSettings(targetDir, settings);
121
- console.log(' ✓ Configured workflow enforcement hooks (PreToolUse, UserPromptSubmit, SubagentStart)');
133
+ console.log(
134
+ " ✓ Configured workflow enforcement hooks (PreToolUse, UserPromptSubmit, SubagentStart)",
135
+ );
122
136
  }
123
137
 
124
138
  function configurePermissions(targetDir) {
@@ -126,7 +140,7 @@ function configurePermissions(targetDir) {
126
140
  if (!settings.permissions) settings.permissions = {};
127
141
  if (!settings.permissions.allow) settings.permissions.allow = [];
128
142
 
129
- const rule = 'mcp__prevention__*';
143
+ const rule = "mcp__prevention__*";
130
144
  if (!settings.permissions.allow.includes(rule)) {
131
145
  settings.permissions.allow.push(rule);
132
146
  }
@@ -135,19 +149,23 @@ function configurePermissions(targetDir) {
135
149
  }
136
150
 
137
151
  function configureStatusLine(targetDir) {
138
- const statusLineSrc = path.join(targetDir, '.avesta', 'statusLine.js');
152
+ const statusLineSrc = path.join(targetDir, ".avesta", "statusLine.cjs");
139
153
  if (!fs.existsSync(statusLineSrc)) return;
140
154
 
141
155
  const settings = readSettings(targetDir);
142
- settings.statusLine = { type: 'command', command: `node ${statusLineSrc}` };
156
+ settings.statusLine = { type: "command", command: `node ${statusLineSrc}` };
143
157
  writeSettings(targetDir, settings);
144
158
  }
145
159
 
146
160
  function configureMcpServer(targetDir, binaryPath, options = {}) {
147
- const mcpJsonPath = path.join(targetDir, '.mcp.json');
161
+ const mcpJsonPath = path.join(targetDir, ".mcp.json");
148
162
  let mcpConfig = {};
149
163
  if (fs.existsSync(mcpJsonPath)) {
150
- try { mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8')); } catch { mcpConfig = {}; }
164
+ try {
165
+ mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, "utf8"));
166
+ } catch {
167
+ mcpConfig = {};
168
+ }
151
169
  }
152
170
 
153
171
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
@@ -155,36 +173,42 @@ function configureMcpServer(targetDir, binaryPath, options = {}) {
155
173
  // Local MCP server: workflow orchestration tools
156
174
  // Pass through API/MCP URLs so the server process can reach them
157
175
  const env = {};
158
- if (process.env.AVESTA_API_URL) env.AVESTA_API_URL = process.env.AVESTA_API_URL;
159
- if (process.env.AVESTA_MCP_URL) env.AVESTA_MCP_URL = process.env.AVESTA_MCP_URL;
176
+ if (process.env.AVESTA_API_URL)
177
+ env.AVESTA_API_URL = process.env.AVESTA_API_URL;
178
+ if (process.env.AVESTA_MCP_URL)
179
+ env.AVESTA_MCP_URL = process.env.AVESTA_MCP_URL;
160
180
  const hasEnv = Object.keys(env).length > 0;
161
181
 
162
182
  if (options.localSource) {
163
183
  // Dev mode: run from source via tsx
164
- const entry = { command: 'npx', args: ['tsx', options.localSource] };
184
+ const entry = { command: "npx", args: ["tsx", options.localSource] };
165
185
  if (hasEnv) entry.env = env;
166
- mcpConfig.mcpServers['prevention'] = entry;
186
+ mcpConfig.mcpServers["prevention"] = entry;
167
187
  } else if (binaryPath) {
168
188
  const entry = { command: path.resolve(binaryPath) };
169
189
  if (hasEnv) entry.env = env;
170
- mcpConfig.mcpServers['prevention'] = entry;
190
+ mcpConfig.mcpServers["prevention"] = entry;
171
191
  } else {
172
- const entry = { command: 'npx', args: ['@avesta-hq/prevention', 'serve'] };
192
+ const entry = { command: "npx", args: ["@avesta-hq/prevention", "serve"] };
173
193
  if (hasEnv) entry.env = env;
174
- mcpConfig.mcpServers['prevention'] = entry;
194
+ mcpConfig.mcpServers["prevention"] = entry;
175
195
  }
176
196
 
177
197
  // Remote MCP server: content delivery (skills, prompts, catalog)
178
198
  // Pre-populate auth token from existing session if available
179
- const remoteUrl = process.env.AVESTA_MCP_URL || 'https://prevention-production.up.railway.app/mcp';
180
- const existingRemote = mcpConfig.mcpServers['prevention-content'];
199
+ const remoteUrl = process.env.AVESTA_MCP_URL || "http://localhost:3000/mcp";
200
+ const existingRemote = mcpConfig.mcpServers["prevention-content"];
181
201
  const existingToken = existingRemote?.headers?.Authorization;
182
- let authToken = existingToken || '';
202
+ let authToken = existingToken || "";
183
203
  if (!authToken) {
184
204
  try {
185
- const sessionPath = path.join(require('os').homedir(), '.avesta', 'session.json');
205
+ const sessionPath = path.join(
206
+ require("os").homedir(),
207
+ ".avesta",
208
+ "session.json",
209
+ );
186
210
  if (fs.existsSync(sessionPath)) {
187
- const session = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
211
+ const session = JSON.parse(fs.readFileSync(sessionPath, "utf8"));
188
212
  if (session.access_token) {
189
213
  authToken = `Bearer ${session.access_token}`;
190
214
  }
@@ -192,38 +216,53 @@ function configureMcpServer(targetDir, binaryPath, options = {}) {
192
216
  } catch {}
193
217
  }
194
218
  const headers = authToken ? { Authorization: authToken } : {};
195
- mcpConfig.mcpServers['prevention-content'] = {
196
- type: 'http',
219
+ mcpConfig.mcpServers["prevention-content"] = {
220
+ type: "http",
197
221
  url: remoteUrl,
198
222
  headers,
199
223
  };
200
224
 
201
- fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
225
+ fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n");
202
226
  }
203
227
 
204
228
  function ensureClaudeMdSection(targetDir) {
205
- const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
229
+ const claudeMdPath = path.join(targetDir, "CLAUDE.md");
206
230
 
207
231
  if (!fs.existsSync(claudeMdPath)) {
208
- fs.writeFileSync(claudeMdPath, PREVENTION_CLAUDE_MD_SECTION + '\n', 'utf-8');
209
- console.log(' ✓ Created CLAUDE.md with Prevention section');
232
+ fs.writeFileSync(
233
+ claudeMdPath,
234
+ PREVENTION_CLAUDE_MD_SECTION + "\n",
235
+ "utf-8",
236
+ );
237
+ console.log(" ✓ Created CLAUDE.md with Prevention section");
210
238
  return;
211
239
  }
212
240
 
213
- const content = fs.readFileSync(claudeMdPath, 'utf-8');
241
+ const content = fs.readFileSync(claudeMdPath, "utf-8");
214
242
 
215
243
  if (content.includes(PREVENTION_SECTION_START)) {
216
244
  const regex = new RegExp(
217
- escapeRegExp(PREVENTION_SECTION_START) + '[\\s\\S]*?' + escapeRegExp(PREVENTION_SECTION_END), 'm'
245
+ escapeRegExp(PREVENTION_SECTION_START) +
246
+ "[\\s\\S]*?" +
247
+ escapeRegExp(PREVENTION_SECTION_END),
248
+ "m",
218
249
  );
219
- fs.writeFileSync(claudeMdPath, content.replace(regex, PREVENTION_CLAUDE_MD_SECTION), 'utf-8');
220
- console.log(' ✓ Updated Prevention section in CLAUDE.md');
250
+ fs.writeFileSync(
251
+ claudeMdPath,
252
+ content.replace(regex, PREVENTION_CLAUDE_MD_SECTION),
253
+ "utf-8",
254
+ );
255
+ console.log(" ✓ Updated Prevention section in CLAUDE.md");
221
256
  return;
222
257
  }
223
258
 
224
- const separator = content.endsWith('\n') ? '\n' : '\n\n';
225
- fs.writeFileSync(claudeMdPath, content + separator + PREVENTION_CLAUDE_MD_SECTION + '\n', 'utf-8');
226
- console.log(' ✓ Added Prevention section to CLAUDE.md');
259
+ const separator = content.endsWith("\n") ? "\n" : "\n\n";
260
+ fs.writeFileSync(
261
+ claudeMdPath,
262
+ content + separator + PREVENTION_CLAUDE_MD_SECTION + "\n",
263
+ "utf-8",
264
+ );
265
+ console.log(" ✓ Added Prevention section to CLAUDE.md");
227
266
  }
228
267
 
229
268
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avesta-hq/prevention",
3
- "version": "0.6.0-pre.13",
3
+ "version": "0.6.0-pre.14",
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",