fraim-framework 2.0.35 โ†’ 2.0.36

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 (51) hide show
  1. package/bin/fraim.js +52 -5
  2. package/dist/registry/scripts/cleanup-branch.js +62 -33
  3. package/dist/registry/scripts/generate-engagement-emails.js +119 -44
  4. package/dist/registry/scripts/newsletter-helpers.js +208 -268
  5. package/dist/registry/scripts/profile-server.js +387 -0
  6. package/dist/tests/test-chalk-regression.js +18 -2
  7. package/dist/tests/test-client-scripts-validation.js +133 -0
  8. package/dist/tests/test-prep-issue.js +1 -34
  9. package/dist/tests/test-script-location-independence.js +76 -28
  10. package/package.json +2 -2
  11. package/registry/agent-guardrails.md +62 -62
  12. package/registry/rules/communication.md +121 -121
  13. package/registry/rules/continuous-learning.md +54 -54
  14. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  15. package/registry/rules/software-development-lifecycle.md +104 -104
  16. package/registry/scripts/cleanup-branch.ts +341 -0
  17. package/registry/scripts/code-quality-check.sh +559 -559
  18. package/registry/scripts/detect-tautological-tests.sh +38 -38
  19. package/registry/scripts/generate-engagement-emails.ts +830 -0
  20. package/registry/scripts/markdown-to-pdf.js +7 -3
  21. package/registry/scripts/newsletter-helpers.ts +777 -0
  22. package/registry/scripts/prep-issue.sh +30 -61
  23. package/registry/scripts/profile-server.ts +424 -0
  24. package/registry/scripts/run-thank-you-workflow.ts +122 -0
  25. package/registry/scripts/send-newsletter-simple.ts +102 -0
  26. package/registry/scripts/send-thank-you-emails.ts +57 -0
  27. package/registry/scripts/validate-openapi-limits.ts +366 -366
  28. package/registry/scripts/validate-test-coverage.ts +280 -280
  29. package/registry/scripts/verify-pr-comments.sh +70 -70
  30. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  31. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  32. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  33. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  34. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  35. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  36. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  37. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  38. package/dist/registry/scripts/build-scripts-generator.js +0 -205
  39. package/dist/registry/scripts/fraim-config.js +0 -61
  40. package/dist/registry/scripts/generic-issues-api.js +0 -100
  41. package/dist/registry/scripts/openapi-generator.js +0 -664
  42. package/dist/registry/scripts/performance/profile-server.js +0 -390
  43. package/dist/test-utils.js +0 -96
  44. package/dist/tests/esm-compat.js +0 -11
  45. package/dist/tests/test-chalk-esm-issue.js +0 -159
  46. package/dist/tests/test-chalk-real-world.js +0 -265
  47. package/dist/tests/test-chalk-resolution-issue.js +0 -304
  48. package/dist/tests/test-fraim-install-chalk-issue.js +0 -254
  49. package/dist/tests/test-npm-resolution-diagnostic.js +0 -140
  50. package/registry/templates/marketing/STORYTELLING-TEMPLATE.md +0 -130
  51. package/registry/workflows/marketing/storytelling.md +0 -65
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env npx tsx
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const child_process_1 = require("child_process");
38
+ const fs_1 = require("fs");
39
+ const path_1 = require("path");
40
+ function loadClientConfig() {
41
+ const configPath = (0, path_1.join)(process.cwd(), '.fraim', 'config.json');
42
+ if (!(0, fs_1.existsSync)(configPath)) {
43
+ throw new Error('.fraim/config.json not found. Run fraim init first.');
44
+ }
45
+ return JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
46
+ }
47
+ function getEnvOr(keys, fallback) {
48
+ for (const key of keys) {
49
+ const value = process.env[key];
50
+ if (value && value.length)
51
+ return value;
52
+ }
53
+ return fallback;
54
+ }
55
+ function checkAzureCLI() {
56
+ try {
57
+ (0, child_process_1.execSync)('az --version', { stdio: 'pipe' });
58
+ return true;
59
+ }
60
+ catch (error) {
61
+ return false;
62
+ }
63
+ }
64
+ function checkAzureAuth() {
65
+ try {
66
+ (0, child_process_1.execSync)('az account show', { stdio: 'pipe' });
67
+ return true;
68
+ }
69
+ catch (error) {
70
+ return false;
71
+ }
72
+ }
73
+ async function getAppServicePlanMetrics(appName, resourceGroup) {
74
+ try {
75
+ console.log('\n--- โ˜๏ธ App Service Plan Metrics ---');
76
+ // Get App Service Plan ID
77
+ const planIdCmd = `az webapp show --name ${appName} --resource-group ${resourceGroup} --query "appServicePlanId" -o tsv`;
78
+ const planId = (0, child_process_1.execSync)(planIdCmd, { encoding: 'utf8' }).trim();
79
+ const planName = planId.split('/').pop();
80
+ console.log(`๐Ÿ“‹ Plan: ${planName}`);
81
+ // Get CPU and Memory metrics for last 5 minutes
82
+ console.log('๐Ÿ“Š Fetching CPU and Memory metrics (last 5 minutes)...');
83
+ const metricsCmd = `az monitor metrics list --resource "${planId}" --metrics CpuPercentage MemoryPercentage --interval PT1M --query "value[].{name:name.value, data:timeseries[0].data}" -o json`;
84
+ const metricsOutput = (0, child_process_1.execSync)(metricsCmd, { encoding: 'utf8' });
85
+ const metrics = JSON.parse(metricsOutput);
86
+ for (const metric of metrics) {
87
+ const latestData = metric.data[metric.data.length - 1];
88
+ const value = latestData?.average || latestData?.total || 'N/A';
89
+ console.log(` ${metric.name}: ${typeof value === 'number' ? value.toFixed(2) + '%' : value}`);
90
+ }
91
+ }
92
+ catch (error) {
93
+ console.log(`โš ๏ธ Could not fetch App Service Plan metrics: ${error.message.split('\n')[0]}`);
94
+ }
95
+ }
96
+ async function getAppServiceMetrics(appName, resourceGroup) {
97
+ try {
98
+ console.log('\n--- ๐Ÿ–ฅ๏ธ App Service Metrics ---');
99
+ // Get subscription ID
100
+ const subscriptionId = (0, child_process_1.execSync)('az account show --query id -o tsv', { encoding: 'utf8' }).trim();
101
+ // Get app-level metrics using Azure CLI
102
+ console.log('๐Ÿ“Š Fetching App Service metrics (last 5 minutes)...');
103
+ const appResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Web/sites/${appName}`;
104
+ const appMetricsCmd = `az monitor metrics list --resource "${appResourceId}" --metrics CpuTime Requests Http2xx Http4xx Http5xx --interval PT1M --query "value[].{name:name.value, data:timeseries[0].data}" -o json`;
105
+ const appMetricsOutput = (0, child_process_1.execSync)(appMetricsCmd, { encoding: 'utf8' });
106
+ const appMetrics = JSON.parse(appMetricsOutput);
107
+ for (const metric of appMetrics) {
108
+ const latestData = metric.data[metric.data.length - 1];
109
+ const value = latestData?.total || latestData?.average || 'N/A';
110
+ let displayValue = value;
111
+ if (typeof value === 'number') {
112
+ if (metric.name === 'CpuTime') {
113
+ displayValue = `${value.toFixed(2)}s`;
114
+ }
115
+ else {
116
+ displayValue = value.toString();
117
+ }
118
+ }
119
+ console.log(` ${metric.name}: ${displayValue}`);
120
+ }
121
+ }
122
+ catch (error) {
123
+ console.log(`โš ๏ธ Could not fetch App Service metrics: ${error.message.split('\n')[0]}`);
124
+ }
125
+ }
126
+ async function getProcessInformation(appName) {
127
+ try {
128
+ console.log('\n--- โš™๏ธ Process Information (Kudu API) ---');
129
+ // Get access token for Kudu API
130
+ const token = (0, child_process_1.execSync)('az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv', { encoding: 'utf8' }).trim();
131
+ // Import axios dynamically
132
+ const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
133
+ // Get process list from Kudu
134
+ console.log('๐Ÿ“‹ Fetching running processes...');
135
+ const processResponse = await axios.get(`https://${appName}.scm.azurewebsites.net/api/processes`, {
136
+ headers: { Authorization: `Bearer ${token}` },
137
+ timeout: 10000
138
+ });
139
+ const processes = processResponse.data;
140
+ // Sort by CPU usage and show top processes
141
+ const sortedProcesses = processes
142
+ .filter((p) => p.cpu_usage !== undefined)
143
+ .sort((a, b) => (b.cpu_usage || 0) - (a.cpu_usage || 0))
144
+ .slice(0, 10);
145
+ console.log('\n๐Ÿ”ฅ Top 10 Processes by CPU Usage:');
146
+ console.log('PID\tCPU%\tMemory(MB)\tName');
147
+ console.log('---\t----\t---------\t----');
148
+ for (const process of sortedProcesses) {
149
+ const pid = process.id || 'N/A';
150
+ const cpu = process.cpu_usage ? process.cpu_usage.toFixed(2) : '0.00';
151
+ const memory = process.working_set ? (process.working_set / 1024 / 1024).toFixed(1) : 'N/A';
152
+ const name = process.name || 'Unknown';
153
+ console.log(`${pid}\t${cpu}\t${memory}\t\t${name.substring(0, 30)}`);
154
+ }
155
+ }
156
+ catch (error) {
157
+ console.log(`โš ๏ธ Could not fetch process information: ${error.message.split('\n')[0]}`);
158
+ console.log(' This might be due to authentication issues or Kudu API being unavailable.');
159
+ }
160
+ }
161
+ async function getSystemInformation(appName) {
162
+ try {
163
+ console.log('\n--- ๐Ÿ–ฅ๏ธ System Information ---');
164
+ const token = (0, child_process_1.execSync)('az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv', { encoding: 'utf8' }).trim();
165
+ const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
166
+ // Get system info using Kudu command API
167
+ console.log('๐Ÿ“Š Fetching system information...');
168
+ const commands = [
169
+ { name: 'Memory Info', cmd: 'cat /proc/meminfo | head -10' },
170
+ { name: 'CPU Info', cmd: 'cat /proc/cpuinfo | grep "model name" | head -1' },
171
+ { name: 'Disk Usage', cmd: 'df -h | head -5' },
172
+ { name: 'Load Average', cmd: 'uptime' }
173
+ ];
174
+ for (const command of commands) {
175
+ try {
176
+ const response = await axios.post(`https://${appName}.scm.azurewebsites.net/api/command`, {
177
+ command: command.cmd,
178
+ dir: '/home'
179
+ }, {
180
+ headers: { Authorization: `Bearer ${token}` },
181
+ timeout: 5000
182
+ });
183
+ console.log(`\n${command.name}:`);
184
+ const output = response.data.Output || response.data.Error || 'No output';
185
+ console.log(output.split('\n').map((line) => ` ${line}`).join('\n'));
186
+ }
187
+ catch (cmdError) {
188
+ console.log(`\n${command.name}: Could not retrieve (${cmdError.message.split('\n')[0]})`);
189
+ }
190
+ }
191
+ }
192
+ catch (error) {
193
+ console.log(`โš ๏ธ Could not fetch system information: ${error.message.split('\n')[0]}`);
194
+ }
195
+ }
196
+ async function getApplicationLogs(appName, resourceGroup) {
197
+ try {
198
+ console.log('\n--- ๐Ÿ“‹ Recent Application Logs ---');
199
+ // Get recent logs using Azure CLI
200
+ console.log('๐Ÿ“„ Fetching recent application logs...');
201
+ const logsCmd = `az webapp log tail --name ${appName} --resource-group ${resourceGroup} --provider application --timeout 5`;
202
+ try {
203
+ const logs = (0, child_process_1.execSync)(logsCmd, { encoding: 'utf8', timeout: 10000 });
204
+ console.log('Recent logs:');
205
+ console.log(logs.split('\n').slice(-20).map(line => ` ${line}`).join('\n'));
206
+ }
207
+ catch (logError) {
208
+ console.log('โš ๏ธ Could not fetch logs - they may not be enabled or available');
209
+ console.log(' Enable application logging in Azure portal for better diagnostics');
210
+ }
211
+ }
212
+ catch (error) {
213
+ console.log(`โš ๏ธ Could not fetch application logs: ${error.message.split('\n')[0]}`);
214
+ }
215
+ }
216
+ async function profileLocalEnvironment() {
217
+ console.log('\n--- ๐Ÿ  Local Environment Profiling ---');
218
+ try {
219
+ // Basic system information
220
+ console.log('๐Ÿ’ป System Information:');
221
+ if (process.platform === 'win32') {
222
+ try {
223
+ const systemInfo = (0, child_process_1.execSync)('systeminfo | findstr /C:"Total Physical Memory" /C:"Available Physical Memory" /C:"Processor"', { encoding: 'utf8' });
224
+ console.log(systemInfo.split('\n').map(line => ` ${line.trim()}`).filter(line => line).join('\n'));
225
+ }
226
+ catch {
227
+ console.log(' Could not retrieve Windows system info');
228
+ }
229
+ }
230
+ else {
231
+ try {
232
+ const memInfo = (0, child_process_1.execSync)('free -h', { encoding: 'utf8' });
233
+ const cpuInfo = (0, child_process_1.execSync)('cat /proc/cpuinfo | grep "model name" | head -1', { encoding: 'utf8' });
234
+ console.log(' Memory:');
235
+ console.log(memInfo.split('\n').map(line => ` ${line}`).join('\n'));
236
+ console.log(' CPU:');
237
+ console.log(` ${cpuInfo.trim()}`);
238
+ }
239
+ catch {
240
+ console.log(' Could not retrieve Linux system info');
241
+ }
242
+ }
243
+ // Node.js process information
244
+ console.log('\n๐ŸŸข Node.js Process Information:');
245
+ console.log(` Node Version: ${process.version}`);
246
+ console.log(` Platform: ${process.platform}`);
247
+ console.log(` Architecture: ${process.arch}`);
248
+ console.log(` Memory Usage:`);
249
+ const memUsage = process.memoryUsage();
250
+ console.log(` RSS: ${(memUsage.rss / 1024 / 1024).toFixed(2)} MB`);
251
+ console.log(` Heap Used: ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`);
252
+ console.log(` Heap Total: ${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`);
253
+ console.log(` External: ${(memUsage.external / 1024 / 1024).toFixed(2)} MB`);
254
+ }
255
+ catch (error) {
256
+ console.log(`โš ๏ธ Error profiling local environment: ${error.message}`);
257
+ }
258
+ }
259
+ async function main() {
260
+ const config = loadClientConfig();
261
+ // Azure configuration with environment variable fallbacks
262
+ const azureConfig = {
263
+ prodAppName: config.azure?.prodAppName || getEnvOr(['FRAIM_AZURE_PROD_APP_NAME'], 'fraim-app-prod'),
264
+ prodResourceGroup: config.azure?.prodResourceGroup || getEnvOr(['FRAIM_AZURE_PROD_RESOURCE_GROUP'], 'fraim-prod-rg'),
265
+ preprodAppName: config.azure?.preprodAppName || getEnvOr(['FRAIM_AZURE_PREPROD_APP_NAME'], 'fraim-app-pre-prod'),
266
+ preprodResourceGroup: config.azure?.preprodResourceGroup || getEnvOr(['FRAIM_AZURE_PREPROD_RESOURCE_GROUP'], 'fraim-pre-prod-rg'),
267
+ localAppName: config.azure?.localAppName || getEnvOr(['FRAIM_AZURE_LOCAL_APP_NAME'], 'local'),
268
+ localResourceGroup: config.azure?.localResourceGroup || getEnvOr(['FRAIM_AZURE_LOCAL_RESOURCE_GROUP'], 'local')
269
+ };
270
+ const CONFIGS = {
271
+ prod: {
272
+ env: 'prod',
273
+ appName: azureConfig.prodAppName,
274
+ resourceGroup: azureConfig.prodResourceGroup
275
+ },
276
+ preprod: {
277
+ env: 'preprod',
278
+ appName: azureConfig.preprodAppName,
279
+ resourceGroup: azureConfig.preprodResourceGroup
280
+ },
281
+ local: {
282
+ env: 'local',
283
+ appName: azureConfig.localAppName,
284
+ resourceGroup: azureConfig.localResourceGroup
285
+ }
286
+ };
287
+ const args = process.argv.slice(2);
288
+ let profileConfig = CONFIGS.local;
289
+ const includeLogs = args.includes('--logs');
290
+ if (args.includes('--help') || args.includes('-h')) {
291
+ console.log(`
292
+ ๐Ÿ” Azure App Service Profiler
293
+
294
+ Usage:
295
+ npx tsx profile-server.ts [options]
296
+
297
+ Options:
298
+ --prod Profile production environment
299
+ --preprod Profile pre-production environment
300
+ --local Profile local environment (default)
301
+ --logs Include application logs in analysis
302
+ --help, -h Show this help message
303
+
304
+ Examples:
305
+ # Profile production server
306
+ npx tsx profile-server.ts --prod
307
+
308
+ # Profile with logs
309
+ npx tsx profile-server.ts --prod --logs
310
+
311
+ # Profile local environment
312
+ npx tsx profile-server.ts --local
313
+
314
+ Requirements:
315
+ - Azure CLI (az) installed and authenticated
316
+ - Proper permissions to access App Service resources
317
+ - For Kudu API access: Contributor role on the App Service
318
+
319
+ Configuration:
320
+ Reads Azure settings from .fraim/config.json or environment variables:
321
+ - FRAIM_AZURE_PROD_APP_NAME
322
+ - FRAIM_AZURE_PROD_RESOURCE_GROUP
323
+ - FRAIM_AZURE_PREPROD_APP_NAME
324
+ - FRAIM_AZURE_PREPROD_RESOURCE_GROUP
325
+ `);
326
+ return;
327
+ }
328
+ if (args.includes('--prod'))
329
+ profileConfig = CONFIGS.prod;
330
+ else if (args.includes('--preprod'))
331
+ profileConfig = CONFIGS.preprod;
332
+ else if (args.includes('--local'))
333
+ profileConfig = CONFIGS.local;
334
+ console.log(`\n๐Ÿš€ Starting Server Profiling`);
335
+ console.log(`๐Ÿ“Š Environment: ${profileConfig.env.toUpperCase()}`);
336
+ console.log(`๐Ÿท๏ธ App: ${profileConfig.appName}`);
337
+ console.log(`๐Ÿ“ Resource Group: ${profileConfig.resourceGroup}`);
338
+ if (profileConfig.env === 'local') {
339
+ await profileLocalEnvironment();
340
+ console.log('\nโœ… Local profiling completed');
341
+ return;
342
+ }
343
+ // Check prerequisites for Azure profiling
344
+ console.log('\n๐Ÿ” Checking prerequisites...');
345
+ if (!checkAzureCLI()) {
346
+ console.error('โŒ Azure CLI not found. Please install Azure CLI first.');
347
+ console.error(' Download from: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli');
348
+ process.exit(1);
349
+ }
350
+ console.log('โœ… Azure CLI found');
351
+ if (!checkAzureAuth()) {
352
+ console.error('โŒ Not authenticated with Azure. Please run: az login');
353
+ process.exit(1);
354
+ }
355
+ console.log('โœ… Azure authentication verified');
356
+ try {
357
+ // Verify app exists
358
+ console.log(`\n๐Ÿ” Verifying App Service: ${profileConfig.appName}`);
359
+ (0, child_process_1.execSync)(`az webapp show --name ${profileConfig.appName} --resource-group ${profileConfig.resourceGroup} --query name -o tsv`, { stdio: 'pipe' });
360
+ console.log('โœ… App Service found');
361
+ // Run profiling analysis
362
+ await getAppServicePlanMetrics(profileConfig.appName, profileConfig.resourceGroup);
363
+ await getAppServiceMetrics(profileConfig.appName, profileConfig.resourceGroup);
364
+ await getProcessInformation(profileConfig.appName);
365
+ await getSystemInformation(profileConfig.appName);
366
+ if (includeLogs) {
367
+ await getApplicationLogs(profileConfig.appName, profileConfig.resourceGroup);
368
+ }
369
+ console.log('\nโœ… Server profiling completed successfully');
370
+ console.log('\n๐Ÿ’ก Tips:');
371
+ console.log(' - Use --logs flag to include application logs');
372
+ console.log(' - High CPU usage may indicate performance bottlenecks');
373
+ console.log(' - High memory usage may indicate memory leaks');
374
+ console.log(' - Check process list for unexpected or resource-heavy processes');
375
+ }
376
+ catch (error) {
377
+ console.error(`\nโŒ Profiling failed: ${error.message}`);
378
+ console.error('\nTroubleshooting:');
379
+ console.error(' - Verify app name and resource group are correct');
380
+ console.error(' - Ensure you have proper permissions (Contributor role)');
381
+ console.error(' - Check if the App Service is running');
382
+ console.error(' - Try: az webapp list --query "[].{name:name, resourceGroup:resourceGroup}"');
383
+ process.exit(1);
384
+ }
385
+ }
386
+ // Run the main function
387
+ main().catch(console.error);
@@ -124,9 +124,17 @@ try {
124
124
  }
125
125
 
126
126
  try {
127
- const fraimCli = require('./node_modules/fraim-framework/dist/src/cli/fraim.js');
127
+ const fraimBin = require('./node_modules/fraim-framework/bin/fraim.js');
128
128
  console.log('โœ“ FRAIM CLI loaded successfully');
129
129
  console.log('โœ“ No ERR_REQUIRE_ESM error');
130
+
131
+ // Test that we can access the loadCli function
132
+ if (fraimBin && typeof fraimBin.loadCli === 'function') {
133
+ console.log('โœ“ loadCli function available');
134
+ } else {
135
+ console.log('โš ๏ธ loadCli function not found, but require succeeded');
136
+ }
137
+
130
138
  process.exit(0);
131
139
  } catch (error) {
132
140
  if (error.code === 'ERR_REQUIRE_ESM') {
@@ -279,8 +287,16 @@ async function testFreshInstall() {
279
287
  console.log(' ๐Ÿš€ Testing if fraim CLI can be required...');
280
288
  const testScript = `
281
289
  try {
282
- const fraimCli = require('./node_modules/fraim-framework/dist/src/cli/fraim.js');
290
+ const fraimBin = require('./node_modules/fraim-framework/bin/fraim.js');
283
291
  console.log('โœ“ FRAIM CLI loaded successfully');
292
+
293
+ // Test that we can access the loadCli function
294
+ if (fraimBin && typeof fraimBin.loadCli === 'function') {
295
+ console.log('โœ“ loadCli function available');
296
+ } else {
297
+ console.log('โš ๏ธ loadCli function not found, but require succeeded');
298
+ }
299
+
284
300
  process.exit(0);
285
301
  } catch (error) {
286
302
  if (error.code === 'ERR_REQUIRE_ESM') {
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const test_utils_1 = require("./test-utils");
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const node_assert_1 = __importDefault(require("node:assert"));
11
+ async function testNoFraimInternalImports() {
12
+ console.log(' ๐Ÿงช Testing registry scripts do not import FRAIM internals...');
13
+ try {
14
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
15
+ const scriptFiles = ['cleanup-branch.ts', 'generate-engagement-emails.ts', 'newsletter-helpers.ts'];
16
+ for (const scriptFile of scriptFiles) {
17
+ const scriptPath = (0, path_1.join)(scriptsDir, scriptFile);
18
+ if ((0, fs_1.existsSync)(scriptPath)) {
19
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
20
+ // Should not import from ../../src/
21
+ (0, node_assert_1.default)(!content.includes('../../src/'), `${scriptFile} should not import from ../../src/`);
22
+ // Should not import fraim-config
23
+ (0, node_assert_1.default)(!content.includes('./fraim-config'), `${scriptFile} should not import fraim-config`);
24
+ // Should have self-contained config loading
25
+ (0, node_assert_1.default)(content.includes('.fraim/config.json') || content.includes('loadClientConfig'), `${scriptFile} should read config directly`);
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ catch (e) {
31
+ console.error(' โŒ FRAIM internal imports check failed:', e);
32
+ return false;
33
+ }
34
+ }
35
+ async function testInlineUtilityFunctions() {
36
+ console.log(' ๐Ÿงช Testing scripts have inline utility functions...');
37
+ try {
38
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
39
+ const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
40
+ if ((0, fs_1.existsSync)(scriptPath)) {
41
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
42
+ // Should have inline extractIssueNumber function
43
+ (0, node_assert_1.default)(content.includes('function extractIssueNumber'), 'cleanup-branch.ts should have inline extractIssueNumber function');
44
+ // Should have inline getCurrentGitBranch function
45
+ (0, node_assert_1.default)(content.includes('function getCurrentGitBranch'), 'cleanup-branch.ts should have inline getCurrentGitBranch function');
46
+ }
47
+ return true;
48
+ }
49
+ catch (e) {
50
+ console.error(' โŒ Inline utility functions check failed:', e);
51
+ return false;
52
+ }
53
+ }
54
+ async function testConfigLoadingFromJson() {
55
+ console.log(' ๐Ÿงช Testing scripts load config from .fraim/config.json...');
56
+ try {
57
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
58
+ const scriptPath = (0, path_1.join)(scriptsDir, 'generate-engagement-emails.ts');
59
+ if ((0, fs_1.existsSync)(scriptPath)) {
60
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
61
+ // Should have loadClientConfig function
62
+ (0, node_assert_1.default)(content.includes('function loadClientConfig'), 'generate-engagement-emails.ts should have loadClientConfig function');
63
+ // Should read .fraim/config.json
64
+ (0, node_assert_1.default)(content.includes('.fraim/config.json'), 'generate-engagement-emails.ts should read .fraim/config.json');
65
+ }
66
+ return true;
67
+ }
68
+ catch (e) {
69
+ console.error(' โŒ Config loading check failed:', e);
70
+ return false;
71
+ }
72
+ }
73
+ async function testRegistryPathValidator() {
74
+ console.log(' ๐Ÿงช Testing registry path validator passes...');
75
+ try {
76
+ (0, child_process_1.execSync)('npm run validate:registry', { stdio: 'pipe' });
77
+ return true;
78
+ }
79
+ catch (error) {
80
+ console.error(' โŒ Registry validation failed:', error.message);
81
+ return false;
82
+ }
83
+ }
84
+ async function testScriptExecutability() {
85
+ console.log(' ๐Ÿงช Testing scripts are executable without import errors...');
86
+ try {
87
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
88
+ const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
89
+ if ((0, fs_1.existsSync)(scriptPath)) {
90
+ // Test that the script can be parsed without import errors
91
+ const result = (0, child_process_1.execSync)(`npx tsx ${scriptPath} --help`, {
92
+ stdio: 'pipe',
93
+ encoding: 'utf-8'
94
+ });
95
+ // Should show help without errors
96
+ (0, node_assert_1.default)(result.includes('Branch Cleanup Script'), 'cleanup-branch.ts should show help without import errors');
97
+ }
98
+ return true;
99
+ }
100
+ catch (error) {
101
+ // Check if it's an import error
102
+ if (error.message.includes('Cannot find module') || error.message.includes('../../src/')) {
103
+ console.error(' โŒ Script has import errors:', error.message);
104
+ return false;
105
+ }
106
+ // Other errors (like missing git repo) are acceptable for this test
107
+ return true;
108
+ }
109
+ }
110
+ const testCases = [
111
+ {
112
+ name: 'Registry scripts should not import FRAIM internals',
113
+ testFunction: testNoFraimInternalImports
114
+ },
115
+ {
116
+ name: 'Scripts should have inline utility functions',
117
+ testFunction: testInlineUtilityFunctions
118
+ },
119
+ {
120
+ name: 'Scripts should load config from .fraim/config.json',
121
+ testFunction: testConfigLoadingFromJson
122
+ },
123
+ {
124
+ name: 'Registry path validator should pass',
125
+ testFunction: testRegistryPathValidator
126
+ },
127
+ {
128
+ name: 'Scripts should be executable without import errors',
129
+ testFunction: testScriptExecutability
130
+ }
131
+ ];
132
+ // Run the tests using the standard test runner
133
+ (0, test_utils_1.runTests)(testCases, async (testCase) => testCase.testFunction(), 'Client-Side Script Validation');
@@ -57,7 +57,7 @@ async function verifyPrepIssueConfigParsing() {
57
57
  }
58
58
  };
59
59
  const output = runParsingLogic(nodeScript, validConfig);
60
- assert_1.default.strictEqual(output, 'test-owner|test-repo|https://github.com/test-owner/test-repo.git|master');
60
+ assert_1.default.strictEqual(output, 'test-owner:test-repo:https://github.com/test-owner/test-repo.git');
61
61
  console.log(' โœ… Valid config parsed correctly');
62
62
  // 4. Test Missing Config
63
63
  assert_1.default.throws(() => {
@@ -69,39 +69,6 @@ async function verifyPrepIssueConfigParsing() {
69
69
  runParsingLogic(nodeScript, { repository: { owner: 'test-owner' } });
70
70
  }, /Script failed/);
71
71
  console.log(' โœ… Incomplete config correctly failed');
72
- // 6. Test Default Branch Extraction (git schema)
73
- const gitConfig = {
74
- git: {
75
- repoOwner: 'test-owner',
76
- repoName: 'test-repo',
77
- defaultBranch: 'master'
78
- }
79
- };
80
- const gitOutput = runParsingLogic(nodeScript, gitConfig);
81
- assert_1.default.strictEqual(gitOutput, 'test-owner|test-repo|https://github.com/test-owner/test-repo.git|master');
82
- console.log(' โœ… Git config with defaultBranch parsed correctly');
83
- // 7. Test Default Branch Extraction (repository schema)
84
- const repoConfig = {
85
- repository: {
86
- owner: 'test-owner',
87
- name: 'test-repo',
88
- url: 'https://github.com/test-owner/test-repo.git',
89
- defaultBranch: 'main'
90
- }
91
- };
92
- const repoOutput = runParsingLogic(nodeScript, repoConfig);
93
- assert_1.default.strictEqual(repoOutput, 'test-owner|test-repo|https://github.com/test-owner/test-repo.git|main');
94
- console.log(' โœ… Repository config with defaultBranch parsed correctly');
95
- // 8. Test Default Branch Fallback
96
- const noDefaultConfig = {
97
- git: {
98
- repoOwner: 'test-owner',
99
- repoName: 'test-repo'
100
- }
101
- };
102
- const fallbackOutput = runParsingLogic(nodeScript, noDefaultConfig);
103
- assert_1.default.strictEqual(fallbackOutput, 'test-owner|test-repo|https://github.com/test-owner/test-repo.git|master');
104
- console.log(' โœ… Default branch fallback to master works correctly');
105
72
  return true;
106
73
  }
107
74
  catch (error) {