@programinglive/dev-workflow-mcp-server 1.12.1 → 1.13.0

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.
Files changed (48) hide show
  1. package/.dockerignore +24 -0
  2. package/.env.example +11 -31
  3. package/README.md +109 -6
  4. package/db/adapters/base.js +12 -0
  5. package/db/adapters/mysql.js +11 -5
  6. package/db/adapters/postgres.js +10 -5
  7. package/db/adapters/sqlite.js +15 -7
  8. package/db/index.js +2 -1
  9. package/docker-compose.yml +7 -5
  10. package/exec.js +78 -12
  11. package/factory-client.js +142 -10
  12. package/index.js +28 -19
  13. package/install.js +6 -5
  14. package/package.json +4 -1
  15. package/release-wrapper.js +32 -19
  16. package/scripts/check-review-guard.mjs +33 -0
  17. package/scripts/create-admin.js +11 -9
  18. package/scripts/install-husky.js +3 -2
  19. package/scripts/lint.mjs +20 -0
  20. package/scripts/release.cjs +25 -22
  21. package/scripts/release.js +6 -5
  22. package/scripts/setup-stack.js +42 -41
  23. package/scripts/update-release-notes.cjs +13 -12
  24. package/scripts/update-release-notes.js +8 -7
  25. package/scripts/verify-bundle.mjs +8 -7
  26. package/security/audit.js +86 -0
  27. package/security/commands.js +10 -0
  28. package/security/config.js +24 -0
  29. package/security/files.js +27 -0
  30. package/security/http.js +26 -0
  31. package/security/log-bridge.cjs +17 -0
  32. package/security/policy.js +37 -0
  33. package/security/references.js +21 -0
  34. package/security/runtime-secrets.js +42 -0
  35. package/security/sanitizer.js +42 -0
  36. package/security/scanner.js +108 -0
  37. package/tools/definitions.js +51 -3
  38. package/tools/handler-modules/commit-handlers.js +3 -7
  39. package/tools/handler-modules/factory-handlers.js +84 -0
  40. package/tools/handler-modules/index.js +45 -3
  41. package/tools/handler-modules/shared.js +5 -2
  42. package/tools/handler-modules/status-handlers.js +1 -1
  43. package/tools/handler-modules/summary-handlers.js +2 -1
  44. package/tools/handler-modules/workflow-handlers.js +2 -0
  45. package/utils.js +1 -0
  46. package/verify-build.js +7 -6
  47. package/vite.config.js +4 -0
  48. package/workflow-state.js +109 -18
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
  import 'dotenv/config';
3
4
  import pg from 'pg';
4
5
  import { createUser } from '../web/lib/auth.js';
6
+ import { databaseConnectionUrl } from '../security/config.js';
5
7
 
6
8
  const { Pool } = pg;
7
9
 
8
10
  // Get database configuration from environment
9
- const dbUrl = process.env.DEV_WORKFLOW_DB_URL;
11
+ const dbUrl = databaseConnectionUrl();
10
12
 
11
13
  if (!dbUrl) {
12
- console.error('Error: DEV_WORKFLOW_DB_URL environment variable is not set');
14
+ safeLog('error', 'Error: DEV_WORKFLOW_DB_URL environment variable is not set');
13
15
  process.exit(1);
14
16
  }
15
17
 
@@ -17,7 +19,8 @@ const pool = new Pool({ connectionString: dbUrl });
17
19
 
18
20
  async function createAdminUser() {
19
21
  const username = process.env.ADMIN_USERNAME || 'admin';
20
- const password = process.env.ADMIN_PASSWORD || 'admin123';
22
+ const password = process.env.ADMIN_PASSWORD;
23
+ if(!password || password.length<16) throw new Error('Provision ADMIN_PASSWORD privately with at least 16 characters');
21
24
  const email = process.env.ADMIN_EMAIL || 'admin@example.com';
22
25
 
23
26
  try {
@@ -25,19 +28,18 @@ async function createAdminUser() {
25
28
  const existing = await pool.query('SELECT id FROM users WHERE username = $1', [username]);
26
29
 
27
30
  if (existing.rows.length > 0) {
28
- console.log(`✓ Admin user '${username}' already exists`);
31
+ safeLog('info', `✓ Admin user '${username}' already exists`);
29
32
  process.exit(0);
30
33
  }
31
34
 
32
35
  // Create admin user
33
36
  const user = await createUser(pool, username, password, email);
34
- console.log(`✓ Created admin user: ${user.username}`);
35
- console.log(` Email: ${user.email}`);
36
- console.log(` Password: ${password}`);
37
- console.log('\n⚠️ Please change the password after first login!');
37
+ safeLog('info', `✓ Created admin user: ${user.username}`);
38
+ safeLog('info', ` Email: ${user.email}`);
39
+ safeLog('info','Administrator created; credential value omitted');
38
40
 
39
41
  } catch (error) {
40
- console.error('Error creating admin user:', error.message);
42
+ safeLog('error', 'Error creating admin user:', error.message);
41
43
  process.exit(1);
42
44
  } finally {
43
45
  await pool.end();
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
 
3
4
  /**
4
5
  * Installs husky git hooks when running inside a git checkout, and does
@@ -19,7 +20,7 @@ import { fileURLToPath } from 'node:url';
19
20
  const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
20
21
 
21
22
  if (!existsSync(path.join(projectRoot, '.git'))) {
22
- console.log('Not a git checkout - skipping husky install.');
23
+ safeLog('info', 'Not a git checkout - skipping husky install.');
23
24
  process.exit(0);
24
25
  }
25
26
 
@@ -37,7 +38,7 @@ try {
37
38
  execSync(command, { cwd: projectRoot, stdio: 'inherit', shell: true });
38
39
  } catch (error) {
39
40
  // Never fail an install because git hooks could not be set up.
40
- console.warn(`Could not install husky hooks: ${error.message}`);
41
+ safeLog('warn', `Could not install husky hooks: ${error.message}`);
41
42
  }
42
43
 
43
44
  process.exit(0);
@@ -0,0 +1,20 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
4
+ import { safeLog } from '../security/sanitizer.js';
5
+
6
+ const skip=new Set(['node_modules','.git','.state','dist','out','.next','.superpowers']);
7
+ let checked=0;
8
+ async function walk(dir) {
9
+ for(const item of await fs.readdir(dir,{withFileTypes:true})) {
10
+ if(skip.has(item.name)||item.isSymbolicLink())continue;
11
+ const file=path.join(dir,item.name);
12
+ if(item.isDirectory()){await walk(file);continue;}
13
+ if(!/\.(?:js|mjs|cjs)$/.test(file))continue;
14
+ const result=spawnSync(process.execPath,['--check',file],{encoding:'utf8'});
15
+ if(result.status!==0){safeLog('error','JavaScript syntax check failed',file,result.stderr);process.exitCode=1;}
16
+ checked++;
17
+ }
18
+ }
19
+ await walk(process.cwd());
20
+ safeLog('info',`Checked ${checked} JavaScript source files. Dashboard semantic lint runs separately with npm run lint --prefix web.`);
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ const { ready, safeLog, flushLogs, assertSafeKnowledge, spawnLogged } = require('../security/log-bridge.cjs');
2
3
 
3
- const { spawnSync } = require('child_process');
4
+ const spawnSync = spawnLogged;
4
5
  const fs = require('fs');
5
6
  const path = require('path');
6
7
 
@@ -130,17 +131,22 @@ function runProjectTests({ spawn = spawnSync, env = process.env, cwd = process.c
130
131
  const scripts = packageJson && packageJson.scripts ? packageJson.scripts : {};
131
132
 
132
133
  if (!scripts.test) {
133
- console.log('ℹ️ Skipping tests: no "test" script detected in package.json');
134
+ safeLog('info', 'ℹ️ Skipping tests: no "test" script detected in package.json');
134
135
  return { status: 0 };
135
136
  }
136
137
 
137
138
  const packageManager = detectPackageManager({ env, cwd });
138
139
  const { command, args } = buildTestCommand(packageManager);
139
140
 
140
- console.log(`🧪 Running tests with ${packageManager} ${args.join(' ')}`.trim());
141
- return spawn(command, args, {
141
+ safeLog('info', `🧪 Running tests with ${packageManager} ${args.join(' ')}`.trim());
142
+ // Windows package-manager launchers are .cmd files, not native executables.
143
+ // Only the fixed, allowlisted manager and literal test subcommand enter cmd.
144
+ const executable = process.platform === 'win32' ? (env.ComSpec || 'cmd.exe') : command;
145
+ const testArgs = process.platform === 'win32' ? ['/d', '/s', '/c', `${command} test`] : args;
146
+ return spawn(executable, testArgs, {
142
147
  stdio: 'inherit',
143
- env
148
+ env,
149
+ cwd
144
150
  });
145
151
  }
146
152
 
@@ -166,9 +172,9 @@ function runRelease({
166
172
 
167
173
  ensurePrdPresence({ cwd, dependencies });
168
174
 
169
- const testResult = runProjectTests({ spawn, env });
170
- if (testResult && typeof testResult.status === 'number' && testResult.status !== 0) {
171
- return testResult;
175
+ const testResult = runProjectTests({ spawn, env, cwd });
176
+ if (!testResult || testResult.error || testResult.status !== 0) {
177
+ return { ...testResult, status: testResult?.status || 1 };
172
178
  }
173
179
 
174
180
  const standardVersionBin = require.resolve('standard-version/bin/cli.js');
@@ -179,8 +185,8 @@ function runRelease({
179
185
  cwd
180
186
  });
181
187
 
182
- if (releaseResult && typeof releaseResult.status === 'number' && releaseResult.status !== 0) {
183
- return releaseResult;
188
+ if (!releaseResult || releaseResult.error || releaseResult.status !== 0) {
189
+ return { ...releaseResult, status: releaseResult?.status || 1 };
184
190
  }
185
191
 
186
192
  const releaseNotesPath = dependencies.releaseNotesPath || path.join('docs', 'release-notes', 'RELEASE_NOTES.md');
@@ -190,28 +196,25 @@ function runRelease({
190
196
  try {
191
197
  notesUpdated = updateNotes();
192
198
  } catch (error) {
193
- console.warn(`⚠️ Skipping release notes update: ${error.message}`);
199
+ safeLog('warn', `⚠️ Skipping release notes update: ${error.message}`);
194
200
  }
195
201
 
196
202
  if (notesUpdated) {
197
203
  const gitAddResult = spawnSync('git', ['add', releaseNotesPath], { stdio: 'inherit', cwd });
198
204
  if (!gitAddResult || typeof gitAddResult.status !== 'number' || gitAddResult.status !== 0) {
199
- console.warn('⚠️ Release notes updated but failed to stage. Please add manually: git add ' + releaseNotesPath);
205
+ safeLog('warn', '⚠️ Release notes updated but failed to stage. Please add manually: git add ' + releaseNotesPath);
200
206
  }
201
207
  }
202
208
 
203
209
  return releaseResult;
204
210
  }
205
211
  if (require.main === module) {
206
- try {
207
- const result = runRelease();
208
- if (result.status !== 0) {
209
- process.exit(result.status ?? 1);
210
- }
211
- } catch (error) {
212
- console.error(`❌ ${error.message}`);
213
- process.exit(1);
214
- }
212
+ (async()=>{
213
+ let exitCode=0;
214
+ try {await ready;const result=runRelease();exitCode=result.status ?? 1;}
215
+ catch(error){safeLog('error',error);exitCode=1;}
216
+ await flushLogs();process.exitCode=exitCode;
217
+ })();
215
218
  }
216
219
 
217
220
  module.exports = {
@@ -244,7 +247,7 @@ function ensurePrdPresence({ cwd = process.cwd(), dependencies = {} } = {}) {
244
247
  return true;
245
248
  }
246
249
 
247
- const warn = typeof logger?.warn === 'function' ? logger.warn.bind(logger) : console.warn.bind(console);
250
+ const warn = typeof logger?.warn === 'function' ? logger.warn.bind(logger) : ((message)=>safeLog('warn',message));
248
251
  warn(`⚠️ Product Requirements Document missing (${prdDisplayPath}). Add ${DEFAULT_PRD_DISPLAY_PATH} so teams understand release expectations.`);
249
252
  return false;
250
253
  }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
 
3
4
  const { spawnSync } = require('child_process');
4
5
  const fs = require('fs');
@@ -120,14 +121,14 @@ function runProjectTests({ spawn = spawnSync, env = process.env, cwd = process.c
120
121
  const scripts = packageJson && packageJson.scripts ? packageJson.scripts : {};
121
122
 
122
123
  if (!scripts.test) {
123
- console.log('ℹ️ Skipping tests: no "test" script detected in package.json');
124
+ safeLog('info', 'ℹ️ Skipping tests: no "test" script detected in package.json');
124
125
  return { status: 0 };
125
126
  }
126
127
 
127
128
  const packageManager = detectPackageManager({ env, cwd });
128
129
  const { command, args } = buildTestCommand(packageManager);
129
130
 
130
- console.log(`🧪 Running tests with ${packageManager} ${args.join(' ')}`.trim());
131
+ safeLog('info', `🧪 Running tests with ${packageManager} ${args.join(' ')}`.trim());
131
132
  return spawn(command, args, {
132
133
  stdio: 'inherit',
133
134
  env
@@ -178,13 +179,13 @@ function runRelease({
178
179
  try {
179
180
  notesUpdated = updateNotes();
180
181
  } catch (error) {
181
- console.warn(`⚠️ Skipping release notes update: ${error.message}`);
182
+ safeLog('warn', `⚠️ Skipping release notes update: ${error.message}`);
182
183
  }
183
184
 
184
185
  if (notesUpdated) {
185
186
  const gitAddResult = spawnSync('git', ['add', releaseNotesPath], { stdio: 'inherit', cwd });
186
187
  if (!gitAddResult || typeof gitAddResult.status !== 'number' || gitAddResult.status !== 0) {
187
- console.warn('⚠️ Release notes updated but failed to stage. Please add manually: git add ' + releaseNotesPath);
188
+ safeLog('warn', '⚠️ Release notes updated but failed to stage. Please add manually: git add ' + releaseNotesPath);
188
189
  }
189
190
  }
190
191
 
@@ -198,7 +199,7 @@ if (require.main === module) {
198
199
  process.exit(result.status ?? 1);
199
200
  }
200
201
  } catch (error) {
201
- console.error(`❌ ${error.message}`);
202
+ safeLog('error', `❌ ${error.message}`);
202
203
  process.exit(1);
203
204
  }
204
205
  }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
 
3
4
  /**
4
5
  * Unified Setup Script for AI Agent Development Stack:
@@ -154,17 +155,17 @@ function backupConfig(configPath, configDir) {
154
155
  function ensureDependencies() {
155
156
  const nodeModules = path.join(projectRoot, 'node_modules');
156
157
  if (existsSync(nodeModules)) {
157
- console.log('✓ Dependencies already installed.');
158
+ safeLog('info', '✓ Dependencies already installed.');
158
159
  return true;
159
160
  }
160
161
 
161
- console.log('📥 Installing dependencies (required: the MCP server imports dotenv and others)...');
162
+ safeLog('info', '📥 Installing dependencies (required: the MCP server imports dotenv and others)...');
162
163
  try {
163
164
  execSync('npm install --no-audit --no-fund', { cwd: projectRoot, stdio: 'inherit' });
164
- console.log('✓ Dependencies installed.');
165
+ safeLog('info', '✓ Dependencies installed.');
165
166
  return true;
166
167
  } catch {
167
- console.error('✗ npm install failed. The MCP server cannot start without dependencies.');
168
+ safeLog('error', '✗ npm install failed. The MCP server cannot start without dependencies.');
168
169
  return false;
169
170
  }
170
171
  }
@@ -279,7 +280,7 @@ function verifyInstall(configPath) {
279
280
  function ensureAgentsMd(targetProjectRoot) {
280
281
  const agentsMdPath = path.join(targetProjectRoot, 'AGENTS.md');
281
282
  if (existsSync(agentsMdPath)) {
282
- console.log('✓ AGENTS.md already exists in project root.');
283
+ safeLog('info', '✓ AGENTS.md already exists in project root.');
283
284
  return;
284
285
  }
285
286
 
@@ -308,25 +309,25 @@ Before taking any action or answering user requests regarding code changes, chec
308
309
 
309
310
  try {
310
311
  writeFileSync(agentsMdPath, defaultAgentsMd, 'utf8');
311
- console.log(`✓ Created AGENTS.md rule enforcement at ${agentsMdPath}`);
312
+ safeLog('info', `✓ Created AGENTS.md rule enforcement at ${agentsMdPath}`);
312
313
  } catch (error) {
313
- console.warn('⚠️ Could not create AGENTS.md:', error.message);
314
+ safeLog('warn', '⚠️ Could not create AGENTS.md:', error.message);
314
315
  }
315
316
  }
316
317
 
317
318
  export async function setupStack() {
318
- console.log('🚀 Setting up Unified AI Agent Development Stack...\n');
319
+ safeLog('info', '🚀 Setting up Unified AI Agent Development Stack...\n');
319
320
 
320
321
  const configDir = resolveConfigDir({ probe: probeConfigDir });
321
322
  const opencodeJsonPath = path.join(configDir, 'opencode.json');
322
323
  const serverIndexPath = path.join(projectRoot, 'index.js');
323
324
 
324
- console.log(`✓ OpenCode config directory: ${configDir}`);
325
+ safeLog('info', `✓ OpenCode config directory: ${configDir}`);
325
326
 
326
327
  mkdirSync(path.join(configDir, 'plugins'), { recursive: true });
327
328
 
328
329
  const backup = backupConfig(opencodeJsonPath, configDir);
329
- if (backup) console.log(`✓ Backed up opencode.json → ${backup}`);
330
+ if (backup) safeLog('info', `✓ Backed up opencode.json → ${backup}`);
330
331
 
331
332
  const hasOpencodeCli = hasCommand('opencode');
332
333
  const command = ['node', serverIndexPath];
@@ -334,21 +335,21 @@ export async function setupStack() {
334
335
 
335
336
  if (hasOpencodeCli) {
336
337
  try {
337
- console.log('\n🔌 Registering Superpowers via OpenCode CLI...');
338
+ safeLog('info', '\n🔌 Registering Superpowers via OpenCode CLI...');
338
339
  execSync(`opencode plugin add "${SUPERPOWERS_SPEC}"`, { stdio: 'inherit' });
339
- console.log('✓ Superpowers plugin registered.');
340
+ safeLog('info', '✓ Superpowers plugin registered.');
340
341
 
341
- console.log('\n🔌 Registering dev-workflow MCP server via OpenCode CLI...');
342
+ safeLog('info', '\n🔌 Registering dev-workflow MCP server via OpenCode CLI...');
342
343
  execSync(`opencode mcp add ${MCP_SERVER_NAME} --global -- node "${serverIndexPath}"`, {
343
344
  stdio: 'inherit',
344
345
  });
345
- console.log('✓ dev-workflow MCP server registered.');
346
+ safeLog('info', '✓ dev-workflow MCP server registered.');
346
347
  usedCli = true;
347
348
  } catch (error) {
348
- console.warn(`⚠️ OpenCode CLI registration failed (${error.message}). Falling back to direct config edit.`);
349
+ safeLog('warn', `⚠️ OpenCode CLI registration failed (${error.message}). Falling back to direct config edit.`);
349
350
  }
350
351
  } else {
351
- console.warn('⚠️ `opencode` CLI not found on PATH. Falling back to direct config edit.');
352
+ safeLog('warn', '⚠️ `opencode` CLI not found on PATH. Falling back to direct config edit.');
352
353
  }
353
354
 
354
355
  if (!usedCli) {
@@ -356,7 +357,7 @@ export async function setupStack() {
356
357
  mergePluginEntry(config);
357
358
  mergeMcpServer(config, { name: MCP_SERVER_NAME, command });
358
359
  writeConfig(opencodeJsonPath, config);
359
- console.log(`✓ opencode.json updated directly at ${opencodeJsonPath}`);
360
+ safeLog('info', `✓ opencode.json updated directly at ${opencodeJsonPath}`);
360
361
  }
361
362
 
362
363
  // Whatever path we took, confirm the on-disk config is V2-shaped.
@@ -364,61 +365,61 @@ export async function setupStack() {
364
365
  mergePluginEntry(finalConfig);
365
366
  mergeMcpServer(finalConfig, { name: MCP_SERVER_NAME, command });
366
367
  writeConfig(opencodeJsonPath, finalConfig);
367
- console.log('✓ opencode.json verified (V2 `plugins` + `mcp.servers`).');
368
+ safeLog('info', '✓ opencode.json verified (V2 `plugins` + `mcp.servers`).');
368
369
 
369
- console.log('\n⚡ Checking RTK (Rust Token Killer)...');
370
+ safeLog('info', '\n⚡ Checking RTK (Rust Token Killer)...');
370
371
  if (hasCommand('rtk')) {
371
372
  try {
372
373
  const version = run('rtk --version').trim();
373
- console.log(`✓ Found ${version}`);
374
+ safeLog('info', `✓ Found ${version}`);
374
375
  execSync('rtk init -g --opencode --auto-patch --no-trust-filters', { stdio: 'inherit' });
375
- console.log('✓ RTK OpenCode plugin registered.');
376
+ safeLog('info', '✓ RTK OpenCode plugin registered.');
376
377
  } catch {
377
- console.warn('⚠️ Could not run `rtk init -g --opencode`. Run it manually.');
378
+ safeLog('warn', '⚠️ Could not run `rtk init -g --opencode`. Run it manually.');
378
379
  }
379
380
  } else {
380
- console.warn('⚠️ `rtk` not found on PATH. Install it, then re-run this script:');
381
- console.log(process.platform === 'win32' ? ' winget install rtk-ai.rtk' : ' brew install rtk');
381
+ safeLog('warn', '⚠️ `rtk` not found on PATH. Install it, then re-run this script:');
382
+ safeLog('info', process.platform === 'win32' ? ' winget install rtk-ai.rtk' : ' brew install rtk');
382
383
  }
383
384
 
384
385
  const depsOk = ensureDependencies();
385
386
 
386
- console.log('\n🔎 Smoke testing the MCP server...');
387
+ safeLog('info', '\n🔎 Smoke testing the MCP server...');
387
388
  const smoke = depsOk
388
389
  ? await smokeTestServer()
389
390
  : { ok: false, reason: 'skipped: dependencies missing', stderr: '' };
390
391
  if (smoke.ok) {
391
- console.log(`✓ MCP server healthy (${smoke.reason}).`);
392
+ safeLog('info', `✓ MCP server healthy (${smoke.reason}).`);
392
393
  } else {
393
- console.error(`✗ MCP server failed to start (${smoke.reason}).`);
394
- if (smoke.stderr) console.error(smoke.stderr.trim());
394
+ safeLog('error', `✗ MCP server failed to start (${smoke.reason}).`);
395
+ if (smoke.stderr) safeLog('error', smoke.stderr.trim());
395
396
  }
396
397
 
397
398
  ensureAgentsMd(process.env.INIT_CWD || process.cwd());
398
399
 
399
- console.log('\n🧪 Verifying installation...');
400
+ safeLog('info', '\n🧪 Verifying installation...');
400
401
  const checks = verifyInstall(opencodeJsonPath);
401
402
  let allOk = smoke.ok;
402
403
 
403
404
  for (const check of checks) {
404
- console.log(` ${check.ok ? '✓' : '✗'} ${check.check}`);
405
+ safeLog('info', ` ${check.ok ? '✓' : '✗'} ${check.check}`);
405
406
  if (!check.ok) {
406
407
  allOk = false;
407
- if (check.detail) console.log(` ${check.detail.split('\n')[0]}`);
408
+ if (check.detail) safeLog('info', ` ${check.detail.split('\n')[0]}`);
408
409
  }
409
410
  }
410
411
 
411
- console.log('\n============================================================');
412
+ safeLog('info', '\n============================================================');
412
413
  if (allOk) {
413
- console.log('🎉 UNIFIED AI AGENT DEVELOPMENT STACK INSTALLED SUCCESSFULLY!');
414
- console.log('============================================================');
415
- console.log('• Workflow Discipline (dev-workflow MCP): Active');
416
- console.log('• Agentic Skills (Superpowers): Registered');
417
- console.log('• Token Compression (RTK): Configured');
418
- console.log('\nRestart OpenCode to start developing with superpowers! 🚀');
414
+ safeLog('info', '🎉 UNIFIED AI AGENT DEVELOPMENT STACK INSTALLED SUCCESSFULLY!');
415
+ safeLog('info', '============================================================');
416
+ safeLog('info', '• Workflow Discipline (dev-workflow MCP): Active');
417
+ safeLog('info', '• Agentic Skills (Superpowers): Registered');
418
+ safeLog('info', '• Token Compression (RTK): Configured');
419
+ safeLog('info', '\nRestart OpenCode to start developing with superpowers! 🚀');
419
420
  } else {
420
- console.log('⚠️ SETUP INCOMPLETE - see the failed checks above.');
421
- console.log('============================================================');
421
+ safeLog('info', '⚠️ SETUP INCOMPLETE - see the failed checks above.');
422
+ safeLog('info', '============================================================');
422
423
  }
423
424
 
424
425
  return allOk;
@@ -430,7 +431,7 @@ if (isDirectRun) {
430
431
  setupStack()
431
432
  .then((ok) => process.exit(ok ? 0 : 1))
432
433
  .catch((error) => {
433
- console.error('Setup encountered an error:', error);
434
+ safeLog('error', 'Setup encountered an error:', error);
434
435
  process.exit(1);
435
436
  });
436
437
  }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ const { ready, safeLog, flushLogs, assertSafeKnowledge, spawnLogged } = require('../security/log-bridge.cjs');
2
3
 
3
4
  const fs = require('fs');
4
5
  const path = require('path');
@@ -12,32 +13,32 @@ function updateReleaseNotes({
12
13
  } = {}) {
13
14
  const releaseNotesPath = path.join(rootDir, releaseNotesRelativePath);
14
15
  if (!fs.existsSync(releaseNotesPath)) {
15
- console.log(`ℹ️ Release notes file not found at ${releaseNotesPath}; skipping update.`);
16
+ safeLog('info', `ℹ️ Release notes file not found at ${releaseNotesPath}; skipping update.`);
16
17
  return false;
17
18
  }
18
19
 
19
20
  const packageJsonPath = path.join(rootDir, packageJsonRelativePath);
20
21
  if (!fs.existsSync(packageJsonPath)) {
21
- console.log(`ℹ️ package.json not found at ${packageJsonPath}; skipping release notes update.`);
22
+ safeLog('info', `ℹ️ package.json not found at ${packageJsonPath}; skipping release notes update.`);
22
23
  return false;
23
24
  }
24
25
 
25
26
  const changelogPath = path.join(rootDir, changelogRelativePath);
26
27
  if (!fs.existsSync(changelogPath)) {
27
- console.log(`ℹ️ CHANGELOG not found at ${changelogPath}; skipping release notes update.`);
28
+ safeLog('info', `ℹ️ CHANGELOG not found at ${changelogPath}; skipping release notes update.`);
28
29
  return false;
29
30
  }
30
31
 
31
32
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
32
33
  const version = packageJson.version;
33
34
  if (!version) {
34
- console.log('ℹ️ package.json version is missing; skipping release notes update.');
35
+ safeLog('info', 'ℹ️ package.json version is missing; skipping release notes update.');
35
36
  return false;
36
37
  }
37
38
 
38
39
  const releaseNotesContent = fs.readFileSync(releaseNotesPath, 'utf8');
39
40
  if (releaseNotesContent.includes(`| ${version} `) || releaseNotesContent.includes(`## ${version}`)) {
40
- console.log(`ℹ️ Release notes already contain version ${version}; skipping.`);
41
+ safeLog('info', `ℹ️ Release notes already contain version ${version}; skipping.`);
41
42
  return false;
42
43
  }
43
44
 
@@ -60,8 +61,9 @@ function updateReleaseNotes({
60
61
  detailBullets
61
62
  });
62
63
 
64
+ assertSafeKnowledge(updatedContent);
63
65
  fs.writeFileSync(releaseNotesPath, updatedContent);
64
- console.log(`✅ Added release notes entry for version ${version}.`);
66
+ safeLog('info', `✅ Added release notes entry for version ${version}.`);
65
67
  return true;
66
68
  }
67
69
 
@@ -173,10 +175,9 @@ module.exports = {
173
175
  };
174
176
 
175
177
  if (require.main === module) {
176
- try {
177
- updateReleaseNotes();
178
- } catch (error) {
179
- console.error(`❌ Failed to update release notes: ${error.message}`);
180
- process.exit(1);
181
- }
178
+ (async()=>{
179
+ try {await ready;updateReleaseNotes();}
180
+ catch(error){safeLog('error',error);process.exitCode=1;}
181
+ await flushLogs();
182
+ })();
182
183
  }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
 
3
4
  const fs = require('fs');
4
5
  const path = require('path');
@@ -12,32 +13,32 @@ function updateReleaseNotes({
12
13
  } = {}) {
13
14
  const releaseNotesPath = path.join(rootDir, releaseNotesRelativePath);
14
15
  if (!fs.existsSync(releaseNotesPath)) {
15
- console.log(`ℹ️ Release notes file not found at ${releaseNotesPath}; skipping update.`);
16
+ safeLog('info', `ℹ️ Release notes file not found at ${releaseNotesPath}; skipping update.`);
16
17
  return false;
17
18
  }
18
19
 
19
20
  const packageJsonPath = path.join(rootDir, packageJsonRelativePath);
20
21
  if (!fs.existsSync(packageJsonPath)) {
21
- console.log(`ℹ️ package.json not found at ${packageJsonPath}; skipping release notes update.`);
22
+ safeLog('info', `ℹ️ package.json not found at ${packageJsonPath}; skipping release notes update.`);
22
23
  return false;
23
24
  }
24
25
 
25
26
  const changelogPath = path.join(rootDir, changelogRelativePath);
26
27
  if (!fs.existsSync(changelogPath)) {
27
- console.log(`ℹ️ CHANGELOG not found at ${changelogPath}; skipping release notes update.`);
28
+ safeLog('info', `ℹ️ CHANGELOG not found at ${changelogPath}; skipping release notes update.`);
28
29
  return false;
29
30
  }
30
31
 
31
32
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
32
33
  const version = packageJson.version;
33
34
  if (!version) {
34
- console.log('ℹ️ package.json version is missing; skipping release notes update.');
35
+ safeLog('info', 'ℹ️ package.json version is missing; skipping release notes update.');
35
36
  return false;
36
37
  }
37
38
 
38
39
  const releaseNotesContent = fs.readFileSync(releaseNotesPath, 'utf8');
39
40
  if (releaseNotesContent.includes(`| ${version} `) || releaseNotesContent.includes(`## ${version}`)) {
40
- console.log(`ℹ️ Release notes already contain version ${version}; skipping.`);
41
+ safeLog('info', `ℹ️ Release notes already contain version ${version}; skipping.`);
41
42
  return false;
42
43
  }
43
44
 
@@ -61,7 +62,7 @@ function updateReleaseNotes({
61
62
  });
62
63
 
63
64
  fs.writeFileSync(releaseNotesPath, updatedContent);
64
- console.log(`✅ Added release notes entry for version ${version}.`);
65
+ safeLog('info', `✅ Added release notes entry for version ${version}.`);
65
66
  return true;
66
67
  }
67
68
 
@@ -176,7 +177,7 @@ if (require.main === module) {
176
177
  try {
177
178
  updateReleaseNotes();
178
179
  } catch (error) {
179
- console.error(`❌ Failed to update release notes: ${error.message}`);
180
+ safeLog('error', `❌ Failed to update release notes: ${error.message}`);
180
181
  process.exit(1);
181
182
  }
182
183
  }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { safeLog } from '../security/sanitizer.js';
2
3
 
3
4
  /**
4
5
  * Verifies that the built bundle actually starts.
@@ -29,12 +30,12 @@ const STARTUP_PATTERN = /running on stdio|MCP Server starting/i;
29
30
  const TIMEOUT_MS = 15000;
30
31
 
31
32
  if (!existsSync(entry)) {
32
- console.error(`✖ Bundle not found: ${entry}`);
33
- console.error(' Run "npm run build:server" first.');
33
+ safeLog('error', `✖ Bundle not found: ${entry}`);
34
+ safeLog('error', ' Run "npm run build:server" first.');
34
35
  process.exit(1);
35
36
  }
36
37
 
37
- console.log(`Verifying ${path.relative(process.cwd(), entry)}`);
38
+ safeLog('info', `Verifying ${path.relative(process.cwd(), entry)}`);
38
39
 
39
40
  const child = spawn(process.execPath, [entry], {
40
41
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -63,13 +64,13 @@ function finish(ok, reason) {
63
64
 
64
65
  const output = `${stdout}${stderr}`;
65
66
  if (ok) {
66
- console.log(`✓ ${reason}`);
67
+ safeLog('info', `✓ ${reason}`);
67
68
  process.exit(0);
68
69
  }
69
70
 
70
- console.error(`✖ ${reason}`);
71
- console.error('--- bundle output ---');
72
- console.error(output.trim() || '(no output)');
71
+ safeLog('error', `✖ ${reason}`);
72
+ safeLog('error', '--- bundle output ---');
73
+ safeLog('error', output.trim() || '(no output)');
73
74
  process.exit(1);
74
75
  }
75
76