fraim-framework 2.0.35 → 2.0.37
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/dist/registry/scripts/cleanup-branch.js +62 -33
- package/dist/registry/scripts/generate-engagement-emails.js +119 -44
- package/dist/registry/scripts/newsletter-helpers.js +208 -268
- package/dist/registry/scripts/profile-server.js +387 -0
- package/dist/scripts/build-stub-registry.js +108 -0
- package/dist/src/cli/commands/doctor.js +5 -5
- package/dist/src/cli/commands/sync.js +33 -19
- package/dist/tests/test-client-scripts-validation.js +133 -0
- package/dist/tests/test-package-size.js +88 -0
- package/dist/tests/test-script-location-independence.js +76 -28
- package/dist/tests/test-stub-registry.js +120 -0
- package/dist/tests/test-sync-stubs.js +143 -0
- package/package.json +7 -9
- package/registry/scripts/cleanup-branch.ts +341 -0
- package/registry/scripts/generate-engagement-emails.ts +830 -0
- package/registry/scripts/markdown-to-pdf.js +7 -3
- package/registry/scripts/newsletter-helpers.ts +777 -0
- package/registry/scripts/profile-server.ts +424 -0
- package/registry/scripts/run-thank-you-workflow.ts +122 -0
- package/registry/scripts/send-newsletter-simple.ts +102 -0
- package/registry/scripts/send-thank-you-emails.ts +57 -0
- package/registry/stubs/workflows/bootstrap/create-architecture.md +11 -0
- package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +11 -0
- package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +11 -0
- package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +11 -0
- package/registry/stubs/workflows/business-development/create-business-plan.md +11 -0
- package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +11 -0
- package/registry/stubs/workflows/business-development/price-product.md +18 -0
- package/registry/stubs/workflows/convert-to-pdf.md +11 -0
- package/registry/stubs/workflows/customer-development/insight-analysis.md +11 -0
- package/registry/stubs/workflows/customer-development/insight-triage.md +11 -0
- package/registry/stubs/workflows/customer-development/interview-preparation.md +11 -0
- package/registry/stubs/workflows/customer-development/linkedin-outreach.md +11 -0
- package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +11 -0
- package/registry/stubs/workflows/customer-development/thank-customers.md +11 -0
- package/registry/stubs/workflows/customer-development/weekly-newsletter.md +11 -0
- package/registry/stubs/workflows/deploy/cloud-deployment.md +11 -0
- package/registry/stubs/workflows/improve-fraim/contribute.md +11 -0
- package/registry/stubs/workflows/improve-fraim/file-issue.md +11 -0
- package/registry/stubs/workflows/marketing/content-creation.md +11 -0
- package/registry/stubs/workflows/marketing/hbr-article.md +11 -0
- package/registry/stubs/workflows/marketing/launch-checklist.md +11 -0
- package/registry/stubs/workflows/marketing/marketing-strategy.md +11 -0
- package/registry/stubs/workflows/marketing/storytelling.md +11 -0
- package/registry/stubs/workflows/performance/analyze-performance.md +11 -0
- package/registry/stubs/workflows/product-building/design.md +11 -0
- package/registry/stubs/workflows/product-building/implement.md +12 -0
- package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +11 -0
- package/registry/stubs/workflows/product-building/prep-issue.md +11 -0
- package/registry/stubs/workflows/product-building/prototype.md +11 -0
- package/registry/stubs/workflows/product-building/resolve.md +11 -0
- package/registry/stubs/workflows/product-building/retrospect.md +11 -0
- package/registry/stubs/workflows/product-building/spec.md +11 -0
- package/registry/stubs/workflows/product-building/test.md +11 -0
- package/registry/stubs/workflows/quality-assurance/browser-validation.md +11 -0
- package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +11 -0
- package/registry/stubs/workflows/replicate/replicate-discovery.md +11 -0
- package/registry/stubs/workflows/replicate/replicate-to-issues.md +11 -0
- package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +11 -0
- package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +11 -0
- package/registry/stubs/workflows/startup-credits/aws-activate-application.md +11 -0
- package/registry/stubs/workflows/startup-credits/google-cloud-application.md +11 -0
- package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +11 -0
- package/.github/workflows/ci.yml +0 -65
- package/.github/workflows/deploy-fraim.yml +0 -87
- package/.github/workflows/phase-change.yml +0 -251
- package/.github/workflows/status-change.yml +0 -68
- package/.github/workflows/sync-on-pr-review.yml +0 -66
- package/examples/simple-webapp/TESTING.md +0 -62
- package/examples/simple-webapp/example-test.ts +0 -186
- package/registry/github/workflows/ci.yml +0 -51
- package/registry/github/workflows/phase-change.yml +0 -251
- package/registry/github/workflows/status-change.yml +0 -68
- package/registry/github/workflows/sync-on-pr-review.yml +0 -66
- package/registry/mcp-template.jsonc +0 -29
- package/registry/rules/agent-success-criteria.md +0 -52
- package/registry/rules/agent-testing-guidelines.md +0 -502
- package/registry/rules/architecture.md +0 -52
- package/registry/rules/communication.md +0 -122
- package/registry/rules/continuous-learning.md +0 -55
- package/registry/rules/debugging-multitenancy-issues.md +0 -85
- package/registry/rules/ephemeral-execution.md +0 -57
- package/registry/rules/git-safe-commands.md +0 -34
- package/registry/rules/hitl-ppe-record-analysis.md +0 -302
- package/registry/rules/integrity-and-test-ethics.md +0 -275
- package/registry/rules/local-development.md +0 -254
- package/registry/rules/merge-requirements.md +0 -231
- package/registry/rules/simplicity.md +0 -118
- package/registry/rules/software-development-lifecycle.md +0 -105
- package/registry/rules/spike-first-development.md +0 -205
- package/registry/rules/successful-debugging-patterns.md +0 -491
- package/registry/rules/telemetry.md +0 -67
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +0 -53
- package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +0 -37
- package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +0 -35
- package/registry/templates/business-development/IDEATION-REPORT-TEMPLATE.md +0 -29
- package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +0 -126
- package/registry/templates/customer-development/customer-interview-template.md +0 -99
- package/registry/templates/customer-development/follow-up-email-templates.md +0 -132
- package/registry/templates/customer-development/insight-analysis-template.md +0 -74
- package/registry/templates/customer-development/strategic-recommendations-template.md +0 -53
- package/registry/templates/customer-development/thank-you-email-template.html +0 -124
- package/registry/templates/customer-development/thank-you-note-template.md +0 -16
- package/registry/templates/customer-development/triage-log-template.md +0 -278
- package/registry/templates/customer-development/weekly-newsletter-template.html +0 -204
- package/registry/templates/evidence/Design-Evidence.md +0 -30
- package/registry/templates/evidence/Implementation-BugEvidence.md +0 -86
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +0 -121
- package/registry/templates/evidence/Spec-Evidence.md +0 -19
- package/registry/templates/help/HelpNeeded.md +0 -14
- package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +0 -66
- package/registry/templates/marketing/STORYTELLING-TEMPLATE.md +0 -130
- package/registry/templates/replicate/implementation-checklist.md +0 -39
- package/registry/templates/replicate/use-cases-template.md +0 -88
- package/registry/templates/retrospective/RETROSPECTIVE-TEMPLATE.md +0 -55
- package/registry/templates/specs/BUGSPEC-TEMPLATE.md +0 -37
- package/registry/templates/specs/FEATURESPEC-TEMPLATE.md +0 -29
- package/registry/templates/specs/TECHSPEC-TEMPLATE.md +0 -39
- package/registry/workflows/bootstrap/create-architecture.md +0 -38
- package/registry/workflows/bootstrap/evaluate-code-quality.md +0 -36
- package/registry/workflows/bootstrap/verify-test-coverage.md +0 -37
- package/registry/workflows/business-development/create-business-plan.md +0 -737
- package/registry/workflows/business-development/ideate-business-opportunity.md +0 -55
- package/registry/workflows/business-development/price-product.md +0 -325
- package/registry/workflows/convert-to-pdf.md +0 -235
- package/registry/workflows/customer-development/insight-analysis.md +0 -156
- package/registry/workflows/customer-development/insight-triage.md +0 -933
- package/registry/workflows/customer-development/interview-preparation.md +0 -421
- package/registry/workflows/customer-development/linkedin-outreach.md +0 -593
- package/registry/workflows/customer-development/strategic-brainstorming.md +0 -146
- package/registry/workflows/customer-development/thank-customers.md +0 -203
- package/registry/workflows/customer-development/weekly-newsletter.md +0 -366
- package/registry/workflows/deploy/cloud-deployment.md +0 -310
- package/registry/workflows/improve-fraim/contribute.md +0 -32
- package/registry/workflows/improve-fraim/file-issue.md +0 -32
- package/registry/workflows/marketing/content-creation.md +0 -37
- package/registry/workflows/marketing/hbr-article.md +0 -73
- package/registry/workflows/marketing/launch-checklist.md +0 -37
- package/registry/workflows/marketing/marketing-strategy.md +0 -45
- package/registry/workflows/marketing/storytelling.md +0 -65
- package/registry/workflows/performance/analyze-performance.md +0 -65
- package/registry/workflows/product-building/design.md +0 -130
- package/registry/workflows/product-building/implement.md +0 -315
- package/registry/workflows/product-building/iterate-on-pr-comments.md +0 -70
- package/registry/workflows/product-building/prep-issue.md +0 -43
- package/registry/workflows/product-building/prototype.md +0 -60
- package/registry/workflows/product-building/resolve.md +0 -164
- package/registry/workflows/product-building/retrospect.md +0 -86
- package/registry/workflows/product-building/spec.md +0 -117
- package/registry/workflows/product-building/test.md +0 -120
- package/registry/workflows/quality-assurance/browser-validation.md +0 -221
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +0 -562
- package/registry/workflows/replicate/replicate-discovery.md +0 -336
- package/registry/workflows/replicate/replicate-to-issues.md +0 -319
- package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +0 -632
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -669
- package/registry/workflows/startup-credits/aws-activate-application.md +0 -535
- package/registry/workflows/startup-credits/google-cloud-application.md +0 -647
- package/registry/workflows/startup-credits/microsoft-azure-application.md +0 -538
|
@@ -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);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.buildStubRegistry = buildStubRegistry;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const stub_generator_1 = require("../src/utils/stub-generator");
|
|
11
|
+
/**
|
|
12
|
+
* Build script to generate stub-only registry for npm package
|
|
13
|
+
*
|
|
14
|
+
* This creates a minimal registry containing:
|
|
15
|
+
* - Pre-generated workflow stubs (for IDE discoverability)
|
|
16
|
+
* - Scripts (for local execution)
|
|
17
|
+
*
|
|
18
|
+
* Excludes:
|
|
19
|
+
* - Complete workflows (served by MCP server)
|
|
20
|
+
* - Templates (served by MCP server)
|
|
21
|
+
* - Rules (served by MCP server)
|
|
22
|
+
*/
|
|
23
|
+
const SOURCE_REGISTRY = path_1.default.join(__dirname, '..', 'registry');
|
|
24
|
+
const STUB_REGISTRY = path_1.default.join(__dirname, '..', 'registry', 'stubs');
|
|
25
|
+
async function buildStubRegistry() {
|
|
26
|
+
console.log('🔨 Building stub-only registry for npm package...');
|
|
27
|
+
// Clean existing stubs directory
|
|
28
|
+
if (fs_1.default.existsSync(STUB_REGISTRY)) {
|
|
29
|
+
fs_1.default.rmSync(STUB_REGISTRY, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
// Create stubs directory structure
|
|
32
|
+
fs_1.default.mkdirSync(STUB_REGISTRY, { recursive: true });
|
|
33
|
+
// Generate workflow stubs
|
|
34
|
+
await generateWorkflowStubs();
|
|
35
|
+
console.log('✅ Stub registry build complete!');
|
|
36
|
+
}
|
|
37
|
+
async function generateWorkflowStubs() {
|
|
38
|
+
const workflowsSourceDir = path_1.default.join(SOURCE_REGISTRY, 'workflows');
|
|
39
|
+
const workflowsStubDir = path_1.default.join(STUB_REGISTRY, 'workflows');
|
|
40
|
+
if (!fs_1.default.existsSync(workflowsSourceDir)) {
|
|
41
|
+
console.warn('⚠️ No workflows directory found in source registry');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log('📝 Generating workflow stubs...');
|
|
45
|
+
// Get all workflow files recursively
|
|
46
|
+
const workflowFiles = getAllWorkflowFiles(workflowsSourceDir);
|
|
47
|
+
let stubCount = 0;
|
|
48
|
+
for (const workflowFile of workflowFiles) {
|
|
49
|
+
try {
|
|
50
|
+
// Read complete workflow
|
|
51
|
+
const content = fs_1.default.readFileSync(workflowFile.fullPath, 'utf8');
|
|
52
|
+
// Parse intent and principles
|
|
53
|
+
const { intent, principles } = (0, stub_generator_1.parseRegistryWorkflow)(content);
|
|
54
|
+
// Generate stub
|
|
55
|
+
const workflowName = path_1.default.basename(workflowFile.name, '.md');
|
|
56
|
+
const stub = (0, stub_generator_1.generateWorkflowStub)(workflowName, workflowFile.relativePath, intent, principles);
|
|
57
|
+
// Create target directory structure
|
|
58
|
+
const targetDir = path_1.default.join(workflowsStubDir, path_1.default.dirname(workflowFile.relativePath));
|
|
59
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
60
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
// Write stub file
|
|
63
|
+
const stubPath = path_1.default.join(workflowsStubDir, workflowFile.relativePath);
|
|
64
|
+
fs_1.default.writeFileSync(stubPath, stub);
|
|
65
|
+
stubCount++;
|
|
66
|
+
console.log(` ✓ ${workflowFile.relativePath}`);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error(` ❌ Failed to generate stub for ${workflowFile.relativePath}:`, error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log(`📝 Generated ${stubCount} workflow stubs`);
|
|
73
|
+
}
|
|
74
|
+
function getAllWorkflowFiles(dir, baseDir = dir) {
|
|
75
|
+
const files = [];
|
|
76
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
77
|
+
return files;
|
|
78
|
+
}
|
|
79
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
// Skip certain directories
|
|
84
|
+
if (entry.name.includes('Issue ') || entry.name.includes('Master')) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
// Recursively process subdirectories
|
|
88
|
+
files.push(...getAllWorkflowFiles(fullPath, baseDir));
|
|
89
|
+
}
|
|
90
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
91
|
+
// Add markdown files
|
|
92
|
+
const relativePath = path_1.default.relative(baseDir, fullPath);
|
|
93
|
+
files.push({
|
|
94
|
+
name: entry.name,
|
|
95
|
+
fullPath,
|
|
96
|
+
relativePath: relativePath.replace(/\\/g, '/') // Normalize path separators
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
// Run the build if this script is executed directly
|
|
103
|
+
if (require.main === module) {
|
|
104
|
+
buildStubRegistry().catch(error => {
|
|
105
|
+
console.error('❌ Build failed:', error);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -53,20 +53,20 @@ exports.doctorCommand = new commander_1.Command('doctor')
|
|
|
53
53
|
// 4. Check registry & sync status
|
|
54
54
|
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
55
55
|
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
56
|
-
let registryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
56
|
+
let registryPath = path_1.default.join(__dirname, '../../../../registry/stubs');
|
|
57
57
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
58
58
|
// Try 3 levels up (src/cli/commands -> root)
|
|
59
|
-
registryPath = path_1.default.join(__dirname, '../../../registry');
|
|
59
|
+
registryPath = path_1.default.join(__dirname, '../../../registry/stubs');
|
|
60
60
|
}
|
|
61
61
|
// Fallback for local development if running from within the framework repo itself (and CWD is root)
|
|
62
62
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
63
|
-
registryPath = path_1.default.join(projectRoot, 'registry');
|
|
63
|
+
registryPath = path_1.default.join(projectRoot, 'registry/stubs');
|
|
64
64
|
}
|
|
65
65
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
66
|
-
console.log(chalk_1.default.yellow('⚠️ Could not locate framework registry. (Expected in node_modules/@fraim/framework/registry)'));
|
|
66
|
+
console.log(chalk_1.default.yellow('⚠️ Could not locate framework stub registry. (Expected in node_modules/@fraim/framework/registry/stubs)'));
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
console.log(chalk_1.default.green('✅ Framework registry found.'));
|
|
69
|
+
console.log(chalk_1.default.green('✅ Framework stub registry found.'));
|
|
70
70
|
const currentDigest = await (0, digest_utils_1.generateDigest)(registryPath);
|
|
71
71
|
const existingDigest = fs_1.default.existsSync(digestPath) ? fs_1.default.readFileSync(digestPath, 'utf8') : '';
|
|
72
72
|
if (currentDigest !== existingDigest) {
|
|
@@ -9,7 +9,6 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const digest_utils_1 = require("../../utils/digest-utils");
|
|
12
|
-
const stub_generator_1 = require("../../utils/stub-generator");
|
|
13
12
|
const config_loader_1 = require("../../fraim/config-loader");
|
|
14
13
|
const version_utils_1 = require("../../utils/version-utils");
|
|
15
14
|
const script_sync_utils_1 = require("../../utils/script-sync-utils");
|
|
@@ -20,20 +19,20 @@ const runSync = async (options) => {
|
|
|
20
19
|
const workflowsRelativePath = config.customizations?.workflowsPath || '.fraim/workflows';
|
|
21
20
|
const workflowsDir = path_1.default.resolve(projectRoot, workflowsRelativePath);
|
|
22
21
|
const digestPath = path_1.default.join(fraimDir, '.digest');
|
|
23
|
-
// In
|
|
22
|
+
// In npm package, stubs are in node_modules/@fraim/framework/registry/stubs/workflows
|
|
24
23
|
// We need to handle both "running from source" (src/cli/commands) and "running from dist" (dist/src/cli/commands)
|
|
25
24
|
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
26
|
-
let registryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
25
|
+
let registryPath = path_1.default.join(__dirname, '../../../../registry/stubs');
|
|
27
26
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
28
27
|
// Try 3 levels up (src/cli/commands -> root)
|
|
29
|
-
registryPath = path_1.default.join(__dirname, '../../../registry');
|
|
28
|
+
registryPath = path_1.default.join(__dirname, '../../../registry/stubs');
|
|
30
29
|
}
|
|
31
30
|
// Fallback for local development if running from within the framework repo itself
|
|
32
31
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
33
|
-
registryPath = path_1.default.join(projectRoot, 'registry');
|
|
32
|
+
registryPath = path_1.default.join(projectRoot, 'registry/stubs');
|
|
34
33
|
}
|
|
35
34
|
if (!fs_1.default.existsSync(registryPath)) {
|
|
36
|
-
console.error(chalk_1.default.red('❌
|
|
35
|
+
console.error(chalk_1.default.red('❌ Stub registry not found. Ensure @fraim/framework is installed and built.'));
|
|
37
36
|
process.exit(1);
|
|
38
37
|
}
|
|
39
38
|
if (!fs_1.default.existsSync(workflowsDir)) {
|
|
@@ -64,10 +63,10 @@ const runSync = async (options) => {
|
|
|
64
63
|
else {
|
|
65
64
|
const registryWorkflowsPath = path_1.default.join(registryPath, 'workflows');
|
|
66
65
|
if (!fs_1.default.existsSync(registryWorkflowsPath)) {
|
|
67
|
-
console.log(chalk_1.default.yellow('⚠️ No
|
|
66
|
+
console.log(chalk_1.default.yellow('⚠️ No workflow stubs found in registry.'));
|
|
68
67
|
}
|
|
69
68
|
else {
|
|
70
|
-
// Get all
|
|
69
|
+
// Get all workflow stubs from registry (recursive)
|
|
71
70
|
const getFiles = (dir) => {
|
|
72
71
|
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
73
72
|
const files = entries
|
|
@@ -80,13 +79,12 @@ const runSync = async (options) => {
|
|
|
80
79
|
return files;
|
|
81
80
|
};
|
|
82
81
|
const registryFiles = getFiles(registryWorkflowsPath);
|
|
83
|
-
const
|
|
82
|
+
const copiedStubs = [];
|
|
84
83
|
for (const file of registryFiles) {
|
|
85
|
-
|
|
86
|
-
const { intent, principles } = (0, stub_generator_1.parseRegistryWorkflow)(content);
|
|
84
|
+
// These are already stubs, just copy them directly
|
|
87
85
|
const fileName = path_1.default.basename(file);
|
|
88
86
|
const workflowName = fileName.replace('.md', '');
|
|
89
|
-
// Calculate relative path from registry/workflows to preserve structure
|
|
87
|
+
// Calculate relative path from registry/stubs/workflows to preserve structure
|
|
90
88
|
const relativePath = path_1.default.relative(registryWorkflowsPath, file);
|
|
91
89
|
const relativeDir = path_1.default.dirname(relativePath);
|
|
92
90
|
// Ensure target directory exists
|
|
@@ -94,10 +92,11 @@ const runSync = async (options) => {
|
|
|
94
92
|
if (!fs_1.default.existsSync(targetDir)) {
|
|
95
93
|
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
96
94
|
}
|
|
97
|
-
|
|
95
|
+
// Copy stub file directly
|
|
96
|
+
const stubContent = fs_1.default.readFileSync(file, 'utf8');
|
|
98
97
|
const stubPath = path_1.default.join(targetDir, fileName);
|
|
99
98
|
fs_1.default.writeFileSync(stubPath, stubContent);
|
|
100
|
-
|
|
99
|
+
copiedStubs.push(relativePath); // Store relative path for cleanup tracking
|
|
101
100
|
console.log(chalk_1.default.gray(` + ${workflowName} (${relativeDir === '.' ? 'root' : relativeDir})`));
|
|
102
101
|
}
|
|
103
102
|
// Cleanup stubs that no longer exist in registry
|
|
@@ -119,8 +118,8 @@ const runSync = async (options) => {
|
|
|
119
118
|
for (const stub of localStubs) {
|
|
120
119
|
// standardise path separators for comparison
|
|
121
120
|
const normalizedStub = stub.replace(/\\/g, '/');
|
|
122
|
-
const
|
|
123
|
-
if (!
|
|
121
|
+
const normalizedCopied = copiedStubs.map(s => s.replace(/\\/g, '/'));
|
|
122
|
+
if (!normalizedCopied.includes(normalizedStub)) {
|
|
124
123
|
fs_1.default.unlinkSync(path_1.default.join(workflowsDir, stub));
|
|
125
124
|
console.log(chalk_1.default.yellow(` - ${stub} (removed from registry)`));
|
|
126
125
|
// Cleanup empty directories
|
|
@@ -134,13 +133,28 @@ const runSync = async (options) => {
|
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
135
|
fs_1.default.writeFileSync(digestPath, currentDigest);
|
|
137
|
-
console.log(chalk_1.default.green(`\n✅ Workflow sync complete.
|
|
136
|
+
console.log(chalk_1.default.green(`\n✅ Workflow sync complete. Copied ${copiedStubs.length} stubs.`));
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
// Always sync scripts, regardless of workflow sync status
|
|
141
140
|
console.log(chalk_1.default.blue('\n🔄 Syncing FRAIM scripts to user directory...'));
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
// Scripts are packaged separately from stubs, find the correct registry path
|
|
142
|
+
// Try 4 levels up (dist/src/cli/commands -> root)
|
|
143
|
+
let scriptsRegistryPath = path_1.default.join(__dirname, '../../../../registry');
|
|
144
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
145
|
+
// Try 3 levels up (src/cli/commands -> root)
|
|
146
|
+
scriptsRegistryPath = path_1.default.join(__dirname, '../../../registry');
|
|
147
|
+
}
|
|
148
|
+
// Fallback for local development if running from within the framework repo itself
|
|
149
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
150
|
+
scriptsRegistryPath = path_1.default.join(projectRoot, 'registry');
|
|
151
|
+
}
|
|
152
|
+
if (!fs_1.default.existsSync(path_1.default.join(scriptsRegistryPath, 'scripts'))) {
|
|
153
|
+
console.error(chalk_1.default.red('❌ Scripts registry not found. Ensure @fraim/framework is installed and built.'));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
const syncResult = (0, script_sync_utils_1.syncScriptsToUserDirectory)(scriptsRegistryPath);
|
|
157
|
+
const cleanedScriptCount = (0, script_sync_utils_1.cleanupObsoleteUserScripts)(scriptsRegistryPath);
|
|
144
158
|
if (syncResult.synced > 0 || cleanedScriptCount > 0) {
|
|
145
159
|
console.log(chalk_1.default.green(`✅ Script sync complete. Updated ${syncResult.synced} scripts, removed ${cleanedScriptCount} obsolete scripts.`));
|
|
146
160
|
}
|