fraim-framework 2.0.36 → 2.0.38
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/bin/fraim.js +5 -52
- package/dist/registry/scripts/build-scripts-generator.js +205 -0
- package/dist/registry/scripts/fraim-config.js +61 -0
- package/dist/registry/scripts/generic-issues-api.js +100 -0
- package/dist/registry/scripts/openapi-generator.js +664 -0
- package/dist/registry/scripts/performance/profile-server.js +390 -0
- package/dist/scripts/build-stub-registry.js +108 -0
- package/dist/src/cli/commands/doctor.js +5 -5
- package/dist/src/cli/commands/init-project.js +74 -0
- package/dist/src/cli/commands/setup.js +176 -0
- package/dist/src/cli/commands/sync.js +33 -19
- package/dist/src/cli/commands/test-mcp.js +135 -0
- package/dist/src/cli/fraim.js +6 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +367 -0
- package/dist/src/cli/setup/ide-detector.js +163 -0
- package/dist/src/cli/setup/mcp-config-generator.js +115 -0
- package/dist/src/cli/setup/token-validator.js +49 -0
- package/dist/test-utils.js +96 -0
- package/dist/tests/debug-tools.js +2 -2
- package/dist/tests/esm-compat.js +11 -0
- package/dist/tests/shared-server-utils.js +57 -0
- package/dist/tests/test-chalk-esm-issue.js +159 -0
- package/dist/tests/test-chalk-real-world.js +265 -0
- package/dist/tests/test-chalk-regression.js +2 -18
- package/dist/tests/test-chalk-resolution-issue.js +304 -0
- package/dist/tests/test-client-scripts-validation.js +27 -5
- package/dist/tests/test-complete-setup-flow.js +110 -0
- package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
- package/dist/tests/test-ide-detector.js +46 -0
- package/dist/tests/test-improved-setup.js +121 -0
- package/dist/tests/test-mcp-config-generator.js +70 -0
- package/dist/tests/test-mcp-connection.js +58 -117
- package/dist/tests/test-mcp-issue-integration.js +2 -2
- package/dist/tests/test-mcp-lifecycle-methods.js +34 -100
- package/dist/tests/test-mcp-shared-server.js +308 -0
- package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
- package/dist/tests/test-package-size.js +101 -0
- package/dist/tests/test-prep-issue.js +34 -1
- package/dist/tests/test-script-location-independence.js +39 -62
- package/dist/tests/test-server-utils.js +32 -0
- package/dist/tests/test-session-rehydration.js +2 -2
- package/dist/tests/test-setup-integration.js +98 -0
- package/dist/tests/test-standalone.js +2 -2
- package/dist/tests/test-stub-registry.js +136 -0
- package/dist/tests/test-sync-stubs.js +143 -0
- package/dist/tests/test-telemetry.js +2 -2
- package/dist/tests/test-token-validator.js +30 -0
- package/dist/tests/test-user-journey.js +2 -1
- package/package.json +7 -9
- package/registry/agent-guardrails.md +62 -62
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/prep-issue.sh +61 -30
- package/registry/scripts/validate-openapi-limits.ts +366 -366
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- 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/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/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,308 @@
|
|
|
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 axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const test_utils_1 = require("./test-utils");
|
|
8
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
+
const db_service_js_1 = require("../src/fraim/db-service.js");
|
|
10
|
+
const shared_server_utils_1 = require("./shared-server-utils");
|
|
11
|
+
// Test 1: Basic Connection
|
|
12
|
+
async function testBasicConnection() {
|
|
13
|
+
console.log(' 🧪 Testing basic MCP connection...');
|
|
14
|
+
const BASE_URL = (0, shared_server_utils_1.getTestServerUrl)();
|
|
15
|
+
const MCP_URL = (0, shared_server_utils_1.getMcpEndpoint)();
|
|
16
|
+
const TEST_API_KEY = 'test-fraim-key-shared-basic';
|
|
17
|
+
let dbService;
|
|
18
|
+
try {
|
|
19
|
+
// Wait for server to be ready
|
|
20
|
+
await (0, shared_server_utils_1.waitForServer)();
|
|
21
|
+
// Setup test API key
|
|
22
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
23
|
+
await dbService.connect();
|
|
24
|
+
const db = dbService.db;
|
|
25
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
26
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
27
|
+
key: TEST_API_KEY,
|
|
28
|
+
userId: 'test-user-shared-basic',
|
|
29
|
+
orgId: 'test-org',
|
|
30
|
+
isActive: true,
|
|
31
|
+
createdAt: new Date()
|
|
32
|
+
});
|
|
33
|
+
// Test health endpoint
|
|
34
|
+
const healthRes = await axios_1.default.get(`${BASE_URL}/health`);
|
|
35
|
+
node_assert_1.default.strictEqual(healthRes.status, 200);
|
|
36
|
+
// Test MCP endpoint with initialize
|
|
37
|
+
const initRes = await axios_1.default.post(MCP_URL, {
|
|
38
|
+
jsonrpc: '2.0',
|
|
39
|
+
id: 1,
|
|
40
|
+
method: 'initialize',
|
|
41
|
+
params: {}
|
|
42
|
+
}, {
|
|
43
|
+
headers: { 'x-api-key': TEST_API_KEY }
|
|
44
|
+
});
|
|
45
|
+
node_assert_1.default.strictEqual(initRes.status, 200);
|
|
46
|
+
node_assert_1.default.strictEqual(initRes.data.jsonrpc, '2.0');
|
|
47
|
+
console.log(' ✅ Basic connection test passed');
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(' ❌ Basic connection test failed:', error.message);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (dbService) {
|
|
56
|
+
try {
|
|
57
|
+
const db = dbService.db;
|
|
58
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
59
|
+
await dbService.close();
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
console.warn(' ⚠️ DB cleanup failed:', e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Test 2: Tools List
|
|
68
|
+
async function testToolsList() {
|
|
69
|
+
console.log(' 🧪 Testing tools/list endpoint...');
|
|
70
|
+
const MCP_URL = (0, shared_server_utils_1.getMcpEndpoint)();
|
|
71
|
+
const TEST_API_KEY = 'test-fraim-key-shared-tools';
|
|
72
|
+
let dbService;
|
|
73
|
+
try {
|
|
74
|
+
// Wait for server to be ready
|
|
75
|
+
await (0, shared_server_utils_1.waitForServer)();
|
|
76
|
+
// Setup test API key
|
|
77
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
78
|
+
await dbService.connect();
|
|
79
|
+
const db = dbService.db;
|
|
80
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
81
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
82
|
+
key: TEST_API_KEY,
|
|
83
|
+
userId: 'test-user-shared-tools',
|
|
84
|
+
orgId: 'test-org',
|
|
85
|
+
isActive: true,
|
|
86
|
+
createdAt: new Date()
|
|
87
|
+
});
|
|
88
|
+
const res = await axios_1.default.post(MCP_URL, {
|
|
89
|
+
jsonrpc: '2.0',
|
|
90
|
+
id: 2,
|
|
91
|
+
method: 'tools/list',
|
|
92
|
+
params: {}
|
|
93
|
+
}, {
|
|
94
|
+
headers: { 'x-api-key': TEST_API_KEY }
|
|
95
|
+
});
|
|
96
|
+
node_assert_1.default.strictEqual(res.status, 200);
|
|
97
|
+
node_assert_1.default.strictEqual(res.data.jsonrpc, '2.0');
|
|
98
|
+
(0, node_assert_1.default)(Array.isArray(res.data.result.tools), 'Should return tools array');
|
|
99
|
+
console.log(` ✅ Tools list returned ${res.data.result.tools.length} tools`);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error(' ❌ Tools list test failed:', error.message);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (dbService) {
|
|
108
|
+
try {
|
|
109
|
+
const db = dbService.db;
|
|
110
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
111
|
+
await dbService.close();
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
console.warn(' ⚠️ DB cleanup failed:', e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Test 3: Tool Call
|
|
120
|
+
async function testToolCall() {
|
|
121
|
+
console.log(' 🧪 Testing tool call (get_fraim_init)...');
|
|
122
|
+
const MCP_URL = (0, shared_server_utils_1.getMcpEndpoint)();
|
|
123
|
+
const TEST_API_KEY = 'test-fraim-key-shared-call';
|
|
124
|
+
let dbService;
|
|
125
|
+
try {
|
|
126
|
+
// Wait for server to be ready
|
|
127
|
+
await (0, shared_server_utils_1.waitForServer)();
|
|
128
|
+
// Setup test API key
|
|
129
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
130
|
+
await dbService.connect();
|
|
131
|
+
const db = dbService.db;
|
|
132
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
133
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
134
|
+
key: TEST_API_KEY,
|
|
135
|
+
userId: 'test-user-shared-call',
|
|
136
|
+
orgId: 'test-org',
|
|
137
|
+
isActive: true,
|
|
138
|
+
createdAt: new Date()
|
|
139
|
+
});
|
|
140
|
+
const res = await axios_1.default.post(MCP_URL, {
|
|
141
|
+
jsonrpc: '2.0',
|
|
142
|
+
id: 3,
|
|
143
|
+
method: 'tools/call',
|
|
144
|
+
params: {
|
|
145
|
+
name: 'get_fraim_init',
|
|
146
|
+
arguments: {}
|
|
147
|
+
}
|
|
148
|
+
}, {
|
|
149
|
+
headers: { 'x-api-key': TEST_API_KEY }
|
|
150
|
+
});
|
|
151
|
+
node_assert_1.default.strictEqual(res.status, 200);
|
|
152
|
+
node_assert_1.default.strictEqual(res.data.jsonrpc, '2.0');
|
|
153
|
+
(0, node_assert_1.default)(res.data.result, 'Should return result');
|
|
154
|
+
console.log(' ✅ Tool call test passed');
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error(' ❌ Tool call test failed:', error.message);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
if (dbService) {
|
|
163
|
+
try {
|
|
164
|
+
const db = dbService.db;
|
|
165
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
166
|
+
await dbService.close();
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
console.warn(' ⚠️ DB cleanup failed:', e);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Test 4: Error Handling
|
|
175
|
+
async function testErrorHandling() {
|
|
176
|
+
console.log(' 🧪 Testing error handling...');
|
|
177
|
+
const MCP_URL = (0, shared_server_utils_1.getMcpEndpoint)();
|
|
178
|
+
const TEST_API_KEY = 'test-fraim-key-shared-error';
|
|
179
|
+
let dbService;
|
|
180
|
+
try {
|
|
181
|
+
// Wait for server to be ready
|
|
182
|
+
await (0, shared_server_utils_1.waitForServer)();
|
|
183
|
+
// Setup test API key
|
|
184
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
185
|
+
await dbService.connect();
|
|
186
|
+
const db = dbService.db;
|
|
187
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
188
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
189
|
+
key: TEST_API_KEY,
|
|
190
|
+
userId: 'test-user-shared-error',
|
|
191
|
+
orgId: 'test-org',
|
|
192
|
+
isActive: true,
|
|
193
|
+
createdAt: new Date()
|
|
194
|
+
});
|
|
195
|
+
// Test invalid method
|
|
196
|
+
const res = await axios_1.default.post(MCP_URL, {
|
|
197
|
+
jsonrpc: '2.0',
|
|
198
|
+
id: 4,
|
|
199
|
+
method: 'invalid/method',
|
|
200
|
+
params: {}
|
|
201
|
+
}, {
|
|
202
|
+
headers: { 'x-api-key': TEST_API_KEY },
|
|
203
|
+
validateStatus: () => true // Don't throw on error status
|
|
204
|
+
});
|
|
205
|
+
// Should return error response, not crash
|
|
206
|
+
(0, node_assert_1.default)(res.data.error, 'Should return error for invalid method');
|
|
207
|
+
console.log(' ✅ Error handling test passed');
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
console.error(' ❌ Error handling test failed:', error.message);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
if (dbService) {
|
|
216
|
+
try {
|
|
217
|
+
const db = dbService.db;
|
|
218
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
219
|
+
await dbService.close();
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
console.warn(' ⚠️ DB cleanup failed:', e);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Test 5: Concurrent Requests
|
|
228
|
+
async function testConcurrentRequests() {
|
|
229
|
+
console.log(' 🧪 Testing concurrent requests...');
|
|
230
|
+
const MCP_URL = (0, shared_server_utils_1.getMcpEndpoint)();
|
|
231
|
+
const TEST_API_KEY = 'test-fraim-key-shared-concurrent';
|
|
232
|
+
let dbService;
|
|
233
|
+
try {
|
|
234
|
+
// Wait for server to be ready
|
|
235
|
+
await (0, shared_server_utils_1.waitForServer)();
|
|
236
|
+
// Setup test API key
|
|
237
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
238
|
+
await dbService.connect();
|
|
239
|
+
const db = dbService.db;
|
|
240
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
241
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
242
|
+
key: TEST_API_KEY,
|
|
243
|
+
userId: 'test-user-shared-concurrent',
|
|
244
|
+
orgId: 'test-org',
|
|
245
|
+
isActive: true,
|
|
246
|
+
createdAt: new Date()
|
|
247
|
+
});
|
|
248
|
+
const requests = Array.from({ length: 5 }, (_, i) => axios_1.default.post(MCP_URL, {
|
|
249
|
+
jsonrpc: '2.0',
|
|
250
|
+
id: 10 + i,
|
|
251
|
+
method: 'tools/list',
|
|
252
|
+
params: {}
|
|
253
|
+
}, {
|
|
254
|
+
headers: { 'x-api-key': TEST_API_KEY }
|
|
255
|
+
}));
|
|
256
|
+
const responses = await Promise.all(requests);
|
|
257
|
+
responses.forEach((res, i) => {
|
|
258
|
+
node_assert_1.default.strictEqual(res.status, 200, `Request ${i} should succeed`);
|
|
259
|
+
node_assert_1.default.strictEqual(res.data.id, 10 + i, `Request ${i} should have correct ID`);
|
|
260
|
+
});
|
|
261
|
+
console.log(' ✅ Concurrent requests test passed');
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
console.error(' ❌ Concurrent requests test failed:', error.message);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
if (dbService) {
|
|
270
|
+
try {
|
|
271
|
+
const db = dbService.db;
|
|
272
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
273
|
+
await dbService.close();
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
console.warn(' ⚠️ DB cleanup failed:', e);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const testCases = [
|
|
282
|
+
{
|
|
283
|
+
name: 'Basic Connection',
|
|
284
|
+
testFunction: testBasicConnection
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'Tools List',
|
|
288
|
+
testFunction: testToolsList
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'Tool Call',
|
|
292
|
+
testFunction: testToolCall
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: 'Error Handling',
|
|
296
|
+
testFunction: testErrorHandling
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'Concurrent Requests',
|
|
300
|
+
testFunction: testConcurrentRequests
|
|
301
|
+
}
|
|
302
|
+
];
|
|
303
|
+
// Custom test runner function for shared MCP tests
|
|
304
|
+
async function runSharedMcpTest(testCase) {
|
|
305
|
+
return await testCase.testFunction();
|
|
306
|
+
}
|
|
307
|
+
// Use the standard test runner with the shared server
|
|
308
|
+
(0, test_utils_1.runTests)(testCases, runSharedMcpTest, 'Shared MCP Server Tests');
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Diagnostic test to understand npm's chalk resolution behavior
|
|
4
|
+
*
|
|
5
|
+
* This test will show us exactly what npm does when there's a version conflict
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
async function main() {
|
|
16
|
+
console.log('🔍 NPM Chalk Resolution Diagnostic\n');
|
|
17
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'npm-diagnostic-'));
|
|
18
|
+
console.log(`📂 Temp dir: ${tempDir}\n`);
|
|
19
|
+
try {
|
|
20
|
+
// 1. Pack fraim-framework
|
|
21
|
+
console.log('📦 Packing fraim-framework...');
|
|
22
|
+
const projectRoot = process.cwd();
|
|
23
|
+
const packResult = (0, node_child_process_1.execSync)('npm pack', {
|
|
24
|
+
cwd: projectRoot,
|
|
25
|
+
encoding: 'utf-8'
|
|
26
|
+
});
|
|
27
|
+
const tarballName = packResult.trim().split('\n').pop()?.trim();
|
|
28
|
+
if (!tarballName) {
|
|
29
|
+
console.log('❌ Failed to pack');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const tarballPath = path_1.default.join(projectRoot, tarballName);
|
|
33
|
+
console.log(`✅ Created: ${tarballName}\n`);
|
|
34
|
+
// 2. Create test project with chalk v5
|
|
35
|
+
console.log('📝 Creating test project with chalk v5 dependency...');
|
|
36
|
+
const packageJson = {
|
|
37
|
+
name: 'diagnostic-test',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
dependencies: {
|
|
40
|
+
'chalk': '^5.0.0',
|
|
41
|
+
'fraim-framework': `file:${tarballPath}`
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
fs_1.default.writeFileSync(path_1.default.join(tempDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
45
|
+
// 3. Install
|
|
46
|
+
console.log('📥 Running npm install...\n');
|
|
47
|
+
try {
|
|
48
|
+
const installOutput = (0, node_child_process_1.execSync)('npm install', {
|
|
49
|
+
cwd: tempDir,
|
|
50
|
+
encoding: 'utf-8',
|
|
51
|
+
stdio: 'pipe'
|
|
52
|
+
});
|
|
53
|
+
console.log('✅ Install completed\n');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.log('⚠️ Install had issues:\n', error.stdout || error.message);
|
|
57
|
+
}
|
|
58
|
+
// 4. Check what was installed
|
|
59
|
+
console.log('📋 Checking installed chalk versions...\n');
|
|
60
|
+
// Root chalk
|
|
61
|
+
const rootChalkPath = path_1.default.join(tempDir, 'node_modules', 'chalk', 'package.json');
|
|
62
|
+
if (fs_1.default.existsSync(rootChalkPath)) {
|
|
63
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(rootChalkPath, 'utf-8'));
|
|
64
|
+
console.log(` Root chalk: ${pkg.version}`);
|
|
65
|
+
console.log(` Type: ${pkg.type || 'commonjs'}`);
|
|
66
|
+
console.log(` Main: ${pkg.main || 'N/A'}`);
|
|
67
|
+
console.log(` Exports: ${pkg.exports ? 'Yes' : 'No'}\n`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(' ❌ No root chalk found\n');
|
|
71
|
+
}
|
|
72
|
+
// Fraim's chalk
|
|
73
|
+
const fraimChalkPath = path_1.default.join(tempDir, 'node_modules', 'fraim-framework', 'node_modules', 'chalk', 'package.json');
|
|
74
|
+
if (fs_1.default.existsSync(fraimChalkPath)) {
|
|
75
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(fraimChalkPath, 'utf-8'));
|
|
76
|
+
console.log(` fraim-framework's chalk: ${pkg.version}`);
|
|
77
|
+
console.log(` Type: ${pkg.type || 'commonjs'}`);
|
|
78
|
+
console.log(` Main: ${pkg.main || 'N/A'}\n`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(' ℹ️ fraim-framework has no separate chalk (using root)\n');
|
|
82
|
+
}
|
|
83
|
+
// 5. Run npm ls to see the dependency tree
|
|
84
|
+
console.log('🌳 Dependency tree (npm ls chalk):\n');
|
|
85
|
+
try {
|
|
86
|
+
const lsOutput = (0, node_child_process_1.execSync)('npm ls chalk', {
|
|
87
|
+
cwd: tempDir,
|
|
88
|
+
encoding: 'utf-8'
|
|
89
|
+
});
|
|
90
|
+
console.log(lsOutput);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.log(error.stdout || error.message);
|
|
94
|
+
}
|
|
95
|
+
// 6. Check fraim-framework's package.json in node_modules
|
|
96
|
+
console.log('\n📋 fraim-framework\'s chalk dependency in node_modules:\n');
|
|
97
|
+
const fraimPkgPath = path_1.default.join(tempDir, 'node_modules', 'fraim-framework', 'package.json');
|
|
98
|
+
if (fs_1.default.existsSync(fraimPkgPath)) {
|
|
99
|
+
const fraimPkg = JSON.parse(fs_1.default.readFileSync(fraimPkgPath, 'utf-8'));
|
|
100
|
+
console.log(` Chalk version in package.json: ${fraimPkg.dependencies?.chalk || 'N/A'}\n`);
|
|
101
|
+
}
|
|
102
|
+
// 7. Try to load fraim CLI
|
|
103
|
+
console.log('🚀 Testing fraim CLI load...\n');
|
|
104
|
+
const testScript = `
|
|
105
|
+
try {
|
|
106
|
+
const fraimCli = require('./node_modules/fraim-framework/dist/src/cli/fraim.js');
|
|
107
|
+
console.log('✅ SUCCESS: fraim CLI loaded');
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log('❌ ERROR:', error.code || 'Unknown');
|
|
110
|
+
console.log('Message:', error.message.substring(0, 200));
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
fs_1.default.writeFileSync(path_1.default.join(tempDir, 'test.js'), testScript);
|
|
114
|
+
try {
|
|
115
|
+
const testOutput = (0, node_child_process_1.execSync)('node test.js', {
|
|
116
|
+
cwd: tempDir,
|
|
117
|
+
encoding: 'utf-8'
|
|
118
|
+
});
|
|
119
|
+
console.log(testOutput);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.log(error.stdout || error.message);
|
|
123
|
+
}
|
|
124
|
+
// Cleanup
|
|
125
|
+
fs_1.default.unlinkSync(tarballPath);
|
|
126
|
+
console.log('\n✅ Diagnostic complete');
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('\n❌ Diagnostic failed:', error);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
try {
|
|
133
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
console.log('\n⚠️ Could not clean up temp directory');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
main();
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
const node_test_1 = require("node:test");
|
|
8
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
/**
|
|
12
|
+
* Test to verify package size reduction from stub-only registry
|
|
13
|
+
*/
|
|
14
|
+
// Find project root by looking for package.json
|
|
15
|
+
function findProjectRoot() {
|
|
16
|
+
let currentDir = __dirname;
|
|
17
|
+
while (currentDir !== path_1.default.dirname(currentDir)) {
|
|
18
|
+
if (fs_1.default.existsSync(path_1.default.join(currentDir, 'package.json'))) {
|
|
19
|
+
return currentDir;
|
|
20
|
+
}
|
|
21
|
+
currentDir = path_1.default.dirname(currentDir);
|
|
22
|
+
}
|
|
23
|
+
throw new Error('Could not find project root (package.json not found)');
|
|
24
|
+
}
|
|
25
|
+
(0, node_test_1.test)('Stub registry is significantly smaller than full registry', () => {
|
|
26
|
+
const projectRoot = findProjectRoot();
|
|
27
|
+
const fullWorkflowsPath = path_1.default.join(projectRoot, 'registry', 'workflows');
|
|
28
|
+
const stubWorkflowsPath = path_1.default.join(projectRoot, 'registry', 'stubs', 'workflows');
|
|
29
|
+
(0, node_assert_1.default)(fs_1.default.existsSync(fullWorkflowsPath), 'Full workflows directory should exist');
|
|
30
|
+
(0, node_assert_1.default)(fs_1.default.existsSync(stubWorkflowsPath), 'Stub workflows directory should exist');
|
|
31
|
+
// Calculate sizes
|
|
32
|
+
const fullSize = getDirectorySize(fullWorkflowsPath);
|
|
33
|
+
const stubSize = getDirectorySize(stubWorkflowsPath);
|
|
34
|
+
console.log(`Full workflows size: ${fullSize} bytes`);
|
|
35
|
+
console.log(`Stub workflows size: ${stubSize} bytes`);
|
|
36
|
+
console.log(`Size reduction: ${Math.round((1 - stubSize / fullSize) * 100)}%`);
|
|
37
|
+
// Stubs should be significantly smaller
|
|
38
|
+
(0, node_assert_1.default)(stubSize < fullSize * 0.3, `Stubs should be <30% of full size (actual: ${Math.round(stubSize / fullSize * 100)}%)`);
|
|
39
|
+
// Should have same number of files
|
|
40
|
+
const fullFiles = getAllFiles(fullWorkflowsPath, '.md');
|
|
41
|
+
const stubFiles = getAllFiles(stubWorkflowsPath, '.md');
|
|
42
|
+
node_assert_1.default.strictEqual(stubFiles.length, fullFiles.length, 'Should have same number of workflow files');
|
|
43
|
+
console.log(`✅ Package size reduced by ${Math.round((1 - stubSize / fullSize) * 100)}% while maintaining all ${stubFiles.length} workflows`);
|
|
44
|
+
});
|
|
45
|
+
(0, node_test_1.test)('Package excludes full workflows and templates', () => {
|
|
46
|
+
const projectRoot = findProjectRoot();
|
|
47
|
+
const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
|
|
48
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
49
|
+
const files = packageJson.files || [];
|
|
50
|
+
// Calculate what would be excluded
|
|
51
|
+
const fullWorkflowsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'workflows'));
|
|
52
|
+
const templatesSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'templates'));
|
|
53
|
+
const rulesSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'rules'));
|
|
54
|
+
const excludedSize = fullWorkflowsSize + templatesSize + rulesSize;
|
|
55
|
+
// What's included
|
|
56
|
+
const stubsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'stubs'));
|
|
57
|
+
const scriptsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'scripts'));
|
|
58
|
+
const includedSize = stubsSize + scriptsSize;
|
|
59
|
+
console.log(`Excluded from package: ${excludedSize} bytes (workflows + templates + rules)`);
|
|
60
|
+
console.log(`Included in package: ${includedSize} bytes (stubs + scripts)`);
|
|
61
|
+
console.log(`Total registry size reduction: ${Math.round((1 - includedSize / (includedSize + excludedSize)) * 100)}%`);
|
|
62
|
+
// Verify significant size reduction
|
|
63
|
+
(0, node_assert_1.default)(includedSize < excludedSize, 'Package should exclude more content than it includes');
|
|
64
|
+
console.log('✅ Package correctly excludes heavy content while keeping essentials');
|
|
65
|
+
});
|
|
66
|
+
function getDirectorySize(dirPath) {
|
|
67
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
let totalSize = 0;
|
|
71
|
+
const files = getAllFiles(dirPath);
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
try {
|
|
74
|
+
const stats = fs_1.default.statSync(file);
|
|
75
|
+
totalSize += stats.size;
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
// Skip files that can't be read
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return totalSize;
|
|
82
|
+
}
|
|
83
|
+
function getAllFiles(dir, extension) {
|
|
84
|
+
const files = [];
|
|
85
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
86
|
+
return files;
|
|
87
|
+
}
|
|
88
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
files.push(...getAllFiles(fullPath, extension));
|
|
93
|
+
}
|
|
94
|
+
else if (entry.isFile()) {
|
|
95
|
+
if (!extension || entry.name.endsWith(extension)) {
|
|
96
|
+
files.push(fullPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
@@ -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
|
|
60
|
+
assert_1.default.strictEqual(output, 'test-owner|test-repo|https://github.com/test-owner/test-repo.git|master');
|
|
61
61
|
console.log(' ✅ Valid config parsed correctly');
|
|
62
62
|
// 4. Test Missing Config
|
|
63
63
|
assert_1.default.throws(() => {
|
|
@@ -69,6 +69,39 @@ 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');
|
|
72
105
|
return true;
|
|
73
106
|
}
|
|
74
107
|
catch (error) {
|