claude-flow-novice 1.5.21 → 1.5.22

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 (25) hide show
  1. package/.claude/agents/CLAUDE.md +186 -2386
  2. package/.claude/agents/agent-principles/agent-type-guidelines.md +328 -0
  3. package/.claude/agents/agent-principles/format-selection.md +204 -0
  4. package/.claude/agents/agent-principles/prompt-engineering.md +371 -0
  5. package/.claude/agents/agent-principles/quality-metrics.md +294 -0
  6. package/.claude/agents/frontend/README.md +574 -53
  7. package/.claude/agents/frontend/interaction-tester.md +850 -108
  8. package/.claude/agents/frontend/react-frontend-engineer.md +130 -0
  9. package/.claude/agents/frontend/state-architect.md +240 -152
  10. package/.claude/agents/frontend/ui-designer.md +292 -68
  11. package/.claude/agents/researcher.md +1 -1
  12. package/.claude/agents/swarm/test-coordinator.md +383 -0
  13. package/.claude/agents/task-coordinator.md +126 -0
  14. package/.claude/settings.json +7 -7
  15. package/.claude-flow-novice/dist/src/hooks/enhanced-hooks-cli.js +168 -167
  16. package/.claude-flow-novice/dist/src/providers/tiered-router.js +118 -0
  17. package/.claude-flow-novice/dist/src/providers/tiered-router.js.map +1 -0
  18. package/.claude-flow-novice/dist/src/providers/types.js.map +1 -1
  19. package/.claude-flow-novice/dist/src/providers/zai-provider.js +268 -0
  20. package/.claude-flow-novice/dist/src/providers/zai-provider.js.map +1 -0
  21. package/package.json +1 -1
  22. package/src/cli/simple-commands/init/templates/CLAUDE.md +25 -0
  23. package/src/hooks/enhanced-hooks-cli.js +23 -3
  24. package/src/hooks/enhanced-post-edit-pipeline.js +154 -75
  25. /package/.claude/agents/{CLAUDE_AGENT_DESIGN_PRINCIPLES.md → agent-principles/CLAUDE_AGENT_DESIGN_PRINCIPLES.md} +0 -0
@@ -509,10 +509,12 @@ class SingleFileTestEngine {
509
509
 
510
510
  for (const dir of possibleDirs) {
511
511
  try {
512
- const result = execSync(`npx jest "${path.basename(testPattern)}" --json --coverage=false`, {
512
+ const result = execSync(`npx jest "${path.basename(testPattern)}" --json --coverage=false --forceExit --detectOpenHandles`, {
513
513
  encoding: 'utf8',
514
514
  stdio: 'pipe',
515
- cwd: dir
515
+ cwd: dir,
516
+ timeout: 30000, // 30 second timeout
517
+ killSignal: 'SIGKILL'
516
518
  });
517
519
 
518
520
  jestOutput = JSON.parse(result);
@@ -616,9 +618,11 @@ class SingleFileTestEngine {
616
618
  // Coverage analysis methods
617
619
  async getJestCoverage(file) {
618
620
  try {
619
- const result = execSync(`npx jest "${file}" --coverage --coverageReporters=json --silent`, {
621
+ const result = execSync(`npx jest "${file}" --coverage --coverageReporters=json --silent --forceExit --detectOpenHandles`, {
620
622
  encoding: 'utf8',
621
- stdio: 'pipe'
623
+ stdio: 'pipe',
624
+ timeout: 30000, // 30 second timeout
625
+ killSignal: 'SIGKILL'
622
626
  });
623
627
 
624
628
  const coveragePath = path.join(process.cwd(), 'coverage', 'coverage-final.json');
@@ -675,8 +679,10 @@ class SingleFileTestEngine {
675
679
  }
676
680
 
677
681
  async runCargoTestSingleFile(file) {
682
+ let testProcess = null;
683
+
678
684
  try {
679
- // For Rust, we run tests from the crate root, not individual files
685
+ // For Rust, we run targeted tests for the specific file
680
686
  const { exec } = await import('child_process');
681
687
  const { promisify } = await import('util');
682
688
  const execAsync = promisify(exec);
@@ -692,39 +698,78 @@ class SingleFileTestEngine {
692
698
  };
693
699
  }
694
700
 
695
- // Run cargo check first for syntax validation
696
- await execAsync('cargo check --quiet');
701
+ // Extract test target from file path for targeted testing
702
+ const testTarget = this.extractRustTestTarget(file);
703
+
704
+ // Build targeted cargo test command
705
+ // --lib for library tests, --test <name> for integration tests, or filter by module
706
+ let cargoTestCmd = 'cargo test --quiet';
707
+
708
+ if (file.includes('tests/')) {
709
+ // Integration test file
710
+ const testName = path.basename(file, '.rs');
711
+ cargoTestCmd += ` --test ${testName}`;
712
+ } else if (file === 'src/lib.rs' || file === './src/lib.rs') {
713
+ // Main library file - run lib tests only
714
+ cargoTestCmd += ' --lib';
715
+ } else if (testTarget) {
716
+ // Specific module - filter tests by module path
717
+ cargoTestCmd += ` ${testTarget}`;
718
+ } else {
719
+ // Fallback: run lib tests
720
+ cargoTestCmd += ' --lib';
721
+ }
697
722
 
698
- // Run cargo test with output capture
699
- const { stdout, stderr } = await execAsync('cargo test --quiet -- --nocapture', {
700
- timeout: 30000,
701
- maxBuffer: 1024 * 1024
702
- });
723
+ cargoTestCmd += ' -- --nocapture --test-threads=1';
703
724
 
704
- // Parse test output (simplified)
705
- const testOutput = stdout + stderr;
706
- const testRegex = /test\s+(\S+)\s+\.\.\.\s+(ok|FAILED)/g;
725
+ // Run targeted cargo test with strict timeout
726
+ try {
727
+ testProcess = await execAsync(cargoTestCmd, {
728
+ timeout: 30000,
729
+ maxBuffer: 1024 * 1024,
730
+ killSignal: 'SIGKILL'
731
+ });
732
+ } catch (error) {
733
+ if (testProcess) {
734
+ try { process.kill(-testProcess.pid, 'SIGKILL'); } catch {}
735
+ }
736
+ // Test failures still return output, so parse if available
737
+ if (!error.stdout && !error.stderr) throw error;
738
+ testProcess = error; // Use error object which contains stdout/stderr
739
+ }
740
+
741
+ // Parse test output
742
+ const testOutput = (testProcess.stdout || '') + (testProcess.stderr || '');
743
+ const testRegex = /test\s+(\S+)\s+\.\.\.\s+(ok|FAILED|ignored)/g;
707
744
  const tests = [];
708
745
  let match;
709
746
 
710
747
  while ((match = testRegex.exec(testOutput)) !== null) {
711
748
  tests.push({
712
749
  name: match[1],
713
- status: match[2] === 'ok' ? 'passed' : 'failed'
750
+ status: match[2] === 'ok' ? 'passed' : match[2] === 'FAILED' ? 'failed' : 'skipped'
714
751
  });
715
752
  }
716
753
 
717
754
  const passed = tests.filter(t => t.status === 'passed').length;
718
755
  const failed = tests.filter(t => t.status === 'failed').length;
756
+ const skipped = tests.filter(t => t.status === 'skipped').length;
719
757
 
720
758
  return {
721
759
  passed: failed === 0,
722
760
  reason: failed > 0 ? `${failed} tests failed` : 'All tests passed',
723
761
  tests,
724
- summary: { total: tests.length, passed, failed, skipped: 0 }
762
+ summary: { total: tests.length, passed, failed, skipped },
763
+ targetedTest: testTarget || 'lib',
764
+ command: cargoTestCmd
725
765
  };
726
766
 
727
767
  } catch (error) {
768
+ // Ensure all processes are killed
769
+ if (testProcess) {
770
+ try { process.kill(-testProcess.pid, 'SIGKILL'); } catch {}
771
+ }
772
+
728
773
  return {
729
774
  passed: false,
730
775
  reason: `Cargo test failed: ${error.message}`,
@@ -734,32 +779,91 @@ class SingleFileTestEngine {
734
779
  }
735
780
  }
736
781
 
782
+ // Extract Rust test target from file path
783
+ extractRustTestTarget(file) {
784
+ // Normalize path
785
+ const normalizedPath = file.replace(/\\/g, '/');
786
+
787
+ // Extract module path from src/ directory
788
+ if (normalizedPath.includes('src/')) {
789
+ const srcIndex = normalizedPath.lastIndexOf('src/');
790
+ const relativePath = normalizedPath.substring(srcIndex + 4); // Skip 'src/'
791
+
792
+ // Remove .rs extension
793
+ let modulePath = relativePath.replace(/\.rs$/, '');
794
+
795
+ // Skip lib.rs and main.rs as they're tested with --lib or --bin
796
+ if (modulePath === 'lib' || modulePath === 'main') {
797
+ return null;
798
+ }
799
+
800
+ // Convert file path to module path: src/foo/bar.rs -> foo::bar
801
+ modulePath = modulePath.replace(/\//g, '::');
802
+
803
+ // Remove 'mod' if it's a mod.rs file
804
+ modulePath = modulePath.replace(/::mod$/, '');
805
+
806
+ return modulePath;
807
+ }
808
+
809
+ return null;
810
+ }
811
+
737
812
  async getRustCoverage(file) {
813
+ let versionProcess = null;
814
+ let coverageProcess = null;
815
+
738
816
  try {
739
817
  // Check if cargo-tarpaulin is available
740
818
  const { exec } = await import('child_process');
741
819
  const { promisify } = await import('util');
742
820
  const execAsync = promisify(exec);
743
821
 
744
- // Try to run tarpaulin for coverage
745
- const { stdout } = await execAsync('cargo tarpaulin --version', { timeout: 5000 });
746
-
747
- if (stdout.includes('tarpaulin')) {
748
- // Run coverage analysis
749
- const { stdout: coverageOutput } = await execAsync('cargo tarpaulin --out Json --timeout 30', {
750
- timeout: 60000,
751
- maxBuffer: 1024 * 1024
822
+ // Try to run tarpaulin for coverage with timeout
823
+ try {
824
+ versionProcess = await execAsync('cargo tarpaulin --version', {
825
+ timeout: 5000,
826
+ killSignal: 'SIGKILL'
752
827
  });
828
+ } catch (error) {
829
+ if (versionProcess) {
830
+ try { process.kill(-versionProcess.pid, 'SIGKILL'); } catch {}
831
+ }
832
+ throw error;
833
+ }
753
834
 
754
- const coverage = JSON.parse(coverageOutput);
755
- return {
756
- available: true,
757
- percentage: coverage.coverage || 0,
758
- lines: coverage.files || {},
759
- tool: 'cargo-tarpaulin'
760
- };
835
+ if (versionProcess.stdout && versionProcess.stdout.includes('tarpaulin')) {
836
+ // Run coverage analysis with strict timeout
837
+ try {
838
+ coverageProcess = await execAsync('cargo tarpaulin --out Json --timeout 30', {
839
+ timeout: 60000,
840
+ maxBuffer: 1024 * 1024,
841
+ killSignal: 'SIGKILL'
842
+ });
843
+
844
+ const coverage = JSON.parse(coverageProcess.stdout);
845
+ return {
846
+ available: true,
847
+ percentage: coverage.coverage || 0,
848
+ lines: coverage.files || {},
849
+ tool: 'cargo-tarpaulin'
850
+ };
851
+ } catch (error) {
852
+ if (coverageProcess) {
853
+ try { process.kill(-coverageProcess.pid, 'SIGKILL'); } catch {}
854
+ }
855
+ throw error;
856
+ }
761
857
  }
762
858
  } catch (error) {
859
+ // Ensure all processes are killed
860
+ if (versionProcess) {
861
+ try { process.kill(-versionProcess.pid, 'SIGKILL'); } catch {}
862
+ }
863
+ if (coverageProcess) {
864
+ try { process.kill(-coverageProcess.pid, 'SIGKILL'); } catch {}
865
+ }
866
+
763
867
  // Fallback: check if we can at least detect test presence
764
868
  const cargoTomlExists = await this.fileExists('Cargo.toml');
765
869
  return {
@@ -1142,52 +1246,16 @@ class ValidationEngine {
1142
1246
  const cargoTomlExists = await this.fileExists('Cargo.toml');
1143
1247
 
1144
1248
  if (cargoTomlExists) {
1145
- // Run cargo check for syntax validation
1146
- const { exec } = await import('child_process');
1147
- const { promisify } = await import('util');
1148
- const execAsync = promisify(exec);
1149
-
1150
- try {
1151
- const { stderr } = await execAsync('cargo check --quiet', {
1152
- timeout: 30000,
1153
- maxBuffer: 1024 * 1024
1154
- });
1155
-
1156
- if (stderr) {
1157
- // Parse cargo check output for errors
1158
- const errorLines = stderr.split('\n').filter(line => line.includes('error['));
1159
- errorLines.forEach((line, index) => {
1160
- if (index < 5) { // Limit to first 5 errors
1161
- issues.push({
1162
- type: 'rust_compile_error',
1163
- severity: 'error',
1164
- message: line.trim(),
1165
- suggestion: 'Fix compilation error before proceeding'
1166
- });
1167
- }
1168
- });
1169
-
1170
- if (errorLines.length > 5) {
1171
- suggestions.push(`${errorLines.length - 5} additional compilation errors found`);
1172
- }
1173
- }
1174
-
1175
- suggestions.push('Rust syntax validated with cargo check');
1176
-
1177
- } catch (cargoError) {
1178
- issues.push({
1179
- type: 'rust_compile_error',
1180
- severity: 'error',
1181
- message: `Compilation failed: ${cargoError.message}`,
1182
- suggestion: 'Fix Rust syntax errors'
1183
- });
1184
- }
1249
+ // OPTIMIZATION: Skip full cargo check for post-edit hook
1250
+ // Cargo test will catch compilation errors anyway
1251
+ // This saves massive compilation time and memory
1185
1252
 
1253
+ suggestions.push('Rust syntax validation deferred to cargo test (performance optimization)');
1186
1254
  } else {
1187
1255
  suggestions.push('Not in a Cargo project - create Cargo.toml for full validation');
1188
1256
  }
1189
1257
 
1190
- // Basic content checks
1258
+ // Basic content checks (fast static analysis)
1191
1259
  if (!content.includes('fn ') && !content.includes('struct ') && !content.includes('enum ') && !content.includes('impl ')) {
1192
1260
  issues.push({
1193
1261
  type: 'rust_structure',
@@ -1197,7 +1265,7 @@ class ValidationEngine {
1197
1265
  });
1198
1266
  }
1199
1267
 
1200
- // Check for common Rust patterns
1268
+ // Check for common Rust anti-patterns (static analysis)
1201
1269
  if (content.includes('unwrap()')) {
1202
1270
  suggestions.push('Consider using proper error handling instead of unwrap()');
1203
1271
  }
@@ -1206,11 +1274,22 @@ class ValidationEngine {
1206
1274
  suggestions.push('Excessive use of clone() - consider borrowing or references');
1207
1275
  }
1208
1276
 
1277
+ // Check for unsafe blocks
1278
+ if (content.includes('unsafe ')) {
1279
+ suggestions.push('Unsafe block detected - ensure safety invariants are documented');
1280
+ }
1281
+
1282
+ // Check for panic macros
1283
+ if (content.includes('panic!') || content.includes('unimplemented!') || content.includes('todo!')) {
1284
+ suggestions.push('Panic macros detected - consider error handling alternatives');
1285
+ }
1286
+
1209
1287
  return {
1210
1288
  passed: issues.filter(i => i.severity === 'error').length === 0,
1211
1289
  issues,
1212
1290
  suggestions,
1213
- coverage: cargoTomlExists ? 'advanced' : 'basic'
1291
+ coverage: 'fast-static-analysis',
1292
+ optimized: true
1214
1293
  };
1215
1294
 
1216
1295
  } catch (error) {