@programinglive/dev-workflow-mcp-server 1.12.0 → 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.
- package/.dockerignore +24 -0
- package/.env.example +11 -31
- package/README.md +109 -6
- package/db/adapters/base.js +12 -0
- package/db/adapters/mysql.js +11 -5
- package/db/adapters/postgres.js +10 -5
- package/db/adapters/sqlite.js +15 -7
- package/db/index.js +2 -1
- package/docker-compose.yml +7 -5
- package/exec.js +78 -12
- package/factory-client.js +142 -10
- package/index.js +28 -19
- package/install.js +6 -5
- package/package.json +4 -1
- package/release-wrapper.js +32 -19
- package/scripts/check-review-guard.mjs +33 -0
- package/scripts/create-admin.js +11 -9
- package/scripts/install-husky.js +3 -2
- package/scripts/lint.mjs +20 -0
- package/scripts/release.cjs +25 -22
- package/scripts/release.js +6 -5
- package/scripts/setup-stack.js +42 -41
- package/scripts/update-release-notes.cjs +13 -12
- package/scripts/update-release-notes.js +8 -7
- package/scripts/verify-bundle.mjs +8 -7
- package/security/audit.js +86 -0
- package/security/commands.js +10 -0
- package/security/config.js +24 -0
- package/security/files.js +27 -0
- package/security/http.js +26 -0
- package/security/log-bridge.cjs +17 -0
- package/security/policy.js +37 -0
- package/security/references.js +21 -0
- package/security/runtime-secrets.js +42 -0
- package/security/sanitizer.js +42 -0
- package/security/scanner.js +108 -0
- package/tools/definitions.js +51 -3
- package/tools/handler-modules/commit-handlers.js +3 -7
- package/tools/handler-modules/factory-handlers.js +84 -0
- package/tools/handler-modules/index.js +45 -3
- package/tools/handler-modules/shared.js +5 -2
- package/tools/handler-modules/status-handlers.js +1 -1
- package/tools/handler-modules/summary-handlers.js +2 -1
- package/tools/handler-modules/workflow-handlers.js +2 -0
- package/utils.js +1 -0
- package/verify-build.js +7 -6
- package/vite.config.js +4 -0
- package/workflow-state.js +142 -42
package/scripts/create-admin.js
CHANGED
|
@@ -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 =
|
|
11
|
+
const dbUrl = databaseConnectionUrl();
|
|
10
12
|
|
|
11
13
|
if (!dbUrl) {
|
|
12
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
42
|
+
safeLog('error', 'Error creating admin user:', error.message);
|
|
41
43
|
process.exit(1);
|
|
42
44
|
} finally {
|
|
43
45
|
await pool.end();
|
package/scripts/install-husky.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
41
|
+
safeLog('warn', `Could not install husky hooks: ${error.message}`);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
process.exit(0);
|
package/scripts/lint.mjs
ADDED
|
@@ -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.`);
|
package/scripts/release.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
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) :
|
|
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
|
}
|
package/scripts/release.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
202
|
+
safeLog('error', `❌ ${error.message}`);
|
|
202
203
|
process.exit(1);
|
|
203
204
|
}
|
|
204
205
|
}
|
package/scripts/setup-stack.js
CHANGED
|
@@ -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
|
-
|
|
158
|
+
safeLog('info', '✓ Dependencies already installed.');
|
|
158
159
|
return true;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
|
|
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
|
-
|
|
165
|
+
safeLog('info', '✓ Dependencies installed.');
|
|
165
166
|
return true;
|
|
166
167
|
} catch {
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
312
|
+
safeLog('info', `✓ Created AGENTS.md rule enforcement at ${agentsMdPath}`);
|
|
312
313
|
} catch (error) {
|
|
313
|
-
|
|
314
|
+
safeLog('warn', '⚠️ Could not create AGENTS.md:', error.message);
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
export async function setupStack() {
|
|
318
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
338
|
+
safeLog('info', '\n🔌 Registering Superpowers via OpenCode CLI...');
|
|
338
339
|
execSync(`opencode plugin add "${SUPERPOWERS_SPEC}"`, { stdio: 'inherit' });
|
|
339
|
-
|
|
340
|
+
safeLog('info', '✓ Superpowers plugin registered.');
|
|
340
341
|
|
|
341
|
-
|
|
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
|
-
|
|
346
|
+
safeLog('info', '✓ dev-workflow MCP server registered.');
|
|
346
347
|
usedCli = true;
|
|
347
348
|
} catch (error) {
|
|
348
|
-
|
|
349
|
+
safeLog('warn', `⚠️ OpenCode CLI registration failed (${error.message}). Falling back to direct config edit.`);
|
|
349
350
|
}
|
|
350
351
|
} else {
|
|
351
|
-
|
|
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
|
-
|
|
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
|
-
|
|
368
|
+
safeLog('info', '✓ opencode.json verified (V2 `plugins` + `mcp.servers`).');
|
|
368
369
|
|
|
369
|
-
|
|
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
|
-
|
|
374
|
+
safeLog('info', `✓ Found ${version}`);
|
|
374
375
|
execSync('rtk init -g --opencode --auto-patch --no-trust-filters', { stdio: 'inherit' });
|
|
375
|
-
|
|
376
|
+
safeLog('info', '✓ RTK OpenCode plugin registered.');
|
|
376
377
|
} catch {
|
|
377
|
-
|
|
378
|
+
safeLog('warn', '⚠️ Could not run `rtk init -g --opencode`. Run it manually.');
|
|
378
379
|
}
|
|
379
380
|
} else {
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
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
|
-
|
|
392
|
+
safeLog('info', `✓ MCP server healthy (${smoke.reason}).`);
|
|
392
393
|
} else {
|
|
393
|
-
|
|
394
|
-
if (smoke.stderr)
|
|
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
|
-
|
|
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
|
-
|
|
405
|
+
safeLog('info', ` ${check.ok ? '✓' : '✗'} ${check.check}`);
|
|
405
406
|
if (!check.ok) {
|
|
406
407
|
allOk = false;
|
|
407
|
-
if (check.detail)
|
|
408
|
+
if (check.detail) safeLog('info', ` ${check.detail.split('\n')[0]}`);
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
411
|
|
|
411
|
-
|
|
412
|
+
safeLog('info', '\n============================================================');
|
|
412
413
|
if (allOk) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
421
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
updateReleaseNotes();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
safeLog('info', `✓ ${reason}`);
|
|
67
68
|
process.exit(0);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|