claude-flow-novice 2.18.13 → 2.18.14
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/.backups/unknown/1765417503_2999b9ec66f591dd76075617ec31fcea/metadata.json +8 -0
- package/.backups/unknown/1765417503_2999b9ec66f591dd76075617ec31fcea/original +3 -0
- package/.backups/unknown/1765417503_2999b9ec66f591dd76075617ec31fcea/revert.sh +7 -0
- package/.backups/unknown/1765417531_2999b9ec66f591dd76075617ec31fcea/metadata.json +8 -0
- package/.backups/unknown/1765417531_2999b9ec66f591dd76075617ec31fcea/original +3 -0
- package/.backups/unknown/1765417531_2999b9ec66f591dd76075617ec31fcea/revert.sh +7 -0
- package/.backups/unknown/1765417552_2999b9ec66f591dd76075617ec31fcea/metadata.json +8 -0
- package/.backups/unknown/1765417552_2999b9ec66f591dd76075617ec31fcea/original +3 -0
- package/.backups/unknown/1765417552_2999b9ec66f591dd76075617ec31fcea/revert.sh +7 -0
- package/.backups/unknown/1765417613_1f34dbf0fe1ec3b6cb74ba7c4e12a2a4/metadata.json +8 -0
- package/.backups/unknown/1765417613_1f34dbf0fe1ec3b6cb74ba7c4e12a2a4/original +1212 -0
- package/.backups/unknown/1765417613_1f34dbf0fe1ec3b6cb74ba7c4e12a2a4/revert.sh +7 -0
- package/.backups/unknown/1765417613_332e9bb223a26270398922640c1c1653/metadata.json +8 -0
- package/.backups/unknown/1765417613_332e9bb223a26270398922640c1c1653/original +1212 -0
- package/.backups/unknown/1765417613_332e9bb223a26270398922640c1c1653/revert.sh +7 -0
- package/.backups/unknown/1765417613_45880a7afef063347b701e7e88e2df9c/metadata.json +8 -0
- package/.backups/unknown/1765417613_45880a7afef063347b701e7e88e2df9c/original +1212 -0
- package/.backups/unknown/1765417613_45880a7afef063347b701e7e88e2df9c/revert.sh +7 -0
- package/.backups/unknown/1765417614_1b6c3b658c2891dbe245aea4bad41661/metadata.json +8 -0
- package/.backups/unknown/1765417614_1b6c3b658c2891dbe245aea4bad41661/original +1228 -0
- package/.backups/unknown/1765417614_1b6c3b658c2891dbe245aea4bad41661/revert.sh +7 -0
- package/.backups/unknown/1765417614_1ed5bd129f0b5d1b297a04ec5267ef62/metadata.json +8 -0
- package/.backups/unknown/1765417614_1ed5bd129f0b5d1b297a04ec5267ef62/original +1237 -0
- package/.backups/unknown/1765417614_1ed5bd129f0b5d1b297a04ec5267ef62/revert.sh +7 -0
- package/.backups/unknown/1765417614_28e032bd1b62dbfcecbd56327a0b0e23/metadata.json +8 -0
- package/.backups/unknown/1765417614_28e032bd1b62dbfcecbd56327a0b0e23/original +1240 -0
- package/.backups/unknown/1765417614_28e032bd1b62dbfcecbd56327a0b0e23/revert.sh +7 -0
- package/.backups/unknown/1765417614_40a1ce1b0efa2e1ba1958c27b2cbc557/metadata.json +8 -0
- package/.backups/unknown/1765417614_40a1ce1b0efa2e1ba1958c27b2cbc557/original +1216 -0
- package/.backups/unknown/1765417614_40a1ce1b0efa2e1ba1958c27b2cbc557/revert.sh +7 -0
- package/.backups/unknown/1765417614_f65dbf58cff7a9aa1c5c3d0d1618074a/metadata.json +8 -0
- package/.backups/unknown/1765417614_f65dbf58cff7a9aa1c5c3d0d1618074a/original +1238 -0
- package/.backups/unknown/1765417614_f65dbf58cff7a9aa1c5c3d0d1618074a/revert.sh +7 -0
- package/.backups/unknown/1765417615_0f45c28b9b8d5204974421c06075ff74/metadata.json +8 -0
- package/.backups/unknown/1765417615_0f45c28b9b8d5204974421c06075ff74/original +1167 -0
- package/.backups/unknown/1765417615_0f45c28b9b8d5204974421c06075ff74/revert.sh +7 -0
- package/.backups/unknown/1765417615_41d36156891ddb6f63d1b4da59d1db4c/metadata.json +8 -0
- package/.backups/unknown/1765417615_41d36156891ddb6f63d1b4da59d1db4c/original +1242 -0
- package/.backups/unknown/1765417615_41d36156891ddb6f63d1b4da59d1db4c/revert.sh +7 -0
- package/.backups/unknown/1765417709_77dda8631459cfc2ab94b8928f7d90b4/metadata.json +8 -0
- package/.backups/unknown/1765417709_77dda8631459cfc2ab94b8928f7d90b4/original +2 -0
- package/.backups/unknown/1765417709_77dda8631459cfc2ab94b8928f7d90b4/revert.sh +7 -0
- package/.backups/unknown/1765417775_1405837bacf112ba832a139840fe94e7/metadata.json +8 -0
- package/.backups/unknown/1765417775_1405837bacf112ba832a139840fe94e7/original +1169 -0
- package/.backups/unknown/1765417775_1405837bacf112ba832a139840fe94e7/revert.sh +7 -0
- package/.backups/unknown/1765417775_8044b4a5cfdcb5cd71317831ad53f5a2/metadata.json +8 -0
- package/.backups/unknown/1765417775_8044b4a5cfdcb5cd71317831ad53f5a2/original +1171 -0
- package/.backups/unknown/1765417775_8044b4a5cfdcb5cd71317831ad53f5a2/revert.sh +7 -0
- package/.backups/unknown/1765417775_8eb94d60151e1db262377fbf8a187212/metadata.json +8 -0
- package/.backups/unknown/1765417775_8eb94d60151e1db262377fbf8a187212/original +1170 -0
- package/.backups/unknown/1765417775_8eb94d60151e1db262377fbf8a187212/revert.sh +7 -0
- package/.backups/unknown/1765417776_a0d4b709e15e0bfa002c4b128f5e4603/metadata.json +8 -0
- package/.backups/unknown/1765417776_a0d4b709e15e0bfa002c4b128f5e4603/original +1173 -0
- package/.backups/unknown/1765417776_a0d4b709e15e0bfa002c4b128f5e4603/revert.sh +7 -0
- package/.backups/unknown/1765417776_cb1bc24d98b63564eb73ef782b8c68b3/metadata.json +8 -0
- package/.backups/unknown/1765417776_cb1bc24d98b63564eb73ef782b8c68b3/original +1170 -0
- package/.backups/unknown/1765417776_cb1bc24d98b63564eb73ef782b8c68b3/revert.sh +7 -0
- package/.backups/unknown/1765417777_1c4cc8c33f476f0c87c8efd4e34efc22/metadata.json +8 -0
- package/.backups/unknown/1765417777_1c4cc8c33f476f0c87c8efd4e34efc22/original +1172 -0
- package/.backups/unknown/1765417777_1c4cc8c33f476f0c87c8efd4e34efc22/revert.sh +7 -0
- package/.backups/unknown/1765417777_325de83ffd34544909b8b9aa6e18dd05/metadata.json +8 -0
- package/.backups/unknown/1765417777_325de83ffd34544909b8b9aa6e18dd05/original +1175 -0
- package/.backups/unknown/1765417777_325de83ffd34544909b8b9aa6e18dd05/revert.sh +7 -0
- package/.backups/unknown/1765417950_402e7c1a8956b56ac7b88e89f024acb4/metadata.json +8 -0
- package/.backups/unknown/1765417950_402e7c1a8956b56ac7b88e89f024acb4/original +1178 -0
- package/.backups/unknown/1765417950_402e7c1a8956b56ac7b88e89f024acb4/revert.sh +7 -0
- package/.backups/unknown/1765417950_647e27e4da18ae42c339e04b4d334feb/metadata.json +8 -0
- package/.backups/unknown/1765417950_647e27e4da18ae42c339e04b4d334feb/original +1174 -0
- package/.backups/unknown/1765417950_647e27e4da18ae42c339e04b4d334feb/revert.sh +7 -0
- package/.backups/unknown/1765417951_1424d2dd67b8c8a4e7cf649b379d21f2/metadata.json +8 -0
- package/.backups/unknown/1765417951_1424d2dd67b8c8a4e7cf649b379d21f2/original +1194 -0
- package/.backups/unknown/1765417951_1424d2dd67b8c8a4e7cf649b379d21f2/revert.sh +7 -0
- package/.backups/unknown/1765417951_6df070ca287ea94a7f08efc69ec9ea51/metadata.json +8 -0
- package/.backups/unknown/1765417951_6df070ca287ea94a7f08efc69ec9ea51/original +1186 -0
- package/.backups/unknown/1765417951_6df070ca287ea94a7f08efc69ec9ea51/revert.sh +7 -0
- package/.backups/unknown/1765417951_848919a1365f6536e5ef8d85d0c80fa7/metadata.json +8 -0
- package/.backups/unknown/1765417951_848919a1365f6536e5ef8d85d0c80fa7/original +1180 -0
- package/.backups/unknown/1765417951_848919a1365f6536e5ef8d85d0c80fa7/revert.sh +7 -0
- package/.backups/unknown/1765417951_b0d1d4ecf85ad10cda0f3900d414b608/metadata.json +8 -0
- package/.backups/unknown/1765417951_b0d1d4ecf85ad10cda0f3900d414b608/original +1198 -0
- package/.backups/unknown/1765417951_b0d1d4ecf85ad10cda0f3900d414b608/revert.sh +7 -0
- package/.backups/unknown/1765417952_2b720e213b5197cbaad53f9f0857f23f/metadata.json +8 -0
- package/.backups/unknown/1765417952_2b720e213b5197cbaad53f9f0857f23f/original +1202 -0
- package/.backups/unknown/1765417952_2b720e213b5197cbaad53f9f0857f23f/revert.sh +7 -0
- package/.backups/unknown/1765417952_ef1d15e8b83a802a0c0f0fc03e56a89c/metadata.json +8 -0
- package/.backups/unknown/1765417952_ef1d15e8b83a802a0c0f0fc03e56a89c/original +1202 -0
- package/.backups/unknown/1765417952_ef1d15e8b83a802a0c0f0fc03e56a89c/revert.sh +7 -0
- package/.backups/unknown/1765418048_ac94238a69a6f86a6ba9d31f623c18a7/metadata.json +8 -0
- package/.backups/unknown/1765418048_ac94238a69a6f86a6ba9d31f623c18a7/original +1209 -0
- package/.backups/unknown/1765418048_ac94238a69a6f86a6ba9d31f623c18a7/revert.sh +7 -0
- package/.backups/unknown/1765418048_f3afed98da63fef9ff57fb5646976f63/metadata.json +8 -0
- package/.backups/unknown/1765418048_f3afed98da63fef9ff57fb5646976f63/original +1207 -0
- package/.backups/unknown/1765418048_f3afed98da63fef9ff57fb5646976f63/revert.sh +7 -0
- package/.backups/unknown/1765418049_9c392702a4e1cace37d17f6f2dfaf54e/metadata.json +8 -0
- package/.backups/unknown/1765418049_9c392702a4e1cace37d17f6f2dfaf54e/original +1207 -0
- package/.backups/unknown/1765418049_9c392702a4e1cace37d17f6f2dfaf54e/revert.sh +7 -0
- package/.backups/unknown/1765418049_9e0777555b51dea4d24d0503b584cfc5/metadata.json +8 -0
- package/.backups/unknown/1765418049_9e0777555b51dea4d24d0503b584cfc5/original +1193 -0
- package/.backups/unknown/1765418049_9e0777555b51dea4d24d0503b584cfc5/revert.sh +7 -0
- package/.backups/unknown/1765418050_22d534bed68f9f5d883bee54e7cbc4d3/metadata.json +8 -0
- package/.backups/unknown/1765418050_22d534bed68f9f5d883bee54e7cbc4d3/original +1194 -0
- package/.backups/unknown/1765418050_22d534bed68f9f5d883bee54e7cbc4d3/revert.sh +7 -0
- package/.backups/unknown/1765418051_d9243722dc90dabc851c5767a62ac7f2/metadata.json +8 -0
- package/.backups/unknown/1765418051_d9243722dc90dabc851c5767a62ac7f2/original +1193 -0
- package/.backups/unknown/1765418051_d9243722dc90dabc851c5767a62ac7f2/revert.sh +7 -0
- package/.backups/unknown/1765418177_21a2fa816d25d419b54ef2d429aab474/metadata.json +8 -0
- package/.backups/unknown/1765418177_21a2fa816d25d419b54ef2d429aab474/original +1191 -0
- package/.backups/unknown/1765418177_21a2fa816d25d419b54ef2d429aab474/revert.sh +7 -0
- package/.backups/unknown/1765418177_33b673dce1eb59d7eb4f752aec882794/metadata.json +8 -0
- package/.backups/unknown/1765418177_33b673dce1eb59d7eb4f752aec882794/original +1190 -0
- package/.backups/unknown/1765418177_33b673dce1eb59d7eb4f752aec882794/revert.sh +7 -0
- package/.backups/unknown/1765418177_72568812ea5a73cf6861a6b14da065fa/metadata.json +8 -0
- package/.backups/unknown/1765418177_72568812ea5a73cf6861a6b14da065fa/original +1191 -0
- package/.backups/unknown/1765418177_72568812ea5a73cf6861a6b14da065fa/revert.sh +7 -0
- package/.backups/unknown/1765418178_20c28b95f12194fe4b81cfb35f4f3757/metadata.json +8 -0
- package/.backups/unknown/1765418178_20c28b95f12194fe4b81cfb35f4f3757/original +1189 -0
- package/.backups/unknown/1765418178_20c28b95f12194fe4b81cfb35f4f3757/revert.sh +7 -0
- package/.backups/unknown/1765418178_cdf68719cc09173769027743e2718ad4/metadata.json +8 -0
- package/.backups/unknown/1765418178_cdf68719cc09173769027743e2718ad4/original +1197 -0
- package/.backups/unknown/1765418178_cdf68719cc09173769027743e2718ad4/revert.sh +7 -0
- package/.backups/unknown/1765418178_eb19e0f8542405480e51af09fad84b71/metadata.json +8 -0
- package/.backups/unknown/1765418178_eb19e0f8542405480e51af09fad84b71/original +1190 -0
- package/.backups/unknown/1765418178_eb19e0f8542405480e51af09fad84b71/revert.sh +7 -0
- package/.backups/unknown/1765418236_c016507725847318a3c369b047cbb802/metadata.json +8 -0
- package/.backups/unknown/1765418236_c016507725847318a3c369b047cbb802/original +1197 -0
- package/.backups/unknown/1765418236_c016507725847318a3c369b047cbb802/revert.sh +7 -0
- package/.backups/unknown/1765418237_932c3cc3a5c537b7933c2370b65f4b39/metadata.json +8 -0
- package/.backups/unknown/1765418237_932c3cc3a5c537b7933c2370b65f4b39/original +1197 -0
- package/.backups/unknown/1765418237_932c3cc3a5c537b7933c2370b65f4b39/revert.sh +7 -0
- package/.claude/agents/cfn-dev-team/utility/epic-creator-v2.md +194 -2
- package/.claude/agents/cfn-dev-team/utility/epic-creator-v2.sh +264 -0
- package/.claude/agents/cfn-dev-team/utility/epic-creator-v2.sh.backup +698 -0
- package/.claude/hooks/README.md +148 -0
- package/.claude/hooks/SessionStart:cfn-load-openai-key.sh +35 -0
- package/.claude/hooks/cfn-invoke-pre-edit.sh +1 -1
- package/.claude/hooks/cfn-load-cerebras-env.sh +65 -0
- package/.claude/hooks/cfn-precompact-enhanced.sh +283 -0
- package/.claude/hooks/deprecated/README.md +27 -0
- package/.claude/settings.json +71 -6
- package/.claude/skills/cfn-cerebras-coordinator/QUICKSTART.md +348 -0
- package/.claude/skills/cfn-cerebras-coordinator/README.md +525 -0
- package/.claude/skills/cfn-cerebras-coordinator/TDD_COORDINATOR_OVERVIEW.md +284 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-py-v2-test-1733854200.json +36 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-py-v3-test-1765383241.json +36 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-rust-v2-test-1765381361.json +36 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-rust-v3-test-1765383242.json +46 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-ts-v2-test-1733864400.json +66 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-ts-v3-test-1765383238.json +66 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-ts-v4-test-1765386506.json +66 -0
- package/.claude/skills/cfn-cerebras-coordinator/conversations/2025-12-10-ts-v5-test-1765393100.json +66 -0
- package/.claude/skills/cfn-cerebras-coordinator/example-usage.sh +206 -0
- package/.claude/skills/cfn-cerebras-coordinator/lib/tdd-conversation-coordinator.ts +569 -0
- package/.claude/skills/cfn-cerebras-coordinator/test-tdd-coordinator.sh +250 -0
- package/.claude/skills/cfn-epic-creator/SKILL.md +392 -0
- package/.claude/skills/cfn-epic-creator/estimate-costs.sh +524 -0
- package/.claude/skills/cfn-epic-creator/invoke.sh +445 -0
- package/.claude/skills/cfn-epic-creator/parse-personas.sh +533 -0
- package/.claude/skills/cfn-epic-creator/security-utils.sh +268 -0
- package/.claude/skills/cfn-epic-creator/security-verification-demo.sh +127 -0
- package/.claude/skills/cfn-epic-creator/test-invoke.sh +620 -0
- package/.claude/skills/cfn-epic-creator/test-security-fixes.sh +195 -0
- package/.claude/skills/cfn-epic-creator/test-security-simple.sh +165 -0
- package/.claude/skills/cfn-epic-creator/validate-epic.sh +474 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/.backups/unknown/1765358428_2cf0ad7377232c919ff0a65630cb307d/metadata.json +8 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/.backups/unknown/1765358428_2cf0ad7377232c919ff0a65630cb307d/original +209 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/.backups/unknown/1765358428_2cf0ad7377232c919ff0a65630cb307d/revert.sh +7 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/AGENT_INTEGRATION_PATTERNS.md +255 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/Cargo.toml +8 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/Cargo.toml.backup +47 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/EXTRACTION_EXAMPLES.md +287 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/README.md +2 -563
- package/.claude/skills/cfn-local-ruvector-accelerator/SCHEMA_V2_IMPLEMENTATION.md +151 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/SECURITY_IMPLEMENTATION.md +185 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/SKILL.md +8 -351
- package/.claude/skills/cfn-local-ruvector-accelerator/docs/EXECUTIVE_SUMMARY.txt +90 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/docs/PHASE_4_QUERY_API.md +210 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/docs/RUST_AST_EXTRACTOR_IMPLEMENTATION.md +222 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/docs/TRANSACTION_MANAGEMENT.md +241 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/docs/VALIDATION_FINDINGS.txt +250 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/epic-ast-indexer.json +335 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/index-code.sh +292 -96
- package/.claude/skills/cfn-local-ruvector-accelerator/index-code.sh.backup +249 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/index_all.sh +65 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/find.rs +214 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index.rs +259 -103
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index_ast.rs +767 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index_modified.rs +37 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/init.rs +17 -12
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/migration.rs +34 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/mod.rs +6 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/refs.rs +209 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/embeddings.rs +82 -33
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/mod.rs +251 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/rust.rs +425 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/rust_placeholder.rs +420 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/text_fallback.rs +490 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/typescript.rs +520 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/typescript_full.rs +531 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/lib.rs +20 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/main.rs +33 -2
- package/.claude/skills/cfn-local-ruvector-accelerator/src/main.rs.backup +360 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/migration.rs +325 -318
- package/.claude/skills/cfn-local-ruvector-accelerator/src/migration_backup.rs +325 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/migration_tx.rs +513 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/paths.rs +29 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/query_api.rs +609 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/schema_v2.rs +603 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/security_tests.rs +154 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2.rs +583 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2_backup.rs +560 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2_fixed.rs +0 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2_tx.rs +397 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/test_schema.rs +228 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/transaction_tests.rs +410 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/test_ast_indexing.rs +59 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/test_query_api.sh +103 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/test_schema.sql +158 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/test_schema_v2.sql +147 -0
- package/.claude/skills/cfn-mdap-context-injection/SKILL.md +31 -10
- package/.claude/skills/cfn-mdap-error-fixer/skill.md +15 -205
- package/.claude/skills/cfn-session-handoff/SKILL.md +1 -53
- package/.claude/skills/cfn-task-decomposition/SKILL.md +1 -0
- package/.claude/skills/cfn-task-intelligence/SKILL.md +39 -18
- package/.claude/skills/cfn-task-planning/SKILL.md +37 -12
- package/.claude/skills/cfn-validation-framework/SKILL.md +3 -60
- package/.claude/skills/cfn-vision-analysis/SKILL.md +3 -20
- package/.claude-flow/metrics/performance.json +3 -3
- package/.claude-flow/metrics/task-metrics.json +3 -3
- package/.ruvector/index_v2.db-shm +0 -0
- package/.ruvector/index_v2.db-wal +0 -0
- package/CLAUDE.md +9 -1
- package/README.md +56 -0
- package/docs/AGENT_PRODUCTION_FEEDBACK_PATTERNS.md +389 -0
- package/docs/CEREBRAS_TDD_COORDINATOR_TEST_RESULTS.md +213 -0
- package/docs/CEREBRAS_TDD_ROUND1_VS_ROUND2_COMPARISON.md +225 -0
- package/docs/CEREBRAS_TDD_ROUND3_FINAL_RESULTS.md +264 -0
- package/docs/EPIC_CREATOR_V2_ARCHITECTURE.md +629 -0
- package/docs/HOOKS_IMPLEMENTATION_PRIORITY.md +585 -0
- package/docs/SKILLS_HOOKS_INTEGRATION.md +1074 -0
- package/epic-with-personas-2025-12-09-17-15-13.json +253 -0
- package/package.json +2 -1
- package/test-epic-creator-security.sh +203 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/__pycache__/search_engine_v2.cpython-312.pyc +0 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/search_engine.py +0 -200
- package/jest.config.ts.cjs +0 -17
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-cerebras-code-generator/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-cerebras-code-generator/context-analyzer.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-cerebras-code-generator/generate-code.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/execute.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/INTEGRATION.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/SAMPLE_OUTPUTS.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/capture-container-logs.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/enable-logging.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/init-hybrid-logging.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/analytics-summary.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/query-agent-timeline.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/query-consensus-history.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/query-coordination-timeline.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/query-failed-containers.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/queries/query-gate-checks.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/schema.sql +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/sqlite-helpers.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/logging/test-hybrid-logging.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/mcp/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/mcp/skill-mcp-selector.js +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/orchestration/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/orchestration/orchestrate.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/spawning/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/spawning/spawn-agent.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/waves/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/waves/cleanup-wave.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/waves/lib/docker-helpers.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/waves/monitor-wave.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-docker-runtime/lib/waves/spawn-wave.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-node-heap-sizer/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/cfn-node-heap-sizer/task-mode-heap-limiter.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/deprecated/analyze-patterns.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/README.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/SECURITY.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/SKILL.md +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/package.json +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/solve.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/test-equation-solver-minimal.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/test-equation-solver.sh +0 -0
- /package/.claude/{skills → cfn-extras/skills/deprecated}/equation-solver/test.cjs +0 -0
- /package/.claude/hooks/{cfn-credential-scanner.sh → deprecated/cfn-credential-scanner.sh} +0 -0
- /package/.claude/hooks/{cfn-detect-hardcoded-credentials.sh → deprecated/cfn-detect-hardcoded-credentials.sh} +0 -0
- /package/.claude/hooks/{cfn-invoke-post-edit-ts.sh → deprecated/cfn-invoke-post-edit-ts.sh} +0 -0
- /package/.claude/hooks/{cfn-invoke-pre-edit-ts.sh → deprecated/cfn-invoke-pre-edit-ts.sh} +0 -0
- /package/.claude/hooks/{cfn-invoke-security-validation.sh → deprecated/cfn-invoke-security-validation.sh} +0 -0
- /package/.claude/hooks/{cfn-lint-sql-injection.sh → deprecated/cfn-lint-sql-injection.sh} +0 -0
- /package/.claude/hooks/{cfn-post-edit.sh → deprecated/cfn-post-edit.sh} +0 -0
- /package/.claude/hooks/{cfn-pre-edit-backup.sh → deprecated/cfn-pre-edit-backup.sh} +0 -0
- /package/.claude/skills/cfn-local-ruvector-accelerator/{embeddings_manager.py → embeddings_manager.py.backup} +0 -0
- /package/{.ruvector → .claude/skills/cfn-local-ruvector-accelerator/index}/index.bin +0 -0
- /package/{.ruvector → .claude/skills/cfn-local-ruvector-accelerator/index}/metadata.json +0 -0
- /package/.claude/skills/cfn-local-ruvector-accelerator/{search_engine_v2.py → search_engine_v2.py.backup} +0 -0
- /package/.claude/skills/cfn-local-ruvector-accelerator/{sqlite_store.py → sqlite_store.py.backup} +0 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
use anyhow::{Result, Context, anyhow};
|
|
2
|
+
use std::fs;
|
|
3
|
+
use std::path::{Path, PathBuf};
|
|
4
|
+
use walkdir::{WalkDir, DirEntry};
|
|
5
|
+
use tracing::{info, debug, warn, error};
|
|
6
|
+
use std::sync::{Arc, RwLock};
|
|
7
|
+
use std::rc::Rc;
|
|
8
|
+
use std::cell::RefCell;
|
|
9
|
+
use sha2::{Sha256, Digest};
|
|
10
|
+
use rusqlite::{params, Transaction, OptionalExtension};
|
|
11
|
+
use std::collections::HashMap;
|
|
12
|
+
|
|
13
|
+
use crate::embeddings::EmbeddingsManager;
|
|
14
|
+
use crate::extractors::{Extractor, ExtractionResult, Entity, Reference};
|
|
15
|
+
use crate::extractors::rust::RustExtractor;
|
|
16
|
+
use crate::extractors::typescript::TypeScriptExtractor;
|
|
17
|
+
use crate::store_v2::{StoreV2, Entity as StoreEntity, Reference as StoreReference, TypeUsage, Module};
|
|
18
|
+
use crate::store_v2_tx::StoreV2WithTx;
|
|
19
|
+
use crate::schema_v2::{EntityKind, RefKind, Visibility};
|
|
20
|
+
|
|
21
|
+
#[derive(Debug)]
|
|
22
|
+
pub struct IndexStats {
|
|
23
|
+
pub files_processed: usize,
|
|
24
|
+
pub entities_extracted: usize,
|
|
25
|
+
pub references_extracted: usize,
|
|
26
|
+
pub embeddings_generated: usize,
|
|
27
|
+
pub errors: Vec<String>,
|
|
28
|
+
pub index_time_ms: u64,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
impl Default for IndexStats {
|
|
32
|
+
fn default() -> Self {
|
|
33
|
+
Self {
|
|
34
|
+
files_processed: 0,
|
|
35
|
+
entities_extracted: 0,
|
|
36
|
+
references_extracted: 0,
|
|
37
|
+
embeddings_generated: 0,
|
|
38
|
+
errors: Vec::new(),
|
|
39
|
+
index_time_ms: 0,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pub struct AstIndexCommand {
|
|
45
|
+
project_dir: PathBuf,
|
|
46
|
+
source_path: PathBuf,
|
|
47
|
+
index_path: PathBuf,
|
|
48
|
+
file_types: Vec<String>,
|
|
49
|
+
patterns: Option<Vec<String>>,
|
|
50
|
+
force: bool,
|
|
51
|
+
embeddings_manager: EmbeddingsManager,
|
|
52
|
+
store_v2: StoreV2,
|
|
53
|
+
extractors: HashMap<String, Rc<RefCell<dyn Extractor>>>,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
impl AstIndexCommand {
|
|
57
|
+
pub fn new(
|
|
58
|
+
project_dir: &Path,
|
|
59
|
+
path: &Path,
|
|
60
|
+
file_types: Vec<String>,
|
|
61
|
+
patterns: Option<Vec<String>>,
|
|
62
|
+
force: bool,
|
|
63
|
+
) -> Result<Self> {
|
|
64
|
+
let index_path = project_dir.join(".ruvector");
|
|
65
|
+
|
|
66
|
+
// Create embeddings manager and store
|
|
67
|
+
fs::create_dir_all(&index_path)?;
|
|
68
|
+
let embeddings_manager = EmbeddingsManager::new(&index_path)?;
|
|
69
|
+
let store_v2 = StoreV2::new(&index_path.join("index_v2.db"))?;
|
|
70
|
+
|
|
71
|
+
// Initialize extractors for different languages
|
|
72
|
+
let mut extractors: HashMap<String, Rc<RefCell<dyn Extractor>>> = HashMap::new();
|
|
73
|
+
extractors.insert("rust".to_string(), Rc::new(RefCell::new(RustExtractor::new()?)));
|
|
74
|
+
extractors.insert("typescript".to_string(), Rc::new(RefCell::new(TypeScriptExtractor::new()?)));
|
|
75
|
+
|
|
76
|
+
// Use path argument for file collection
|
|
77
|
+
let source_path = if path.as_os_str().is_empty() || path == Path::new(".") {
|
|
78
|
+
project_dir.to_path_buf()
|
|
79
|
+
} else {
|
|
80
|
+
path.to_path_buf()
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
Ok(Self {
|
|
84
|
+
project_dir: project_dir.to_path_buf(),
|
|
85
|
+
source_path,
|
|
86
|
+
index_path,
|
|
87
|
+
file_types,
|
|
88
|
+
patterns,
|
|
89
|
+
force,
|
|
90
|
+
embeddings_manager,
|
|
91
|
+
store_v2,
|
|
92
|
+
extractors,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pub fn execute(&mut self) -> Result<IndexStats> {
|
|
97
|
+
let start_time = std::time::Instant::now();
|
|
98
|
+
|
|
99
|
+
info!("Starting AST-based index process");
|
|
100
|
+
info!("File types: {:?}", self.file_types);
|
|
101
|
+
|
|
102
|
+
// Initialize database schema
|
|
103
|
+
self.initialize()?;
|
|
104
|
+
|
|
105
|
+
// Walk directory and collect files
|
|
106
|
+
let files = self.collect_files()?;
|
|
107
|
+
info!("Found {} files to process", files.len());
|
|
108
|
+
|
|
109
|
+
// Process files
|
|
110
|
+
let stats = self.process_files(files)?;
|
|
111
|
+
|
|
112
|
+
let elapsed = start_time.elapsed();
|
|
113
|
+
let mut final_stats = stats;
|
|
114
|
+
final_stats.index_time_ms = elapsed.as_millis() as u64;
|
|
115
|
+
|
|
116
|
+
info!("AST indexing complete in {}ms: {} files, {} entities, {} references, {} embeddings",
|
|
117
|
+
elapsed.as_millis(),
|
|
118
|
+
final_stats.files_processed,
|
|
119
|
+
final_stats.entities_extracted,
|
|
120
|
+
final_stats.references_extracted,
|
|
121
|
+
final_stats.embeddings_generated);
|
|
122
|
+
|
|
123
|
+
Ok(final_stats)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fn initialize(&self) -> Result<()> {
|
|
127
|
+
info!("Initializing AST index components");
|
|
128
|
+
|
|
129
|
+
// Initialize database schema v2 on the connection (not in a transaction)
|
|
130
|
+
crate::schema_v2::SchemaV2::initialize(&self.store_v2.conn)?;
|
|
131
|
+
|
|
132
|
+
// Now create file_hashes and handle force rebuild in a transaction
|
|
133
|
+
let tx = self.store_v2.transaction()?;
|
|
134
|
+
|
|
135
|
+
// Create file_hashes table for incremental indexing
|
|
136
|
+
tx.execute(
|
|
137
|
+
"CREATE TABLE IF NOT EXISTS file_hashes (
|
|
138
|
+
file_path TEXT PRIMARY KEY,
|
|
139
|
+
file_hash TEXT NOT NULL,
|
|
140
|
+
indexed_at INTEGER NOT NULL
|
|
141
|
+
)",
|
|
142
|
+
[],
|
|
143
|
+
)?;
|
|
144
|
+
|
|
145
|
+
// If force rebuild, clear existing data
|
|
146
|
+
if self.force {
|
|
147
|
+
info!("Force rebuild enabled, clearing existing index");
|
|
148
|
+
tx.execute("DELETE FROM entities", [])?;
|
|
149
|
+
tx.execute("DELETE FROM refs", [])?;
|
|
150
|
+
tx.execute("DELETE FROM type_usage", [])?;
|
|
151
|
+
tx.execute("DELETE FROM modules", [])?;
|
|
152
|
+
tx.execute("DELETE FROM entity_embeddings", [])?;
|
|
153
|
+
tx.execute("DELETE FROM file_hashes", [])?;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
tx.commit()?;
|
|
157
|
+
|
|
158
|
+
info!("Components initialized");
|
|
159
|
+
Ok(())
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn collect_files(&self) -> Result<Vec<PathBuf>> {
|
|
163
|
+
info!("Collecting files to index from: {}", self.source_path.display());
|
|
164
|
+
|
|
165
|
+
let mut files = Vec::new();
|
|
166
|
+
|
|
167
|
+
let walker = WalkDir::new(&self.source_path)
|
|
168
|
+
.into_iter()
|
|
169
|
+
.filter_entry(|e| !Self::is_hidden(e))
|
|
170
|
+
.filter_map(|e| e.ok())
|
|
171
|
+
.filter(|e| {
|
|
172
|
+
// Skip directories
|
|
173
|
+
if e.file_type().is_dir() {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Skip .ruvector directory
|
|
178
|
+
if e.path().starts_with(&self.index_path) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Skip common build/output directories
|
|
183
|
+
let path_str = e.path().to_string_lossy();
|
|
184
|
+
if path_str.contains("/node_modules/") ||
|
|
185
|
+
path_str.contains("/target/") ||
|
|
186
|
+
path_str.contains("/dist/") ||
|
|
187
|
+
path_str.contains("/build/") ||
|
|
188
|
+
path_str.contains("/.git/") {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check file extension for supported languages
|
|
193
|
+
if let Some(ext) = e.path().extension() {
|
|
194
|
+
self.file_types.contains(&ext.to_string_lossy().to_string())
|
|
195
|
+
} else {
|
|
196
|
+
false
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
for entry in walker {
|
|
201
|
+
files.push(entry.path().to_path_buf());
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
info!("Found {} files to index", files.len());
|
|
205
|
+
Ok(files)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fn is_hidden(entry: &DirEntry) -> bool {
|
|
209
|
+
entry.file_name()
|
|
210
|
+
.to_str()
|
|
211
|
+
.map(|s| {
|
|
212
|
+
// Allow .claude directory
|
|
213
|
+
if s == ".claude" {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
// Skip other hidden directories
|
|
217
|
+
s.starts_with('.')
|
|
218
|
+
})
|
|
219
|
+
.unwrap_or(false)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fn process_files(&mut self, files: Vec<PathBuf>) -> Result<IndexStats> {
|
|
223
|
+
let stats = Arc::new(RwLock::new(IndexStats::default()));
|
|
224
|
+
let errors = Arc::new(RwLock::new(Vec::new()));
|
|
225
|
+
|
|
226
|
+
info!("Processing {} files with AST extraction", files.len());
|
|
227
|
+
|
|
228
|
+
for file_path in files {
|
|
229
|
+
debug!("Processing: {}", file_path.display());
|
|
230
|
+
|
|
231
|
+
if let Err(e) = self.process_file_ast(&file_path, &stats, &errors) {
|
|
232
|
+
let error_msg = format!("Failed to process {}: {}", file_path.display(), e);
|
|
233
|
+
warn!("{}", error_msg);
|
|
234
|
+
errors.write().unwrap().push(error_msg);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let stats_guard = stats.read().unwrap();
|
|
239
|
+
let errors_guard = errors.read().unwrap();
|
|
240
|
+
|
|
241
|
+
Ok(IndexStats {
|
|
242
|
+
files_processed: stats_guard.files_processed,
|
|
243
|
+
entities_extracted: stats_guard.entities_extracted,
|
|
244
|
+
references_extracted: stats_guard.references_extracted,
|
|
245
|
+
embeddings_generated: stats_guard.embeddings_generated,
|
|
246
|
+
errors: errors_guard.clone(),
|
|
247
|
+
index_time_ms: 0, // Will be set by caller
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn process_file_ast(
|
|
252
|
+
&mut self,
|
|
253
|
+
file_path: &Path,
|
|
254
|
+
stats: &Arc<RwLock<IndexStats>>,
|
|
255
|
+
errors: &Arc<RwLock<Vec<String>>>,
|
|
256
|
+
) -> Result<()> {
|
|
257
|
+
// Calculate file hash for incremental indexing
|
|
258
|
+
let file_hash = self.calculate_file_hash(file_path)?;
|
|
259
|
+
|
|
260
|
+
// Check if file needs re-indexing (unless force is enabled)
|
|
261
|
+
if !self.force && self.is_file_indexed(file_path, &file_hash)? {
|
|
262
|
+
debug!("Skipping unchanged file: {}", file_path.display());
|
|
263
|
+
return Ok(());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Read file content
|
|
267
|
+
let content = fs::read_to_string(file_path)
|
|
268
|
+
.with_context(|| format!("Failed to read file: {}", file_path.display()))?;
|
|
269
|
+
|
|
270
|
+
// Detect language and get appropriate extractor
|
|
271
|
+
let language = self.detect_language(file_path)?;
|
|
272
|
+
let extractor = self.extractors.get(&language)
|
|
273
|
+
.ok_or_else(|| anyhow!("No extractor available for language: {}", language))?;
|
|
274
|
+
|
|
275
|
+
// Extract entities and references using AST
|
|
276
|
+
let extraction_result = extractor.borrow_mut().extract(&file_path.to_string_lossy(), &content)
|
|
277
|
+
.with_context(|| format!("Failed to extract AST from: {}", file_path.display()))?;
|
|
278
|
+
|
|
279
|
+
if extraction_result.entities.is_empty() && extraction_result.references.is_empty() {
|
|
280
|
+
debug!("No entities or references found in {}", file_path.display());
|
|
281
|
+
// Still mark file as indexed to avoid re-processing
|
|
282
|
+
self.mark_file_indexed_atomic(file_path, &file_hash)?;
|
|
283
|
+
return Ok(());
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Use atomic file indexing with transactions
|
|
287
|
+
let store_tx = StoreV2WithTx::new(&self.index_path.join("index_v2.db"))
|
|
288
|
+
.context("Failed to create transactional store")?;
|
|
289
|
+
|
|
290
|
+
let file_path_str = file_path.to_string_lossy().to_string();
|
|
291
|
+
let entity_ids = store_tx.index_file_atomic(&file_path_str, &file_hash, |tx| {
|
|
292
|
+
// Store entities in batch
|
|
293
|
+
let mut store_entities = Vec::new();
|
|
294
|
+
let mut entity_map = HashMap::new();
|
|
295
|
+
let mut type_usages = Vec::new();
|
|
296
|
+
|
|
297
|
+
for (idx, entity) in extraction_result.entities.iter().enumerate() {
|
|
298
|
+
let store_entity = StoreEntity {
|
|
299
|
+
id: 0,
|
|
300
|
+
kind: self.convert_entity_kind(&entity.kind),
|
|
301
|
+
name: entity.name.clone(),
|
|
302
|
+
signature: Some(entity.signature.clone()),
|
|
303
|
+
visibility: self.convert_visibility(&entity.visibility),
|
|
304
|
+
parent_id: None, // TODO: Handle parent relationships
|
|
305
|
+
file_path: entity.file_path.clone(),
|
|
306
|
+
line_number: entity.line as i64,
|
|
307
|
+
column_number: Some(entity.column as i64),
|
|
308
|
+
doc_comment: None, // TODO: Extract doc comments
|
|
309
|
+
attributes: None, // TODO: Extract attributes
|
|
310
|
+
metadata: Some(serde_json::to_string(&entity.metadata)?),
|
|
311
|
+
created_at: chrono::Utc::now(),
|
|
312
|
+
updated_at: chrono::Utc::now(),
|
|
313
|
+
};
|
|
314
|
+
store_entities.push(store_entity);
|
|
315
|
+
|
|
316
|
+
// Store temporary mapping for reference resolution
|
|
317
|
+
entity_map.insert(idx, 0); // Will be updated with actual ID after insert
|
|
318
|
+
|
|
319
|
+
// Prepare type usages
|
|
320
|
+
let type_names = self.extract_type_names_from_signature(&entity.signature);
|
|
321
|
+
for type_name in type_names {
|
|
322
|
+
type_usages.push(TypeUsage {
|
|
323
|
+
id: 0,
|
|
324
|
+
entity_id: 0, // Will be updated after entity insert
|
|
325
|
+
type_name: type_name.clone(),
|
|
326
|
+
usage_kind: "signature".to_string(),
|
|
327
|
+
file_path: file_path_str.clone(),
|
|
328
|
+
line_number: entity.line as i64,
|
|
329
|
+
created_at: chrono::Utc::now(),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Insert entities and get their IDs
|
|
335
|
+
let mut entity_ids = Vec::new();
|
|
336
|
+
for entity in &store_entities {
|
|
337
|
+
let mut stmt = tx.prepare(
|
|
338
|
+
r#"
|
|
339
|
+
INSERT INTO entities (
|
|
340
|
+
kind, name, signature, visibility, parent_id, file_path,
|
|
341
|
+
line_number, column_number, doc_comment, attributes, metadata
|
|
342
|
+
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
|
|
343
|
+
"#
|
|
344
|
+
)?;
|
|
345
|
+
|
|
346
|
+
stmt.execute(params![
|
|
347
|
+
entity.kind.as_str(),
|
|
348
|
+
entity.name,
|
|
349
|
+
entity.signature,
|
|
350
|
+
entity.visibility.as_str(),
|
|
351
|
+
entity.parent_id,
|
|
352
|
+
entity.file_path,
|
|
353
|
+
entity.line_number,
|
|
354
|
+
entity.column_number,
|
|
355
|
+
entity.doc_comment,
|
|
356
|
+
entity.attributes,
|
|
357
|
+
entity.metadata
|
|
358
|
+
])?;
|
|
359
|
+
|
|
360
|
+
let entity_id = tx.last_insert_rowid();
|
|
361
|
+
entity_ids.push(entity_id);
|
|
362
|
+
|
|
363
|
+
// Update entity mapping
|
|
364
|
+
if let Some(idx) = entity_map.iter().find(|(_, &v)| v == 0).map(|(k, _)| *k) {
|
|
365
|
+
entity_map.insert(idx, entity_id);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Update type usages with actual entity IDs
|
|
370
|
+
for (type_usage, entity_id) in type_usages.iter_mut().zip(entity_ids.iter()) {
|
|
371
|
+
type_usage.entity_id = *entity_id;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Insert type usages in batch
|
|
375
|
+
if !type_usages.is_empty() {
|
|
376
|
+
let mut stmt = tx.prepare(
|
|
377
|
+
r#"
|
|
378
|
+
INSERT INTO type_usage (
|
|
379
|
+
entity_id, type_name, usage_kind, file_path, line_number
|
|
380
|
+
) VALUES (?1, ?2, ?3, ?4, ?5)
|
|
381
|
+
"#
|
|
382
|
+
)?;
|
|
383
|
+
|
|
384
|
+
for type_usage in &type_usages {
|
|
385
|
+
stmt.execute(params![
|
|
386
|
+
type_usage.entity_id,
|
|
387
|
+
type_usage.type_name,
|
|
388
|
+
type_usage.usage_kind,
|
|
389
|
+
type_usage.file_path,
|
|
390
|
+
type_usage.line_number
|
|
391
|
+
])?;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Store references with proper target entity resolution
|
|
396
|
+
let mut store_references = Vec::new();
|
|
397
|
+
for reference in &extraction_result.references {
|
|
398
|
+
// Try to find target entity by name
|
|
399
|
+
let target_entity_id = self.find_target_entity_tx(tx, &reference.target_name, &file_path_str)?;
|
|
400
|
+
|
|
401
|
+
// Parse source_id if available; extractors typically don't provide source_id
|
|
402
|
+
let source_entity_id = reference.source_id
|
|
403
|
+
.as_ref()
|
|
404
|
+
.and_then(|id| id.parse::<i64>().ok())
|
|
405
|
+
.unwrap_or(0);
|
|
406
|
+
|
|
407
|
+
let store_reference = StoreReference {
|
|
408
|
+
id: 0,
|
|
409
|
+
source_entity_id,
|
|
410
|
+
target_entity_id,
|
|
411
|
+
ref_kind: self.convert_ref_kind(&reference.ref_kind),
|
|
412
|
+
file_path: reference.file_path.clone(),
|
|
413
|
+
line_number: reference.line as i64,
|
|
414
|
+
column_number: Some(reference.column as i64),
|
|
415
|
+
context: reference.metadata.get("context").cloned(),
|
|
416
|
+
created_at: chrono::Utc::now(),
|
|
417
|
+
};
|
|
418
|
+
store_references.push(store_reference);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Insert references in batch
|
|
422
|
+
if !store_references.is_empty() {
|
|
423
|
+
let mut stmt = tx.prepare(
|
|
424
|
+
r#"
|
|
425
|
+
INSERT INTO refs (
|
|
426
|
+
source_entity_id, target_entity_id, ref_kind, file_path,
|
|
427
|
+
line_number, column_number, context
|
|
428
|
+
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
|
429
|
+
"#
|
|
430
|
+
)?;
|
|
431
|
+
|
|
432
|
+
for reference in &store_references {
|
|
433
|
+
stmt.execute(params![
|
|
434
|
+
reference.source_entity_id,
|
|
435
|
+
reference.target_entity_id,
|
|
436
|
+
reference.ref_kind.as_str(),
|
|
437
|
+
reference.file_path,
|
|
438
|
+
reference.line_number,
|
|
439
|
+
reference.column_number,
|
|
440
|
+
reference.context
|
|
441
|
+
])?;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Generate and store embeddings
|
|
446
|
+
if !entity_ids.is_empty() {
|
|
447
|
+
let entity_texts: Vec<String> = extraction_result.entities
|
|
448
|
+
.iter()
|
|
449
|
+
.map(|e| format!("{} {}", e.name, e.signature))
|
|
450
|
+
.collect();
|
|
451
|
+
|
|
452
|
+
let embeddings = self.embeddings_manager.generate_embeddings(&entity_texts)?;
|
|
453
|
+
|
|
454
|
+
// Store embeddings in batch
|
|
455
|
+
let mut stmt = tx.prepare(
|
|
456
|
+
"INSERT OR REPLACE INTO entity_embeddings (entity_id, embedding, embedding_model) VALUES (?1, ?2, ?3)"
|
|
457
|
+
)?;
|
|
458
|
+
|
|
459
|
+
for (entity_id, embedding) in entity_ids.iter().zip(embeddings) {
|
|
460
|
+
let embedding_bytes: Vec<u8> = embedding
|
|
461
|
+
.iter()
|
|
462
|
+
.flat_map(|&v| v.to_le_bytes().to_vec())
|
|
463
|
+
.collect();
|
|
464
|
+
|
|
465
|
+
stmt.execute(params![entity_id, embedding_bytes, "text-embedding-ada-002"])?;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
Ok(())
|
|
470
|
+
})?;
|
|
471
|
+
|
|
472
|
+
// Update stats
|
|
473
|
+
{
|
|
474
|
+
let mut s = stats.write().unwrap();
|
|
475
|
+
s.files_processed += 1;
|
|
476
|
+
s.entities_extracted += extraction_result.entities.len();
|
|
477
|
+
s.references_extracted += extraction_result.references.len();
|
|
478
|
+
s.embeddings_generated += extraction_result.entities.len();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
Ok(())
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
fn detect_language(&self, file_path: &Path) -> Result<String> {
|
|
485
|
+
if let Some(ext) = file_path.extension() {
|
|
486
|
+
match ext.to_string_lossy().as_ref() {
|
|
487
|
+
"rs" => Ok("rust".to_string()),
|
|
488
|
+
"ts" | "tsx" | "js" | "jsx" => Ok("typescript".to_string()),
|
|
489
|
+
_ => Err(anyhow!("Unsupported file extension: {}", ext.to_string_lossy())),
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
Err(anyhow!("No file extension found"))
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
fn convert_entity_kind(&self, kind: &crate::extractors::EntityKind) -> EntityKind {
|
|
497
|
+
match kind {
|
|
498
|
+
crate::extractors::EntityKind::Function => EntityKind::Function,
|
|
499
|
+
crate::extractors::EntityKind::Method => EntityKind::Method,
|
|
500
|
+
crate::extractors::EntityKind::Class => EntityKind::Class,
|
|
501
|
+
crate::extractors::EntityKind::Interface => EntityKind::Interface,
|
|
502
|
+
crate::extractors::EntityKind::Struct => EntityKind::Struct,
|
|
503
|
+
crate::extractors::EntityKind::Enum => EntityKind::Enum,
|
|
504
|
+
crate::extractors::EntityKind::Trait => EntityKind::Trait,
|
|
505
|
+
crate::extractors::EntityKind::Module => EntityKind::Module,
|
|
506
|
+
crate::extractors::EntityKind::TypeAlias => EntityKind::TypeAlias,
|
|
507
|
+
crate::extractors::EntityKind::Variable => EntityKind::Variable,
|
|
508
|
+
crate::extractors::EntityKind::Constant => EntityKind::Constant,
|
|
509
|
+
crate::extractors::EntityKind::Import => EntityKind::Function, // Convert imports to function for now
|
|
510
|
+
crate::extractors::EntityKind::Constructor => EntityKind::Function, // Map to function
|
|
511
|
+
crate::extractors::EntityKind::Getter => EntityKind::Method, // Map to method
|
|
512
|
+
crate::extractors::EntityKind::Setter => EntityKind::Method, // Map to method
|
|
513
|
+
crate::extractors::EntityKind::Namespace => EntityKind::Module, // Map to module
|
|
514
|
+
crate::extractors::EntityKind::Parameter => EntityKind::Variable, // Map to variable
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
fn convert_visibility(&self, visibility: &crate::extractors::Visibility) -> Visibility {
|
|
519
|
+
match visibility {
|
|
520
|
+
crate::extractors::Visibility::Public => Visibility::Public,
|
|
521
|
+
crate::extractors::Visibility::Private => Visibility::Private,
|
|
522
|
+
crate::extractors::Visibility::Protected => Visibility::Protected,
|
|
523
|
+
crate::extractors::Visibility::Internal => Visibility::Internal,
|
|
524
|
+
crate::extractors::Visibility::FilePrivate => Visibility::Private,
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
fn convert_ref_kind(&self, kind: &crate::extractors::RefKind) -> RefKind {
|
|
529
|
+
match kind {
|
|
530
|
+
crate::extractors::RefKind::Calls => RefKind::Call,
|
|
531
|
+
crate::extractors::RefKind::Imports => RefKind::Import,
|
|
532
|
+
crate::extractors::RefKind::Extends => RefKind::Extend,
|
|
533
|
+
crate::extractors::RefKind::Implements => RefKind::Implement,
|
|
534
|
+
crate::extractors::RefKind::Uses => RefKind::Use,
|
|
535
|
+
crate::extractors::RefKind::Instantiates => RefKind::Reference,
|
|
536
|
+
crate::extractors::RefKind::Overrides => RefKind::Reference,
|
|
537
|
+
crate::extractors::RefKind::Reads => RefKind::Reference,
|
|
538
|
+
crate::extractors::RefKind::Writes => RefKind::Reference,
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
fn store_type_usage(&self, entity: &Entity, entity_id: i64, file_path: &Path) -> Result<()> {
|
|
543
|
+
// Extract type names from signature (simple heuristic)
|
|
544
|
+
let type_names = self.extract_type_names_from_signature(&entity.signature);
|
|
545
|
+
|
|
546
|
+
for type_name in type_names {
|
|
547
|
+
let type_usage = TypeUsage {
|
|
548
|
+
id: 0,
|
|
549
|
+
entity_id,
|
|
550
|
+
type_name: type_name.clone(),
|
|
551
|
+
usage_kind: "signature".to_string(),
|
|
552
|
+
file_path: file_path.to_string_lossy().to_string(),
|
|
553
|
+
line_number: entity.line as i64,
|
|
554
|
+
created_at: chrono::Utc::now(),
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
self.store_v2.insert_type_usage(&type_usage)?;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
Ok(())
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
fn extract_type_names_from_signature(&self, signature: &str) -> Vec<String> {
|
|
564
|
+
// Simple regex-based type extraction
|
|
565
|
+
// TODO: Use AST for more accurate type extraction
|
|
566
|
+
let mut types = Vec::new();
|
|
567
|
+
|
|
568
|
+
// Common patterns
|
|
569
|
+
let patterns = vec![
|
|
570
|
+
r"\b([A-Z][a-zA-Z0-9_]*)\b", // CamelCase types
|
|
571
|
+
r"Result<([^,>]+)", // Result<T>
|
|
572
|
+
r"Option<([^>]+)", // Option<T>
|
|
573
|
+
r"Vec<([^>]+)", // Vec<T>
|
|
574
|
+
r"HashMap<([^,>]+)", // HashMap<K>
|
|
575
|
+
];
|
|
576
|
+
|
|
577
|
+
for pattern in patterns {
|
|
578
|
+
if let Ok(re) = regex::Regex::new(pattern) {
|
|
579
|
+
for cap in re.captures_iter(signature) {
|
|
580
|
+
if let Some(type_match) = cap.get(1) {
|
|
581
|
+
let type_name = type_match.as_str().to_string();
|
|
582
|
+
if !types.contains(&type_name) {
|
|
583
|
+
types.push(type_name);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
types
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
fn find_target_entity(&self, target_name: &str, source_file: &Path) -> Result<i64> {
|
|
594
|
+
// Search for target entity in the same file first
|
|
595
|
+
let entities = self.store_v2.find_entities_by_name(target_name, 10)?;
|
|
596
|
+
|
|
597
|
+
for entity in &entities {
|
|
598
|
+
// Prefer entities in the same file
|
|
599
|
+
if entity.file_path == source_file.to_string_lossy() {
|
|
600
|
+
return Ok(entity.id);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// If not found in same file, return the first match
|
|
605
|
+
if let Some(entity) = entities.first() {
|
|
606
|
+
Ok(entity.id)
|
|
607
|
+
} else {
|
|
608
|
+
// Create a placeholder entity for unknown references
|
|
609
|
+
let placeholder = StoreEntity {
|
|
610
|
+
id: 0,
|
|
611
|
+
kind: EntityKind::Function,
|
|
612
|
+
name: target_name.to_string(),
|
|
613
|
+
signature: None,
|
|
614
|
+
visibility: Visibility::Public,
|
|
615
|
+
parent_id: None,
|
|
616
|
+
file_path: "unknown".to_string(),
|
|
617
|
+
line_number: 0,
|
|
618
|
+
column_number: None,
|
|
619
|
+
doc_comment: None,
|
|
620
|
+
attributes: None,
|
|
621
|
+
metadata: None,
|
|
622
|
+
created_at: chrono::Utc::now(),
|
|
623
|
+
updated_at: chrono::Utc::now(),
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
Ok(self.store_v2.insert_entity(&placeholder)?)
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Transaction-aware version for finding target entity
|
|
631
|
+
fn find_target_entity_tx(&self, tx: &Transaction, target_name: &str, source_file: &str) -> Result<i64> {
|
|
632
|
+
// Search for target entity in the same file first
|
|
633
|
+
let mut stmt = tx.prepare(
|
|
634
|
+
"SELECT * FROM entities WHERE name = ? ORDER BY file_path = ? DESC LIMIT 1"
|
|
635
|
+
)?;
|
|
636
|
+
|
|
637
|
+
let entity = stmt.query_row(
|
|
638
|
+
params![target_name, source_file],
|
|
639
|
+
|row| {
|
|
640
|
+
Ok((
|
|
641
|
+
row.get::<_, i64>(0)?, // id
|
|
642
|
+
row.get::<_, String>(6)?, // file_path
|
|
643
|
+
))
|
|
644
|
+
}
|
|
645
|
+
).optional().map_err(|e| anyhow::anyhow!("Database error: {}", e))?;
|
|
646
|
+
|
|
647
|
+
if let Some((entity_id, _)) = entity {
|
|
648
|
+
Ok(entity_id)
|
|
649
|
+
} else {
|
|
650
|
+
// Create a placeholder entity for unknown references
|
|
651
|
+
let mut stmt = tx.prepare(
|
|
652
|
+
r#"
|
|
653
|
+
INSERT INTO entities (
|
|
654
|
+
kind, name, signature, visibility, parent_id, file_path,
|
|
655
|
+
line_number, column_number, doc_comment, attributes, metadata
|
|
656
|
+
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
|
|
657
|
+
"#
|
|
658
|
+
)?;
|
|
659
|
+
|
|
660
|
+
stmt.execute(params![
|
|
661
|
+
EntityKind::Function.as_str(),
|
|
662
|
+
target_name,
|
|
663
|
+
None::<String>,
|
|
664
|
+
Visibility::Public.as_str(),
|
|
665
|
+
None::<i64>,
|
|
666
|
+
"unknown",
|
|
667
|
+
0i64,
|
|
668
|
+
None::<i64>,
|
|
669
|
+
None::<String>,
|
|
670
|
+
None::<String>,
|
|
671
|
+
None::<String>,
|
|
672
|
+
])?;
|
|
673
|
+
|
|
674
|
+
Ok(tx.last_insert_rowid())
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Find entity by name and line number within a transaction
|
|
679
|
+
fn find_entity_by_name_and_line_tx(&self, tx: &Transaction, name: &str, line: usize, file_path: &str) -> Result<i64> {
|
|
680
|
+
let mut stmt = tx.prepare(
|
|
681
|
+
"SELECT id FROM entities WHERE name = ? AND file_path = ? AND line_number = ? LIMIT 1"
|
|
682
|
+
)?;
|
|
683
|
+
|
|
684
|
+
let entity_id = stmt.query_row(
|
|
685
|
+
params![name, file_path, line as i64],
|
|
686
|
+
|row| row.get(0)
|
|
687
|
+
).optional().map_err(|e| anyhow::anyhow!("Database error: {}", e))?;
|
|
688
|
+
|
|
689
|
+
if let Some(id) = entity_id {
|
|
690
|
+
Ok(id)
|
|
691
|
+
} else {
|
|
692
|
+
// Try to find any entity with that name in the file
|
|
693
|
+
let mut stmt = tx.prepare(
|
|
694
|
+
"SELECT id FROM entities WHERE name = ? AND file_path = ? LIMIT 1"
|
|
695
|
+
)?;
|
|
696
|
+
|
|
697
|
+
let entity_id = stmt.query_row(
|
|
698
|
+
params![name, file_path],
|
|
699
|
+
|row| row.get(0)
|
|
700
|
+
).optional().map_err(|e| anyhow::anyhow!("Database error: {}", e))?;
|
|
701
|
+
|
|
702
|
+
if let Some(id) = entity_id {
|
|
703
|
+
Ok(id)
|
|
704
|
+
} else {
|
|
705
|
+
Err(anyhow!("Entity not found: {} at line {} in {}", name, line, file_path))
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Atomic version of mark_file_indexed
|
|
711
|
+
fn mark_file_indexed_atomic(&self, file_path: &Path, file_hash: &str) -> Result<()> {
|
|
712
|
+
let tx = self.store_v2.transaction()?;
|
|
713
|
+
tx.execute(
|
|
714
|
+
"INSERT OR REPLACE INTO file_hashes (file_path, file_hash, indexed_at) VALUES (?1, ?2, ?3)",
|
|
715
|
+
params![
|
|
716
|
+
file_path.to_string_lossy(),
|
|
717
|
+
file_hash,
|
|
718
|
+
chrono::Utc::now().timestamp()
|
|
719
|
+
],
|
|
720
|
+
)?;
|
|
721
|
+
tx.commit()?;
|
|
722
|
+
Ok(())
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
fn is_file_indexed(&self, file_path: &Path, file_hash: &str) -> Result<bool> {
|
|
726
|
+
let mut stmt = self.store_v2.conn.prepare(
|
|
727
|
+
"SELECT COUNT(*) FROM file_hashes WHERE file_path = ? AND file_hash = ?"
|
|
728
|
+
)?;
|
|
729
|
+
|
|
730
|
+
let count: i64 = stmt.query_row(
|
|
731
|
+
params![file_path.to_string_lossy(), file_hash],
|
|
732
|
+
|row| row.get(0)
|
|
733
|
+
)?;
|
|
734
|
+
|
|
735
|
+
Ok(count > 0)
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
fn mark_file_indexed(&self, file_path: &Path, file_hash: &str) -> Result<()> {
|
|
739
|
+
self.store_v2.conn.execute(
|
|
740
|
+
"INSERT OR REPLACE INTO file_hashes (file_path, file_hash, indexed_at) VALUES (?1, ?2, ?3)",
|
|
741
|
+
params![
|
|
742
|
+
file_path.to_string_lossy(),
|
|
743
|
+
file_hash,
|
|
744
|
+
chrono::Utc::now().timestamp()
|
|
745
|
+
],
|
|
746
|
+
)?;
|
|
747
|
+
Ok(())
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
fn calculate_file_hash(&self, file_path: &Path) -> Result<String> {
|
|
751
|
+
use std::io::Read;
|
|
752
|
+
|
|
753
|
+
let mut file = fs::File::open(file_path)?;
|
|
754
|
+
let mut hasher = Sha256::new();
|
|
755
|
+
let mut buffer = [0; 8192];
|
|
756
|
+
|
|
757
|
+
loop {
|
|
758
|
+
let n = file.read(&mut buffer)?;
|
|
759
|
+
if n == 0 {
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
hasher.update(&buffer[..n]);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
Ok(format!("{:x}", hasher.finalize()))
|
|
766
|
+
}
|
|
767
|
+
}
|