strray-ai 1.15.28 → 1.15.29

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 (113) hide show
  1. package/AGENTS.md +1 -3
  2. package/package.json +1 -4
  3. package/scripts/mjs/validate-postinstall-config.mjs +332 -62
  4. package/scripts/node/postinstall.cjs +12 -43
  5. package/scripts/node/universal-version-manager.js +2 -2
  6. package/scripts/mjs/FIX_SUMMARY.md +0 -143
  7. package/scripts/mjs/analyze-imports.mjs +0 -155
  8. package/scripts/mjs/debug-rules.mjs +0 -80
  9. package/scripts/mjs/demo-clickable-monitoring.mjs +0 -142
  10. package/scripts/mjs/monitor-framework-orchestration.mjs +0 -238
  11. package/scripts/mjs/monitor-package.mjs +0 -50
  12. package/scripts/mjs/monitoring-daemon.mjs +0 -66
  13. package/scripts/mjs/run-all-tests.mjs +0 -333
  14. package/scripts/mjs/run-performance-gates.mjs +0 -80
  15. package/scripts/mjs/run-simulations.mjs +0 -93
  16. package/scripts/mjs/scenario-data-processor.py +0 -122
  17. package/scripts/mjs/smart-test-runner.mjs +0 -566
  18. package/scripts/mjs/test-agent-configs.mjs +0 -128
  19. package/scripts/mjs/test-agents.mjs +0 -170
  20. package/scripts/mjs/test-auto-creation-flow.mjs +0 -123
  21. package/scripts/mjs/test-ci-cd-integration.mjs +0 -113
  22. package/scripts/mjs/test-complex-orchestration.mjs +0 -79
  23. package/scripts/mjs/test-comprehensive-path-resolution.mjs +0 -134
  24. package/scripts/mjs/test-configuration-validation.mjs +0 -241
  25. package/scripts/mjs/test-consumer-validation.mjs +0 -916
  26. package/scripts/mjs/test-data-processor.py +0 -51
  27. package/scripts/mjs/test-enforcement-e2e.mjs +0 -80
  28. package/scripts/mjs/test-enforcer-comprehensive.mjs +0 -219
  29. package/scripts/mjs/test-final-consumer-validation.mjs +0 -352
  30. package/scripts/mjs/test-framework-boot.mjs +0 -81
  31. package/scripts/mjs/test-framework-integration.mjs +0 -215
  32. package/scripts/mjs/test-integration.mjs +0 -79
  33. package/scripts/mjs/test-mcp-registration.mjs +0 -374
  34. package/scripts/mjs/test-mcp-servers.mjs +0 -230
  35. package/scripts/mjs/test-orchestrator-complex.mjs +0 -79
  36. package/scripts/mjs/test-orchestrator-simple.mjs +0 -77
  37. package/scripts/mjs/test-path-resolver.mjs +0 -25
  38. package/scripts/mjs/test-postinstall-files.mjs +0 -321
  39. package/scripts/mjs/test-processor-pipeline.mjs +0 -125
  40. package/scripts/mjs/test-quarantine.mjs +0 -250
  41. package/scripts/mjs/test-rules.mjs +0 -81
  42. package/scripts/mjs/test-simple-prompt.mjs +0 -45
  43. package/scripts/mjs/test-simulation.mjs +0 -84
  44. package/scripts/mjs/test-skill-routing.mjs +0 -194
  45. package/scripts/mjs/test-skills-comprehensive.mjs +0 -264
  46. package/scripts/mjs/test-skills-coverage.mjs +0 -266
  47. package/scripts/mjs/test-skills-mcp-integration.mjs +0 -354
  48. package/scripts/mjs/test-stringray-plugin.mjs +0 -285
  49. package/scripts/mjs/trigger-and-monitor-framework.mjs +0 -219
  50. package/scripts/mjs/update-performance-baselines.mjs +0 -78
  51. package/scripts/mjs/validate-phase1.mjs +0 -201
  52. package/scripts/mjs/verify-orchestration.mjs +0 -178
  53. package/scripts/mjs/verify-pipeline-end-to-end.mjs +0 -237
  54. package/scripts/mjs/verify-plugin-paths.mjs +0 -289
  55. package/scripts/node/README.md +0 -127
  56. package/scripts/node/activate-self-direction.js +0 -72
  57. package/scripts/node/add-parent-monitoring.cjs +0 -53
  58. package/scripts/node/add-timeout-shutdown.cjs +0 -55
  59. package/scripts/node/analyze-activity-log.cjs +0 -188
  60. package/scripts/node/analyzer-agent-runner.js +0 -328
  61. package/scripts/node/boot-check.mjs +0 -203
  62. package/scripts/node/ci-cd-orchestrator.cjs +0 -266
  63. package/scripts/node/cleanup-console-logs.js +0 -108
  64. package/scripts/node/cleanup-doc-versions.js +0 -153
  65. package/scripts/node/cleanup-repository.js +0 -116
  66. package/scripts/node/debug-plugin.cjs +0 -87
  67. package/scripts/node/dependency-scan.cjs +0 -145
  68. package/scripts/node/dev-setup.cjs +0 -68
  69. package/scripts/node/enforce-version-compliance.sh.bak +0 -143
  70. package/scripts/node/enhance-mcp-shutdown.cjs +0 -62
  71. package/scripts/node/fix-framework-logger-paths.cjs +0 -79
  72. package/scripts/node/fix-mcp-capabilities.js +0 -87
  73. package/scripts/node/fix-mcp-shutdown.cjs +0 -53
  74. package/scripts/node/generate-activity-report.js +0 -27
  75. package/scripts/node/generate-autonomous-report.cjs +0 -427
  76. package/scripts/node/generate-changelog.js +0 -230
  77. package/scripts/node/generate-phase1-report.js +0 -28
  78. package/scripts/node/generate-reflection-index.js +0 -208
  79. package/scripts/node/generate-skills.cjs +0 -217
  80. package/scripts/node/install.cjs +0 -632
  81. package/scripts/node/kernel-e2e-test.mjs +0 -168
  82. package/scripts/node/kernel-framework-test.mjs +0 -127
  83. package/scripts/node/kernel-live-test.mjs +0 -147
  84. package/scripts/node/kernel-real-framework-test.mjs +0 -103
  85. package/scripts/node/kernel-update.cjs +0 -379
  86. package/scripts/node/performance-report.js +0 -56
  87. package/scripts/node/pre-commit-version-validation.js +0 -122
  88. package/scripts/node/pre-publish-check.sh +0 -78
  89. package/scripts/node/profiling-demo.js +0 -108
  90. package/scripts/node/reflection-check.sh +0 -423
  91. package/scripts/node/release-tweet-multi.mjs.bak +0 -269
  92. package/scripts/node/release-tweet-single.mjs +0 -323
  93. package/scripts/node/release-tweet.mjs +0 -268
  94. package/scripts/node/release.mjs +0 -190
  95. package/scripts/node/remove-console-logs.mjs +0 -83
  96. package/scripts/node/remove-version-headers.mjs +0 -123
  97. package/scripts/node/run-postprocessor.js +0 -59
  98. package/scripts/node/setup-ci-paths.cjs +0 -62
  99. package/scripts/node/setup.cjs +0 -116
  100. package/scripts/node/sync-readme-features.js +0 -166
  101. package/scripts/node/test-install.cjs +0 -79
  102. package/scripts/node/test-plugin-comprehensive.js +0 -279
  103. package/scripts/node/test-postinstall.js +0 -1
  104. package/scripts/node/test-session-management.js +0 -135
  105. package/scripts/node/test-simple-names-comprehensive.mjs +0 -221
  106. package/scripts/node/test-simple-names.mjs +0 -39
  107. package/scripts/node/trigger-report.js +0 -15
  108. package/scripts/node/update-models-global.cjs +0 -82
  109. package/scripts/node/validate-codex.mjs +0 -148
  110. package/scripts/node/validate-oh-my-opencode-integration.cjs +0 -203
  111. package/scripts/node/validate-postinstall-config.mjs +0 -348
  112. package/scripts/node/value-tweet.mjs +0 -305
  113. package/scripts/node/value-tweet.mjs.backup +0 -487
package/AGENTS.md CHANGED
@@ -166,7 +166,7 @@ npx strray-ai validate # Validate installation
166
166
  npx strray-ai capabilities # Show all features
167
167
  npx strray-ai report # Generate reports
168
168
  npx strray-ai analytics # Pattern analytics
169
- npx strray-ai calibrate # Calibrate complexity
169
+
170
170
  npm run test:pipelines # Pipeline integration tests
171
171
  ```
172
172
 
@@ -409,7 +409,6 @@ StringRay uses complexity scoring to route tasks to appropriate agents:
409
409
 
410
410
  ```bash
411
411
  # Calibrate complexity scoring
412
- npx strray-ai calibrate
413
412
 
414
413
  # View current complexity settings
415
414
  cat .opencode/strray/features.json | jq '.complexity'
@@ -469,7 +468,6 @@ Control how agents are spawned and coordinated:
469
468
  | `npx strray-ai health` | Run health check | Verify installation |
470
469
  | `npx strray-ai validate` | Run full validation suite | Pre-commit validation |
471
470
  | `npx strray-ai capabilities` | List all available features | Discover capabilities |
472
- | `npx strray-ai calibrate` | Recalibrate complexity scoring | After major refactors |
473
471
  | `npx strray-ai report` | Generate analytics reports | Review performance |
474
472
  | `npx strray-ai analytics` | View pattern analytics | Understand agent behavior |
475
473
  | `npx strray-ai config` | Manage configuration | Tune settings |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strray-ai",
3
- "version": "1.15.28",
3
+ "version": "1.15.29",
4
4
  "description": "⚡ StringRay ⚡: Bulletproof AI orchestration with systematic error prevention. Zero dead ends. Ship clean, tested, optimized code — every time.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -80,10 +80,7 @@
80
80
  "validate": "node scripts/validate-stringray-comprehensive.js",
81
81
  "validate:quick": "node scripts/validate-stringray-comprehensive.js --quick",
82
82
  "validate:full": "node scripts/validate-stringray-comprehensive.js --full",
83
- "test:modules": "node scripts/test-es-modules.mjs",
84
- "fix:modules": "node scripts/fix-module-compatibility.js",
85
83
  "test:unified": "node scripts/test/test-unified-framework.mjs",
86
- "test:plugin": "node scripts/test/test-stray-plugin.mjs",
87
84
  "test:consumer": "node scripts/test/test-consumer-readiness.cjs",
88
85
  "config:setup": "node scripts/config/utils.js setup-dev",
89
86
  "monitoring:start": "node scripts/monitoring/daemon.js start",
@@ -1,78 +1,348 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Validate Postinstall Configuration
5
- *
6
- * Verifies that postinstall correctly set up all required files.
4
+ * Postinstall Configuration Validator
5
+ *
6
+ * Tests that all critical configuration files and folders are properly copied
7
+ * during npm install, including .claude/, .opencode/, and related files
7
8
  */
8
9
 
9
- import fs from 'fs';
10
- import path from 'path';
11
- import { fileURLToPath } from 'url';
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import os from "os";
12
13
 
13
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ class PostinstallConfigValidator {
15
+ constructor() {
16
+ this.results = { passed: [], failed: [] };
17
+ }
18
+
19
+ async validateConfiguration() {
20
+ console.log("📁 POSTINSTALL CONFIGURATION VALIDATOR");
21
+ console.log("=====================================");
22
+
23
+ const tests = [
24
+ this.validateProjectFiles.bind(this),
25
+ this.validateOpencodeConfig.bind(this),
26
+ this.validateClaudeConfig.bind(this),
27
+ this.validateMCPConfig.bind(this),
28
+ ];
14
29
 
15
- const errors = [];
16
- const warnings = [];
30
+ for (const test of tests) {
31
+ await test();
32
+ }
17
33
 
18
- function check(file, description) {
19
- const fullPath = path.join(process.cwd(), file);
20
- if (fs.existsSync(fullPath)) {
21
- console.log(`✅ ${description}: ${file}`);
22
- return true;
23
- } else {
24
- console.log(`❌ ${description}: ${file} NOT FOUND`);
25
- errors.push(`${description} not found: ${file}`);
26
- return false;
34
+ this.printSummary();
35
+ return this.results.failed.length === 0;
27
36
  }
28
- }
29
37
 
30
- console.log('🔍 Validating Postinstall Configuration...\n');
38
+ async validateProjectFiles() {
39
+ console.log("\n📄 Testing Project Configuration Files...");
31
40
 
32
- // Check opencode.json (main config)
33
- check('opencode.json', 'OpenCode configuration');
41
+ const requiredFiles = [
42
+ { path: "opencode.json", description: "OpenCode base configuration" },
43
+ {
44
+ path: ".opencode/package.json",
45
+ description: "OpenCode package config",
46
+ },
47
+ ];
48
+
49
+ // Optional files (not required for CI/test environments)
50
+ const optionalFiles = [
51
+ {
52
+ description: "OpenCode main config (optional - can cause boot issues)",
53
+ },
54
+ { path: ".mcp.json", description: "MCP server configuration (lazy loaded)" },
55
+ { path: ".opencode/README.md", description: "OpenCode documentation" },
56
+ ];
34
57
 
35
- // Check plugin (can be in .opencode/plugins/ or .opencode/plugin/)
36
- const pluginLocations = ['.opencode/plugins/strray-codex-injection.js', '.opencode/plugin/strray-codex-injection.js'];
37
- const pluginFound = pluginLocations.some(loc => fs.existsSync(path.join(process.cwd(), loc)));
38
- if (pluginFound) {
39
- const foundLoc = pluginLocations.find(loc => fs.existsSync(path.join(process.cwd(), loc)));
40
- console.log(`✅ Plugin file: ${foundLoc}`);
41
- } else {
42
- console.log(`❌ Plugin file: strray-codex-injection.js NOT FOUND`);
43
- errors.push('Plugin file not found');
44
- }
58
+ let allPresent = true;
59
+ for (const file of requiredFiles) {
60
+ try {
61
+ if (fs.existsSync(file.path)) {
62
+ console.log(` ${file.path} - ${file.description}`);
63
+ } else {
64
+ console.log(` ❌ ${file.path} - ${file.description} (MISSING)`);
65
+ allPresent = false;
66
+ }
67
+ } catch (error) {
68
+ console.log(
69
+ ` ❌ ${file.path} - Error checking file: ${error.message}`,
70
+ );
71
+ allPresent = false;
72
+ }
73
+ }
45
74
 
46
- // Check agents
47
- if (fs.existsSync(path.join(process.cwd(), '.opencode/agents'))) {
48
- const agents = fs.readdirSync(path.join(process.cwd(), '.opencode/agents'));
49
- console.log(`✅ Agents directory: ${agents.length} agents found`);
50
- } else {
51
- errors.push('Agents directory not found');
52
- }
75
+ // Check optional files (don't fail if missing)
76
+ for (const file of optionalFiles) {
77
+ try {
78
+ if (fs.existsSync(file.path)) {
79
+ console.log(` ✅ ${file.path} - ${file.description}`);
80
+ } else {
81
+ console.log(` ℹ️ ${file.path} - ${file.description} (OPTIONAL - not present)`);
82
+ }
83
+ } catch (error) {
84
+ console.log(` ℹ️ ${file.path} - Error checking file: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ if (allPresent) {
89
+ this.results.passed.push("Project Configuration Files");
90
+ } else {
91
+ this.results.failed.push({
92
+ test: "Project Configuration Files",
93
+ error: "Some required configuration files are missing",
94
+ });
95
+ }
96
+ }
97
+
98
+ async validateOpencodeConfig() {
99
+ console.log("\n🛠️ Testing OpenCode Configuration...");
100
+
101
+ // OpenCode.json was removed - config is in opencode.json at project root
102
+ // Framework uses opencode.json (root) instead
103
+ console.log(" ℹ️ OpenCode.json removed — config is in opencode.json at project root");
104
+ this.results.passed.push({
105
+ test: "OpenCode Configuration",
106
+ details: "Skipped - uses opencode.json at root"
107
+ });
108
+ return;
109
+
110
+ return;
111
+
112
+ // Leftover code from old implementation - to be removed
113
+ // Check for plugin registration
114
+ if (config.plugin && Array.isArray(config.plugin)) {
115
+ const hasStringRayPlugin = config.plugin.some((p) =>
116
+ p.toLowerCase().includes("strray"),
117
+ );
118
+ if (hasStringRayPlugin) {
119
+ console.log(" ✅ StringRay plugin registered");
120
+ } else {
121
+ console.log(" ❌ StringRay plugin not registered");
122
+ this.results.failed.push({
123
+ test: "OpenCode Configuration",
124
+ error: "StringRay plugin not registered",
125
+ });
126
+ return;
127
+ }
128
+ }
129
+
130
+ // Check for MCP server disabling
131
+ if (config.disabled_mcps && Array.isArray(config.disabled_mcps)) {
132
+ const requiredDisabled = [
133
+ "global-everything",
134
+ "global-git",
135
+ "global-sqlite",
136
+ ];
137
+ const allDisabled = requiredDisabled.every((mcp) =>
138
+ config.disabled_mcps.includes(mcp),
139
+ );
140
+ if (allDisabled) {
141
+ console.log(" ✅ Problematic MCP servers disabled");
142
+ } else {
143
+ console.log(" ❌ Some problematic MCP servers not disabled");
144
+ this.results.failed.push({
145
+ test: "OpenCode Configuration",
146
+ error: "Problematic MCP servers not properly disabled",
147
+ });
148
+ return;
149
+ }
150
+ }
151
+
152
+ this.results.passed.push("OpenCode Configuration");
153
+ }
154
+
155
+ async validateClaudeConfig() {
156
+ console.log("\n🤖 Testing Claude Desktop Integration...");
157
+
158
+ // Skip in CI environments - Claude Desktop is not installed
159
+ const isCI = process.env.CI || process.env.GITHUB_ACTIONS;
160
+ if (isCI) {
161
+ console.log(" ℹ️ Skipping Claude Desktop check (not applicable in CI)");
162
+ this.results.passed.push("Claude Desktop Integration (CI - N/A)");
163
+ return;
164
+ }
165
+
166
+ try {
167
+ const claudeDir = path.join(os.homedir(), ".claude");
168
+ const { execSync } = await import('child_process');
169
+ const claudeMcpPath = path.join(claudeDir, ".mcp.json");
170
+
171
+ if (!fs.existsSync(claudeDir)) {
172
+ console.log(` ❌ ${claudeDir} directory not found`);
173
+ this.results.failed.push({
174
+ test: "Claude Desktop Integration",
175
+ error: ".claude directory not created",
176
+ });
177
+ return;
178
+ }
53
179
 
54
- // Check hooks
55
- check('.opencode/hooks/post-commit', 'Post-commit hook');
56
- check('.opencode/hooks/post-push', 'Post-push hook');
57
-
58
- // Check strray config
59
- const { getConfigDir } = await import('../helpers/resolve-config-path.mjs');
60
- const _cd = getConfigDir();
61
- check(_cd + '/config.json', 'StrRay config');
62
- check(_cd + '/features.json', 'StrRay features');
63
-
64
- console.log('\n══════════════════════════════════════════════════');
65
-
66
- if (errors.length > 0) {
67
- console.log('❌ VALIDATION FAILED');
68
- console.log('\nErrors:');
69
- errors.forEach(e => console.log(` • ${e}`));
70
- process.exit(1);
71
- } else {
72
- console.log('✅ VALIDATION PASSED');
73
- if (warnings.length > 0) {
74
- console.log('\nWarnings:');
75
- warnings.forEach(w => console.log(` • ${w}`));
180
+ if (!fs.existsSync(claudeMcpPath)) {
181
+ console.log(` ❌ ${claudeMcpPath} not found`);
182
+ this.results.failed.push({
183
+ test: "Claude Desktop Integration",
184
+ error: ".claude/.mcp.json not created",
185
+ });
186
+ return;
187
+ }
188
+
189
+ console.log(" ✅ .claude directory created");
190
+ console.log(" ✅ .claude/.mcp.json created");
191
+
192
+ // Validate MCP config content
193
+ const mcpConfig = JSON.parse(fs.readFileSync(claudeMcpPath, "utf8"));
194
+ if (mcpConfig.mcpServers) {
195
+ const serverCount = Object.keys(mcpConfig.mcpServers).length;
196
+ console.log(
197
+ ` ✅ MCP config valid (${serverCount} servers configured)`,
198
+ );
199
+
200
+ // Check for disabled problematic servers
201
+ const disabledServers = [
202
+ "global-everything",
203
+ "global-git",
204
+ "global-sqlite",
205
+ ];
206
+ let allDisabled = true;
207
+ for (const server of disabledServers) {
208
+ if (mcpConfig.mcpServers[server]) {
209
+ console.log(
210
+ ` ❌ ${server} should be disabled but is still active`,
211
+ );
212
+ allDisabled = false;
213
+ }
214
+ }
215
+
216
+ if (allDisabled) {
217
+ console.log(" ✅ Problematic MCP servers properly disabled");
218
+ } else {
219
+ this.results.failed.push({
220
+ test: "Claude Desktop Integration",
221
+ error: "Some problematic MCP servers not disabled",
222
+ });
223
+ return;
224
+ }
225
+ } else {
226
+ console.log(" ❌ Invalid MCP configuration structure");
227
+ this.results.failed.push({
228
+ test: "Claude Desktop Integration",
229
+ error: "Invalid MCP configuration structure",
230
+ });
231
+ return;
232
+ }
233
+
234
+ this.results.passed.push("Claude Desktop Integration");
235
+ } catch (error) {
236
+ console.log(` ❌ Error validating Claude config: ${error.message}`);
237
+ this.results.failed.push({
238
+ test: "Claude Desktop Integration",
239
+ error: error.message,
240
+ });
241
+ }
242
+ }
243
+
244
+ async validateMCPConfig() {
245
+ console.log("\n🔧 Testing MCP Server Configuration...");
246
+
247
+ // Check if .mcp.json exists (optional with lazy loading)
248
+ if (!fs.existsSync(".mcp.json")) {
249
+ console.log(" ℹ️ .mcp.json not found - using lazy loading via opencode.json");
250
+ this.results.passed.push("MCP Server Configuration (Lazy Loading)");
251
+ return;
252
+ }
253
+
254
+ try {
255
+ const mcpConfig = JSON.parse(fs.readFileSync(".mcp.json", "utf8"));
256
+
257
+ if (mcpConfig.mcpServers) {
258
+ const serverCount = Object.keys(mcpConfig.mcpServers).length;
259
+ console.log(
260
+ ` ✅ MCP config valid (${serverCount} servers configured)`,
261
+ );
262
+
263
+ // Check for required StringRay servers
264
+ const requiredServers = [
265
+ "researcher",
266
+ "session-management",
267
+ "orchestrator",
268
+ "enforcer",
269
+ "api-design",
270
+ "architecture-patterns",
271
+ "git-workflow",
272
+ "performance-optimization",
273
+ "project-analysis",
274
+ "testing-strategy",
275
+ "code-review",
276
+ "security-audit",
277
+ "ui-ux-design",
278
+ "refactoring-strategies",
279
+ "testing-best-practices",
280
+ ];
281
+
282
+ let missingServers = [];
283
+ for (const server of requiredServers) {
284
+ if (!mcpConfig.mcpServers[server]) {
285
+ missingServers.push(server);
286
+ }
287
+ }
288
+
289
+ if (missingServers.length === 0) {
290
+ console.log(" ✅ All required StringRay MCP servers configured");
291
+ this.results.passed.push("MCP Server Configuration");
292
+ } else {
293
+ console.log(` ❌ Missing MCP servers: ${missingServers.join(", ")}`);
294
+ this.results.failed.push({
295
+ test: "MCP Server Configuration",
296
+ error: `Missing MCP servers: ${missingServers.join(", ")}`,
297
+ });
298
+ }
299
+ } else {
300
+ console.log(" ❌ Invalid MCP configuration structure");
301
+ this.results.failed.push({
302
+ test: "MCP Server Configuration",
303
+ error: "Invalid MCP configuration structure",
304
+ });
305
+ }
306
+ } catch (error) {
307
+ console.log(` ❌ Error validating MCP config: ${error.message}`);
308
+ this.results.failed.push({
309
+ test: "MCP Server Configuration",
310
+ error: error.message,
311
+ });
312
+ }
313
+ }
314
+
315
+ printSummary() {
316
+ console.log("\n📊 POSTINSTALL CONFIGURATION SUMMARY");
317
+ console.log("=====================================");
318
+
319
+ console.log(`✅ Passed: ${this.results.passed.length}`);
320
+ console.log(`❌ Failed: ${this.results.failed.length}`);
321
+
322
+ if (this.results.failed.length > 0) {
323
+ console.log("\n❌ FAILED TESTS:");
324
+ this.results.failed.forEach((failure) => {
325
+ console.log(` • ${failure.test}: ${failure.error}`);
326
+ });
327
+ }
328
+
329
+ if (this.results.passed.length > 0) {
330
+ console.log("\n✅ PASSED TESTS:");
331
+ this.results.passed.forEach((test) => {
332
+ console.log(` • ${test}`);
333
+ });
334
+ }
76
335
  }
77
- process.exit(0);
78
336
  }
337
+
338
+ // Run postinstall configuration validation
339
+ const validator = new PostinstallConfigValidator();
340
+ validator
341
+ .validateConfiguration()
342
+ .then((success) => {
343
+ process.exit(success ? 0 : 1);
344
+ })
345
+ .catch((error) => {
346
+ console.error("Postinstall configuration validation failed:", error);
347
+ process.exit(1);
348
+ });
@@ -377,56 +377,25 @@ if (!isHermes && isConsumerEnvironment) {
377
377
 
378
378
  console.log("🔧 StrRay Postinstall: Consumer installation complete - all paths are correctly configured.");
379
379
 
380
- // Create symlink to .strray directory for persistent state
380
+ // Copy .strray directory for persistent config (never symlink — user may modify local config)
381
381
  const strraySource = path.join(packageRoot, '.strray');
382
382
  const strrayDest = path.join(targetDir, '.strray');
383
383
 
384
384
  if (fs.existsSync(strraySource)) {
385
385
  try {
386
- // Skip if source === dest (e.g. running postinstall in the dev repo itself)
387
386
  const resolvedSource = path.resolve(strraySource);
388
387
  const resolvedDest = path.resolve(strrayDest);
388
+
389
+ // Skip if source === dest (running postinstall in the dev repo itself)
389
390
  if (resolvedSource === resolvedDest) {
390
- console.log(`ℹ️ Skipping .strray symlink — source and destination are the same (${resolvedSource})`);
391
- } else {
392
- // Check if .strray already exists
393
- if (fs.existsSync(strrayDest)) {
394
- const stats = fs.lstatSync(strrayDest);
395
-
396
- if (stats.isSymbolicLink()) {
397
- // It's a symlink - check if it points to the right location
398
- const existingTarget = fs.readlinkSync(strrayDest);
399
- if (existingTarget === strraySource) {
400
- console.log(`✅ .strray symlink already exists and is correct`);
401
- } else {
402
- // Symlink exists but points to wrong location - remove and recreate
403
- console.log(`📝 Updating .strray symlink to point to new location...`);
404
- fs.unlinkSync(strrayDest);
405
- fs.symlinkSync(strraySource, strrayDest, 'dir');
406
- console.log(`✅ .strray directory symlinked (updated)`);
407
- }
408
- } else if (stats.isDirectory()) {
409
- // It's a regular directory - backup and create symlink
410
- const backupName = `.strray.backup.${Date.now()}`;
411
- console.log(`📝 Backing up existing .strray directory to ${backupName}...`);
412
- fs.renameSync(strrayDest, path.join(targetDir, backupName));
413
- fs.symlinkSync(strraySource, strrayDest, 'dir');
414
- console.log(`✅ .strray directory symlinked (backed up existing)`);
415
- }
391
+ console.log(`ℹ️ Skipping .strray copy — source and destination are the same`);
392
+ } else if (fs.existsSync(strrayDest)) {
393
+ // .strray already exists — don't overwrite, the user may have local config
394
+ console.log(`ℹ️ .strray directory already exists — keeping existing (user config preserved)`);
416
395
  } else {
417
- // .strray doesn't exist - create symlink
418
- fs.symlinkSync(strraySource, strrayDest, 'dir');
419
- console.log(`✅ .strray directory symlinked`);
420
- }
421
- }
422
- } catch (error) {
423
- console.error(`❌ Failed to symlink .strray:`, error.message);
424
- // Fallback: copy the directory
425
- try {
396
+ // Copy the directory recursively
426
397
  function copyDir(src, dest) {
427
- if (!fs.existsSync(dest)) {
428
- fs.mkdirSync(dest, { recursive: true });
429
- }
398
+ fs.mkdirSync(dest, { recursive: true });
430
399
  const entries = fs.readdirSync(src, { withFileTypes: true });
431
400
  for (const entry of entries) {
432
401
  const srcPath = path.join(src, entry.name);
@@ -439,10 +408,10 @@ if (fs.existsSync(strraySource)) {
439
408
  }
440
409
  }
441
410
  copyDir(strraySource, strrayDest);
442
- console.log(`✅ .strray directory copied (fallback)`);
443
- } catch (copyError) {
444
- console.error(`❌ Failed to copy .strray:`, copyError.message);
411
+ console.log(`✅ .strray directory copied`);
445
412
  }
413
+ } catch (error) {
414
+ console.error(`❌ Failed to copy .strray:`, error.message);
446
415
  }
447
416
  }
448
417
 
@@ -78,8 +78,8 @@ const CALCULATED_COUNTS = calculateCounts();
78
78
  const OFFICIAL_VERSIONS = {
79
79
  // Framework version
80
80
  framework: {
81
- version: "1.15.28",
82
- displayName: "StringRay AI v1.15.28",
81
+ version: "1.15.29",
82
+ displayName: "StringRay AI v1.15.29",
83
83
  lastUpdated: "2026-03-30",
84
84
  // Counts (auto-calculated, but can be overridden)
85
85
  ...CALCULATED_COUNTS,