claude-flow-novice 1.5.20 → 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.
- package/.claude/agents/CLAUDE.md +186 -2386
- package/.claude/agents/agent-principles/agent-type-guidelines.md +328 -0
- package/.claude/agents/agent-principles/format-selection.md +204 -0
- package/.claude/agents/agent-principles/prompt-engineering.md +371 -0
- package/.claude/agents/agent-principles/quality-metrics.md +294 -0
- package/.claude/agents/frontend/README.md +640 -0
- package/.claude/agents/frontend/interaction-tester.md +879 -0
- package/.claude/agents/frontend/react-frontend-engineer.md +130 -0
- package/.claude/agents/frontend/state-architect.md +250 -0
- package/.claude/agents/frontend/ui-designer.md +325 -0
- package/.claude/agents/researcher.md +1 -1
- package/.claude/agents/swarm/test-coordinator.md +383 -0
- package/.claude/agents/task-coordinator.md +126 -0
- package/.claude/settings.json +7 -7
- package/.claude-flow-novice/dist/src/hooks/enhanced-hooks-cli.js +168 -167
- package/.claude-flow-novice/dist/src/providers/tiered-router.js +118 -0
- package/.claude-flow-novice/dist/src/providers/tiered-router.js.map +1 -0
- package/.claude-flow-novice/dist/src/providers/types.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/zai-provider.js +268 -0
- package/.claude-flow-novice/dist/src/providers/zai-provider.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/simple-commands/init/templates/CLAUDE.md +25 -0
- package/src/hooks/enhanced-hooks-cli.js +23 -3
- package/src/hooks/enhanced-post-edit-pipeline.js +154 -75
- /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
|
|
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
|
-
//
|
|
696
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
705
|
-
|
|
706
|
-
|
|
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
|
|
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
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
-
//
|
|
1146
|
-
|
|
1147
|
-
|
|
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:
|
|
1291
|
+
coverage: 'fast-static-analysis',
|
|
1292
|
+
optimized: true
|
|
1214
1293
|
};
|
|
1215
1294
|
|
|
1216
1295
|
} catch (error) {
|
|
File without changes
|