fraim-framework 2.0.27 → 2.0.33

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 (130) hide show
  1. package/.github/workflows/deploy-fraim.yml +1 -1
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/cleanup-branch.js +258 -0
  4. package/dist/registry/scripts/evaluate-code-quality.js +66 -0
  5. package/dist/registry/scripts/exec-with-timeout.js +142 -0
  6. package/dist/registry/scripts/fraim-config.js +61 -0
  7. package/dist/registry/scripts/generate-engagement-emails.js +630 -0
  8. package/dist/registry/scripts/generic-issues-api.js +100 -0
  9. package/dist/registry/scripts/newsletter-helpers.js +731 -0
  10. package/dist/registry/scripts/openapi-generator.js +664 -0
  11. package/{registry/scripts/performance/profile-server.ts → dist/registry/scripts/performance/profile-server.js} +390 -368
  12. package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
  13. package/dist/registry/scripts/send-newsletter-simple.js +85 -0
  14. package/dist/registry/scripts/send-thank-you-emails.js +54 -0
  15. package/dist/registry/scripts/validate-openapi-limits.js +311 -0
  16. package/dist/registry/scripts/validate-test-coverage.js +262 -0
  17. package/dist/registry/scripts/verify-test-coverage.js +66 -0
  18. package/dist/src/cli/commands/init.js +42 -15
  19. package/dist/src/cli/commands/sync.js +22 -5
  20. package/dist/src/cli/fraim.js +24 -22
  21. package/dist/src/cli/setup/first-run.js +13 -6
  22. package/dist/src/fraim/db-service.js +26 -15
  23. package/dist/src/fraim/issues.js +67 -0
  24. package/dist/src/fraim-mcp-server.js +272 -18
  25. package/dist/src/utils/git-utils.js +1 -1
  26. package/dist/src/utils/script-sync-utils.js +218 -0
  27. package/dist/tests/debug-tools.js +80 -0
  28. package/dist/tests/esm-compat.js +11 -0
  29. package/dist/tests/test-chalk-esm-issue.js +159 -0
  30. package/dist/tests/test-chalk-real-world.js +265 -0
  31. package/dist/tests/test-chalk-regression.js +377 -0
  32. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  33. package/dist/tests/test-cli.js +70 -5
  34. package/dist/tests/test-end-to-end-hybrid-validation.js +349 -0
  35. package/dist/tests/test-first-run-journey.js +43 -3
  36. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  37. package/dist/tests/test-fraim-issues.js +59 -0
  38. package/dist/tests/test-genericization.js +1 -1
  39. package/dist/tests/test-hybrid-script-execution.js +369 -0
  40. package/dist/tests/test-mcp-connection.js +166 -0
  41. package/dist/tests/test-mcp-issue-integration.js +152 -0
  42. package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
  43. package/dist/tests/test-node-compatibility.js +93 -0
  44. package/dist/tests/test-npm-install.js +66 -0
  45. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  46. package/dist/tests/test-prep-issue.js +4 -1
  47. package/dist/tests/test-script-location-independence.js +173 -0
  48. package/dist/tests/test-script-sync.js +557 -0
  49. package/dist/tests/test-session-rehydration.js +145 -0
  50. package/dist/tests/test-standalone.js +5 -11
  51. package/dist/tests/test-sync-version-update.js +1 -1
  52. package/dist/tests/test-telemetry.js +190 -0
  53. package/dist/tests/test-user-journey.js +8 -4
  54. package/dist/tests/test-utils.js +13 -0
  55. package/dist/tests/test-wizard.js +2 -2
  56. package/package.json +11 -9
  57. package/registry/agent-guardrails.md +62 -54
  58. package/registry/rules/agent-success-criteria.md +52 -0
  59. package/registry/rules/agent-testing-guidelines.md +7 -7
  60. package/registry/rules/communication.md +121 -121
  61. package/registry/rules/continuous-learning.md +54 -54
  62. package/registry/rules/ephemeral-execution.md +39 -24
  63. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  64. package/registry/rules/local-development.md +7 -5
  65. package/registry/rules/software-development-lifecycle.md +104 -104
  66. package/registry/rules/successful-debugging-patterns.md +29 -16
  67. package/registry/rules/telemetry.md +67 -0
  68. package/registry/scripts/code-quality-check.sh +559 -559
  69. package/registry/scripts/detect-tautological-tests.sh +38 -38
  70. package/registry/scripts/evaluate-code-quality.ts +1 -1
  71. package/registry/scripts/prep-issue.sh +16 -3
  72. package/registry/scripts/validate-openapi-limits.ts +366 -365
  73. package/registry/scripts/validate-test-coverage.ts +280 -280
  74. package/registry/scripts/verify-pr-comments.sh +70 -70
  75. package/registry/scripts/verify-test-coverage.ts +1 -1
  76. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  77. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  78. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  79. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
  80. package/registry/workflows/bootstrap/create-architecture.md +2 -2
  81. package/registry/workflows/bootstrap/evaluate-code-quality.md +10 -4
  82. package/registry/workflows/bootstrap/verify-test-coverage.md +9 -3
  83. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  84. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  85. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  86. package/registry/workflows/customer-development/thank-customers.md +14 -2
  87. package/registry/workflows/customer-development/weekly-newsletter.md +27 -13
  88. package/registry/workflows/improve-fraim/contribute.md +32 -0
  89. package/registry/workflows/improve-fraim/file-issue.md +32 -0
  90. package/registry/workflows/marketing/hbr-article.md +73 -0
  91. package/registry/workflows/performance/analyze-performance.md +10 -4
  92. package/registry/workflows/product-building/design.md +3 -2
  93. package/registry/workflows/product-building/implement.md +10 -5
  94. package/registry/workflows/product-building/prep-issue.md +16 -18
  95. package/registry/workflows/product-building/resolve.md +8 -3
  96. package/registry/workflows/product-building/retrospect.md +3 -2
  97. package/registry/workflows/product-building/spec.md +5 -4
  98. package/registry/workflows/product-building/test.md +3 -2
  99. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  100. package/registry/workflows/replicate/replicate-discovery.md +336 -0
  101. package/registry/workflows/replicate/replicate-to-issues.md +319 -0
  102. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +3 -3
  103. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  104. package/tsconfig.json +2 -1
  105. package/.windsurf/rules/windsurf-rules.md +0 -7
  106. package/.windsurf/workflows/resolve-issue.md +0 -6
  107. package/.windsurf/workflows/retrospect.md +0 -6
  108. package/.windsurf/workflows/start-design.md +0 -6
  109. package/.windsurf/workflows/start-impl.md +0 -6
  110. package/.windsurf/workflows/start-spec.md +0 -6
  111. package/.windsurf/workflows/start-tests.md +0 -6
  112. package/bin/fraim.js +0 -23
  113. package/registry/scripts/build-scripts-generator.ts +0 -215
  114. package/registry/scripts/cleanup-branch.ts +0 -284
  115. package/registry/scripts/fraim-config.ts +0 -63
  116. package/registry/scripts/generate-engagement-emails.ts +0 -744
  117. package/registry/scripts/generic-issues-api.ts +0 -150
  118. package/registry/scripts/newsletter-helpers.ts +0 -874
  119. package/registry/scripts/openapi-generator.ts +0 -693
  120. package/registry/scripts/run-thank-you-workflow.ts +0 -122
  121. package/registry/scripts/send-newsletter-simple.ts +0 -104
  122. package/registry/scripts/send-thank-you-emails.ts +0 -57
  123. package/registry/workflows/replicate/re-implementation-strategy.md +0 -226
  124. package/registry/workflows/replicate/use-case-extraction.md +0 -135
  125. package/registry/workflows/replicate/visual-analysis.md +0 -154
  126. package/registry/workflows/replicate/website-discovery-analysis.md +0 -231
  127. package/sample_package.json +0 -18
  128. /package/registry/scripts/{replicate/comprehensive-explorer.py → comprehensive-explorer.py} +0 -0
  129. /package/registry/scripts/{replicate/interactive-explorer.py → interactive-explorer.py} +0 -0
  130. /package/registry/scripts/{replicate/scrape-site.py → scrape-site.py} +0 -0
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ /**
4
+ * Runner script for thank-you email workflow
5
+ * Called by AI agents to generate thank-you emails
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const generate_engagement_emails_1 = require("./generate-engagement-emails");
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
11
+ async function main() {
12
+ console.log('šŸŽÆ Starting thank-you email generation workflow...\n');
13
+ // Step 1: Get resolved issues
14
+ console.log('šŸ“‹ Step 1: Retrieving resolved issues...');
15
+ const issues = await (0, generate_engagement_emails_1.getResolvedIssues)();
16
+ console.log(`\nāœ… Found ${issues.length} resolved issues\n`);
17
+ // Step 2: Extract customer information
18
+ console.log('šŸ“§ Step 2: Extracting customer information...\n');
19
+ const customerMap = new Map();
20
+ for (const issue of issues) {
21
+ // Extract email and name from issue body or title
22
+ let customerEmail = null;
23
+ let customerName = null;
24
+ // Try to extract from body first
25
+ const bodyEmailMatch = issue.body?.match(/\*\*Reported by:\*\*\s+(.+?)\s+\((.+?)\)/);
26
+ if (bodyEmailMatch) {
27
+ customerName = bodyEmailMatch[1].trim();
28
+ customerEmail = bodyEmailMatch[2].trim();
29
+ }
30
+ // Try to extract from title (format: "email: summary")
31
+ if (!customerEmail) {
32
+ const titleEmailMatch = issue.title.match(/^([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}):/);
33
+ if (titleEmailMatch) {
34
+ customerEmail = titleEmailMatch[1];
35
+ }
36
+ }
37
+ if (!customerEmail) {
38
+ console.log(`āš ļø Issue #${issue.number}: Could not extract customer email, skipping`);
39
+ continue;
40
+ }
41
+ // Normalize email to lowercase for consistent matching
42
+ const normalizedEmail = customerEmail.toLowerCase();
43
+ // Add or update customer data
44
+ if (!customerMap.has(normalizedEmail)) {
45
+ customerMap.set(normalizedEmail, {
46
+ email: normalizedEmail,
47
+ name: customerName || 'Valued Customer',
48
+ issues: []
49
+ });
50
+ }
51
+ const customerData = customerMap.get(normalizedEmail);
52
+ customerData.issues.push({
53
+ number: issue.number,
54
+ title: issue.title,
55
+ body: issue.body || ''
56
+ });
57
+ }
58
+ console.log(`šŸ“Š Found ${customerMap.size} unique customers\n`);
59
+ // Step 3: Lookup executive data for each customer
60
+ console.log('šŸ” Step 3: Looking up executive data...\n');
61
+ const entries = Array.from(customerMap.entries());
62
+ for (const [email, customerData] of entries) {
63
+ try {
64
+ const executive = await (0, generate_engagement_emails_1.findExecutiveByEmail)(email);
65
+ if (executive) {
66
+ customerData.executiveId = executive.id;
67
+ customerData.executiveName = executive.name;
68
+ if (executive.id) {
69
+ const personaEmail = await (0, generate_engagement_emails_1.getPersonaEmailForExecutive)(executive.id);
70
+ customerData.personaEmail = personaEmail;
71
+ console.log(`āœ… ${email}: Found executive ${executive.name} (${executive.id}), Persona email: ${personaEmail}`);
72
+ }
73
+ else {
74
+ console.log(`āš ļø ${email}: Found executive ${executive.name} but no ID`);
75
+ }
76
+ }
77
+ else {
78
+ console.log(`āš ļø ${email}: No executive record found in database`);
79
+ }
80
+ }
81
+ catch (error) {
82
+ console.error(`āŒ ${email}: Error looking up executive:`, error);
83
+ }
84
+ }
85
+ console.log('\nšŸ“ Customer Data Summary:\n');
86
+ console.log(JSON.stringify(Array.from(customerMap.values()), null, 2));
87
+ // Save to temp file for AI agent to process
88
+ const outputPath = (0, path_1.join)(process.cwd(), 'temp-customer-data.json');
89
+ (0, fs_1.writeFileSync)(outputPath, JSON.stringify(Array.from(customerMap.values()), null, 2));
90
+ console.log(`\nšŸ’¾ Customer data saved to: ${outputPath}`);
91
+ }
92
+ main().catch(console.error);
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ /**
4
+ * Send Newsletter - Deterministic Script
5
+ *
6
+ * Simple script that users run after approving newsletter JSON.
7
+ * No decisions, just sends the newsletter.
8
+ *
9
+ * Usage:
10
+ * npm run send-newsletter # Send to all executives and potential customers
11
+ * npm run send-newsletter -- --showonly # List recipients without sending
12
+ * npm run send-newsletter -- --no-potential-customers # Send only to active executives
13
+ * npm run send-newsletter -- --only-potential-customers # Send only to potential customers
14
+ * npm run send-newsletter -- --email=user@example.com # Send to specific email
15
+ * npm run send-newsletter -- exec-XXX exec-YYY # Send to specific executive IDs
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const newsletter_helpers_1 = require("./newsletter-helpers");
19
+ const fs_1 = require("fs");
20
+ const path_1 = require("path");
21
+ async function main() {
22
+ console.log('šŸ“§ Weekly Newsletter Sender\n');
23
+ // Parse command line arguments
24
+ const args = process.argv.slice(2);
25
+ const showOnly = args.includes('--showonly');
26
+ const excludePotentialCustomers = args.includes('--no-potential-customers');
27
+ const onlyPotentialCustomers = args.includes('--only-potential-customers');
28
+ const filterEmails = [];
29
+ const filterExecIds = [];
30
+ // Parse email filters (--email=address)
31
+ args.forEach(arg => {
32
+ if (arg.startsWith('--email=')) {
33
+ filterEmails.push(arg.substring(8));
34
+ }
35
+ else if (arg.startsWith('exec-')) {
36
+ filterExecIds.push(arg);
37
+ }
38
+ });
39
+ // Find the most recent newsletter
40
+ const newslettersDir = (0, path_1.join)(process.cwd(), 'docs', 'customer-development', 'newsletters');
41
+ if (!(0, fs_1.existsSync)(newslettersDir)) {
42
+ console.error('āŒ Error: No newsletters directory found');
43
+ console.error(' Expected: docs/customer-development/newsletters/');
44
+ console.error(' Generate a newsletter first using the AI agent workflow');
45
+ process.exit(1);
46
+ }
47
+ const files = (0, fs_1.readdirSync)(newslettersDir)
48
+ .filter(f => f.startsWith('newsletter-') && f.endsWith('.json'))
49
+ .sort()
50
+ .reverse(); // Most recent first
51
+ if (files.length === 0) {
52
+ console.error('āŒ Error: No newsletter files found');
53
+ console.error(' Generate a newsletter first using the AI agent workflow');
54
+ process.exit(1);
55
+ }
56
+ const latestNewsletter = files[0];
57
+ const newsletterPath = (0, path_1.join)(newslettersDir, latestNewsletter);
58
+ console.log(`šŸ“„ Found newsletter: ${latestNewsletter}`);
59
+ if (showOnly) {
60
+ console.log(`šŸ“‹ Listing recipients (--showonly mode - no emails will be sent)...\n`);
61
+ }
62
+ else if (filterEmails.length > 0 || filterExecIds.length > 0) {
63
+ console.log(`šŸ“§ Sending to filtered recipients...\n`);
64
+ console.log(`āš ļø This will send real emails. Make sure the newsletter is approved!`);
65
+ }
66
+ else {
67
+ if (onlyPotentialCustomers) {
68
+ console.log(`šŸ“§ Sending to potential customers only...\n`);
69
+ }
70
+ else {
71
+ console.log(`šŸ“§ Sending to all active executives${excludePotentialCustomers ? '' : ' and potential customers'}...\n`);
72
+ }
73
+ console.log(`āš ļø This will send real emails. Make sure the newsletter is approved!`);
74
+ }
75
+ console.log(` Preview: ${newsletterPath.replace('.json', '.html')}\n`);
76
+ // Determine what to include based on flags
77
+ const includePotentialCustomers = onlyPotentialCustomers || !excludePotentialCustomers;
78
+ const includeExecutives = !onlyPotentialCustomers;
79
+ // Send newsletter (or show only)
80
+ await (0, newsletter_helpers_1.sendNewsletterToExecutives)(newsletterPath, filterEmails.length > 0 ? filterEmails : undefined, filterExecIds.length > 0 ? filterExecIds : undefined, includePotentialCustomers, showOnly, includeExecutives);
81
+ }
82
+ main().catch(error => {
83
+ console.error('āŒ Error:', error.message);
84
+ process.exit(1);
85
+ });
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ /**
4
+ * Send Thank-You Emails from Candidates JSON File
5
+ *
6
+ * Usage:
7
+ * npm run send-thank-you -- <filename> [exec-id-1] [exec-id-2] ...
8
+ *
9
+ * Examples:
10
+ * npm run send-thank-you -- docs/customer-development/thank-you-notes/thank-you-candidates-2025-10-29.json
11
+ * npm run send-thank-you -- docs/customer-development/thank-you-notes/thank-you-candidates-2025-10-29.json exec-123 exec-456
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ const generate_engagement_emails_1 = require("./generate-engagement-emails");
15
+ async function main() {
16
+ const args = process.argv.slice(2);
17
+ if (args.length === 0) {
18
+ console.error('āŒ Error: Missing required argument: filename');
19
+ console.error('');
20
+ console.error('Usage: npm run send-thank-you -- <filename> [exec-id-1] [exec-id-2] ...');
21
+ console.error('');
22
+ console.error('Examples:');
23
+ console.error(' npm run send-thank-you -- docs/customer-development/thank-you-notes/thank-you-candidates-2025-10-29.json');
24
+ console.error(' npm run send-thank-you -- docs/customer-development/thank-you-notes/thank-you-candidates-2025-10-29.json exec-1760627251980-31qmjy5y4');
25
+ process.exit(1);
26
+ }
27
+ const filename = args[0];
28
+ const execIds = args.slice(1).filter(arg => arg.startsWith('exec-'));
29
+ if (execIds.length === 0) {
30
+ // Send to all pending candidates
31
+ console.log(`šŸ“§ Sending emails from: ${filename}`);
32
+ console.log(`šŸŽÆ Sending to ALL pending candidates`);
33
+ await (0, generate_engagement_emails_1.sendCustomerMail)(filename);
34
+ }
35
+ else if (execIds.length === 1) {
36
+ // Send to specific executive
37
+ console.log(`šŸ“§ Sending emails from: ${filename}`);
38
+ console.log(`šŸŽÆ Sending to executive: ${execIds[0]}`);
39
+ await (0, generate_engagement_emails_1.sendCustomerMail)(filename, execIds[0]);
40
+ }
41
+ else {
42
+ // Multiple executives - send to each one
43
+ console.log(`šŸ“§ Sending emails from: ${filename}`);
44
+ console.log(`šŸŽÆ Sending to ${execIds.length} executives: ${execIds.join(', ')}`);
45
+ for (const execId of execIds) {
46
+ console.log(`\nšŸ“§ Processing executive: ${execId}`);
47
+ await (0, generate_engagement_emails_1.sendCustomerMail)(filename, execId);
48
+ }
49
+ }
50
+ }
51
+ main().catch(error => {
52
+ console.error('āŒ Error:', error);
53
+ process.exit(1);
54
+ });
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * FRAIM Generic OpenAPI Limits Validator
5
+ * This script validates OpenAPI file sizes against specified limits.
6
+ *
7
+ * Validates ChatGPT limitations for any project:
8
+ * - instructions.txt: 8,000 character limit
9
+ * - OpenAPI endpoints: 35 endpoint limit
10
+ * - OpenAPI descriptions: 300 character limit per operation
11
+ * - Missing operationIds
12
+ * - Missing schema definitions
13
+ * - x-openai-isConsequential on non-GET endpoints
14
+ *
15
+ * Usage:
16
+ * npx tsx scripts/validate-openapi-limits.ts [instructions-file] [openapi-file]
17
+ *
18
+ * Defaults:
19
+ * instructions-file: src/openapi/instructions.txt
20
+ * openapi-file: src/openapi/openapi.json
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.validateAll = validateAll;
24
+ const fs_1 = require("fs");
25
+ const INSTRUCTIONS_LIMIT = 8000;
26
+ const OPENAPI_ENDPOINT_LIMIT = 35;
27
+ const OPENAPI_DESC_LIMIT = 300;
28
+ /**
29
+ * Collect all schema references from an object recursively
30
+ */
31
+ function collectSchemaRefs(obj, refs = new Set()) {
32
+ if (typeof obj !== 'object' || obj === null) {
33
+ return refs;
34
+ }
35
+ if (Array.isArray(obj)) {
36
+ obj.forEach(item => collectSchemaRefs(item, refs));
37
+ return refs;
38
+ }
39
+ if (obj.$ref && typeof obj.$ref === 'string') {
40
+ // Extract schema name from #/components/schemas/SchemaName
41
+ const match = obj.$ref.match(/#\/components\/schemas\/(.+)$/);
42
+ if (match) {
43
+ refs.add(match[1]);
44
+ }
45
+ }
46
+ Object.values(obj).forEach(value => collectSchemaRefs(value, refs));
47
+ return refs;
48
+ }
49
+ /**
50
+ * Validate instructions.txt length
51
+ */
52
+ function validateInstructions(instructionsPath) {
53
+ const result = {
54
+ passed: true,
55
+ errors: [],
56
+ warnings: []
57
+ };
58
+ if (!(0, fs_1.existsSync)(instructionsPath)) {
59
+ result.warnings.push(`instructions.txt not found at ${instructionsPath}`);
60
+ return result;
61
+ }
62
+ try {
63
+ const instructionsContent = (0, fs_1.readFileSync)(instructionsPath, 'utf-8');
64
+ const instructionsLength = instructionsContent.length;
65
+ console.log(`šŸ“„ instructions.txt: ${instructionsLength} characters (limit: ${INSTRUCTIONS_LIMIT})`);
66
+ if (instructionsLength > INSTRUCTIONS_LIMIT) {
67
+ result.passed = false;
68
+ result.errors.push(`instructions.txt exceeds ${INSTRUCTIONS_LIMIT} character limit (${instructionsLength} chars). Move detailed content to KB files.`);
69
+ console.log(` āŒ FAILED: Exceeds limit`);
70
+ }
71
+ else {
72
+ console.log(` āœ… PASSED: Within limit\n`);
73
+ }
74
+ }
75
+ catch (error) {
76
+ result.passed = false;
77
+ result.errors.push(`Failed to read instructions.txt: ${error.message}`);
78
+ }
79
+ return result;
80
+ }
81
+ /**
82
+ * Validate OpenAPI specification
83
+ */
84
+ function validateOpenAPI(openapiPath) {
85
+ const result = {
86
+ passed: true,
87
+ errors: [],
88
+ warnings: []
89
+ };
90
+ if (!(0, fs_1.existsSync)(openapiPath)) {
91
+ result.warnings.push(`OpenAPI file not found at ${openapiPath}`);
92
+ return result;
93
+ }
94
+ try {
95
+ const openapiContent = (0, fs_1.readFileSync)(openapiPath, 'utf-8');
96
+ const openapiSpec = JSON.parse(openapiContent);
97
+ // Count endpoints (operations) and validate
98
+ let endpointCount = 0;
99
+ const endpointList = [];
100
+ const longDescriptions = [];
101
+ const missingOperationIds = [];
102
+ const allSchemaRefs = new Set();
103
+ const missingConsequential = [];
104
+ const wrongConsequential = [];
105
+ if (openapiSpec.paths) {
106
+ for (const [path, methods] of Object.entries(openapiSpec.paths)) {
107
+ for (const [method, details] of Object.entries(methods)) {
108
+ if (typeof details === 'object' && details !== null) {
109
+ // Skip if it's a parameter definition or response definition
110
+ if (['parameters', 'responses', 'requestBody'].includes(method)) {
111
+ continue;
112
+ }
113
+ endpointCount++;
114
+ endpointList.push(`${method.toUpperCase()} ${path}`);
115
+ // Check for operationId
116
+ if (!details.operationId) {
117
+ missingOperationIds.push({
118
+ path,
119
+ method: method.toUpperCase()
120
+ });
121
+ }
122
+ // Check description length
123
+ if (details.description) {
124
+ const descLength = details.description.length;
125
+ if (descLength > OPENAPI_DESC_LIMIT) {
126
+ longDescriptions.push({
127
+ path,
128
+ method: method.toUpperCase(),
129
+ length: descLength
130
+ });
131
+ }
132
+ }
133
+ // Collect all schema references from this operation
134
+ collectSchemaRefs(details, allSchemaRefs);
135
+ // Check for x-openai-isConsequential on non-GET endpoints
136
+ const nonGetMethods = ['post', 'put', 'patch', 'delete'];
137
+ if (nonGetMethods.includes(method.toLowerCase())) {
138
+ const hasConsequential = details.hasOwnProperty('x-openai-isConsequential');
139
+ const consequentialValue = details['x-openai-isConsequential'];
140
+ // ALL non-GET endpoints should have x-openai-isConsequential: false
141
+ if (!hasConsequential) {
142
+ missingConsequential.push({
143
+ path,
144
+ method: method.toUpperCase()
145
+ });
146
+ }
147
+ else if (consequentialValue !== false) {
148
+ wrongConsequential.push({
149
+ path,
150
+ method: method.toUpperCase(),
151
+ value: consequentialValue
152
+ });
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ // Also collect schema refs from components/responses and components/parameters if they exist
160
+ if (openapiSpec.components) {
161
+ if (openapiSpec.components.responses) {
162
+ Object.values(openapiSpec.components.responses).forEach((response) => {
163
+ collectSchemaRefs(response, allSchemaRefs);
164
+ });
165
+ }
166
+ if (openapiSpec.components.parameters) {
167
+ Object.values(openapiSpec.components.parameters).forEach((param) => {
168
+ collectSchemaRefs(param, allSchemaRefs);
169
+ });
170
+ }
171
+ }
172
+ // Check for missing schema definitions
173
+ const definedSchemas = new Set();
174
+ if (openapiSpec.components && openapiSpec.components.schemas) {
175
+ Object.keys(openapiSpec.components.schemas).forEach(schemaName => {
176
+ definedSchemas.add(schemaName);
177
+ });
178
+ }
179
+ const missingSchemas = [];
180
+ allSchemaRefs.forEach(refName => {
181
+ if (!definedSchemas.has(refName)) {
182
+ missingSchemas.push(refName);
183
+ }
184
+ });
185
+ // Validate endpoint count
186
+ console.log(`šŸ“‹ OpenAPI endpoints: ${endpointCount} (limit: ${OPENAPI_ENDPOINT_LIMIT})`);
187
+ if (endpointCount > OPENAPI_ENDPOINT_LIMIT) {
188
+ result.passed = false;
189
+ result.errors.push(`OpenAPI spec exceeds ${OPENAPI_ENDPOINT_LIMIT} endpoint limit (${endpointCount} endpoints). ChatGPT cannot handle more than ${OPENAPI_ENDPOINT_LIMIT} endpoints.`);
190
+ console.log(` āŒ FAILED: Too many endpoints`);
191
+ console.log(` Endpoints:`);
192
+ endpointList.slice(0, 10).forEach(ep => console.log(` - ${ep}`));
193
+ if (endpointCount > 10) {
194
+ console.log(` ... and ${endpointCount - 10} more`);
195
+ }
196
+ }
197
+ else {
198
+ console.log(` āœ… PASSED: Within limit`);
199
+ }
200
+ // Check description lengths
201
+ if (longDescriptions.length > 0) {
202
+ console.log(`\nšŸ“ OpenAPI descriptions: ${longDescriptions.length} exceed ${OPENAPI_DESC_LIMIT} char limit`);
203
+ longDescriptions.forEach(({ path, method, length }) => {
204
+ result.passed = false;
205
+ result.errors.push(`OpenAPI operation ${method} ${path} description exceeds ${OPENAPI_DESC_LIMIT} character limit (${length} chars). Shorten description or move details to KB files.`);
206
+ console.log(` āŒ ${method} ${path}: ${length} chars`);
207
+ });
208
+ }
209
+ else {
210
+ console.log(`\nšŸ“ OpenAPI descriptions: All within ${OPENAPI_DESC_LIMIT} char limit`);
211
+ console.log(` āœ… PASSED: All descriptions within limit`);
212
+ }
213
+ // Check for missing operationIds
214
+ if (missingOperationIds.length > 0) {
215
+ console.log(`\nšŸ”‘ OpenAPI operationIds: ${missingOperationIds.length} missing`);
216
+ missingOperationIds.forEach(({ path, method }) => {
217
+ result.passed = false;
218
+ result.errors.push(`OpenAPI operation ${method} ${path} is missing required 'operationId' field. Add a unique operationId (e.g., "getTask", "createTask").`);
219
+ console.log(` āŒ ${method} ${path}: Missing operationId`);
220
+ });
221
+ }
222
+ else {
223
+ console.log(`\nšŸ”‘ OpenAPI operationIds: All operations have operationId`);
224
+ console.log(` āœ… PASSED: All operations have operationId`);
225
+ }
226
+ // Check for missing schema definitions
227
+ if (missingSchemas.length > 0) {
228
+ console.log(`\nšŸ“š OpenAPI schemas: ${missingSchemas.length} referenced but not defined`);
229
+ missingSchemas.forEach(schemaName => {
230
+ result.passed = false;
231
+ result.errors.push(`OpenAPI schema '${schemaName}' is referenced but not defined in components.schemas. Add the schema definition to components.schemas.`);
232
+ console.log(` āŒ Missing schema: ${schemaName}`);
233
+ });
234
+ }
235
+ else {
236
+ console.log(`\nšŸ“š OpenAPI schemas: All referenced schemas are defined`);
237
+ console.log(` āœ… PASSED: All schema references resolve`);
238
+ }
239
+ // Check for missing or incorrect x-openai-isConsequential on non-GET endpoints
240
+ const allConsequentialIssues = [...missingConsequential, ...wrongConsequential];
241
+ if (allConsequentialIssues.length > 0) {
242
+ console.log(`\nšŸ” OpenAPI x-openai-isConsequential: ${allConsequentialIssues.length} non-GET endpoint(s) missing or incorrectly set`);
243
+ missingConsequential.forEach(({ path, method }) => {
244
+ result.passed = false;
245
+ result.errors.push(`OpenAPI operation ${method} ${path} is missing required 'x-openai-isConsequential: false' field. Add this field to bypass ChatGPT confirmation prompts.`);
246
+ console.log(` āŒ ${method} ${path}: Missing x-openai-isConsequential`);
247
+ });
248
+ wrongConsequential.forEach(({ path, method, value }) => {
249
+ result.passed = false;
250
+ result.errors.push(`OpenAPI operation ${method} ${path} has x-openai-isConsequential set to ${JSON.stringify(value)} but should be false. Non-GET endpoints should have x-openai-isConsequential: false to bypass ChatGPT confirmation prompts.`);
251
+ console.log(` āŒ ${method} ${path}: x-openai-isConsequential is ${JSON.stringify(value)}, should be false`);
252
+ });
253
+ }
254
+ else {
255
+ console.log(`\nšŸ” OpenAPI x-openai-isConsequential: All non-GET endpoints have x-openai-isConsequential: false`);
256
+ console.log(` āœ… PASSED: All non-GET endpoints configured to bypass confirmation`);
257
+ }
258
+ }
259
+ catch (error) {
260
+ result.passed = false;
261
+ result.errors.push(`Failed to parse OpenAPI JSON: ${error.message}`);
262
+ console.log(` āŒ ERROR: ${error.message}`);
263
+ }
264
+ return result;
265
+ }
266
+ /**
267
+ * Main validation function
268
+ */
269
+ function validateAll(instructionsPath = 'src/openapi/instructions.txt', openapiPath = 'src/openapi/openapi.json') {
270
+ console.log('šŸ” Validating OpenAPI and instructions limits...\n');
271
+ const instructionsResult = validateInstructions(instructionsPath);
272
+ const openapiResult = validateOpenAPI(openapiPath);
273
+ // Combine results
274
+ const allErrors = [...instructionsResult.errors, ...openapiResult.errors];
275
+ const allWarnings = [...instructionsResult.warnings, ...openapiResult.warnings];
276
+ const passed = instructionsResult.passed && openapiResult.passed;
277
+ // Summary
278
+ console.log('\n' + '='.repeat(50));
279
+ console.log('šŸ“Š VALIDATION SUMMARY');
280
+ console.log('='.repeat(50));
281
+ if (allWarnings.length > 0) {
282
+ console.log('\nāš ļø WARNINGS:');
283
+ allWarnings.forEach(w => console.log(` - ${w}`));
284
+ }
285
+ if (allErrors.length > 0) {
286
+ console.log('\nāŒ ERRORS:');
287
+ allErrors.forEach(e => console.log(` - ${e}`));
288
+ console.log('\nšŸ’” Solutions:');
289
+ console.log(' - Move detailed content from instructions.txt to KB files (KB-*.txt)');
290
+ console.log(' - Reduce number of OpenAPI endpoints (merge or remove unused endpoints)');
291
+ console.log(' - Shorten OpenAPI operation descriptions, reference KB files for details');
292
+ console.log(' - Add missing operationId fields to all operations (e.g., "getTask", "createTask")');
293
+ console.log(' - Define missing schemas in components.schemas section');
294
+ console.log(' - Add x-openai-isConsequential: false to all non-GET endpoints (POST, PUT, PATCH, DELETE)');
295
+ console.log('\nāŒ BUILD FAILED: Fix errors above before proceeding\n');
296
+ return false;
297
+ }
298
+ else {
299
+ console.log('\nāœ… ALL CHECKS PASSED');
300
+ console.log(' Instructions and OpenAPI spec are within ChatGPT limits\n');
301
+ return true;
302
+ }
303
+ }
304
+ // Run if executed directly
305
+ // @ts-ignore
306
+ if (import.meta.url === `file://${process.argv[1]}`) {
307
+ const instructionsPath = process.argv[2] || 'src/openapi/instructions.txt';
308
+ const openapiPath = process.argv[3] || 'src/openapi/openapi.json';
309
+ const passed = validateAll(instructionsPath, openapiPath);
310
+ process.exit(passed ? 0 : 1);
311
+ }