memorylink 1.0.0
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/.cursorrules +0 -0
- package/.github/workflows/buddy-check.yml +105 -0
- package/.github/workflows/memorylink-preflight.yml +63 -0
- package/.github/workflows/release-on-tag.yml +58 -0
- package/.github/workflows/stress-tests.yml +79 -0
- package/.memorylinkignore +24 -0
- package/5000_SCENARIOS_TEST_RESULTS.md +174 -0
- package/ADVANCED_SCENARIOS_TEST_RESULTS.md +377 -0
- package/AGGRESSIVE_RANDOM_TEST_RESULTS.md +134 -0
- package/AI_CONSENSUS_ANALYSIS.md +138 -0
- package/AI_CONSENSUS_ANALYSIS_FINAL.md +345 -0
- package/AI_CONSENSUS_ANALYSIS_v2.md +188 -0
- package/AI_CONSENSUS_ANALYSIS_v3.md +246 -0
- package/AI_CONSENSUS_ANALYSIS_v4.md +309 -0
- package/AI_CONSENSUS_ANALYSIS_v5.md +311 -0
- package/AI_CONSENSUS_ANALYSIS_v6.md +432 -0
- package/AI_PANEL_CLARIFICATION_REQUEST.md +37 -0
- package/AI_RESPONSES_BLACKBOX.md +338 -0
- package/AI_RESPONSES_CHATGPT.md +379 -0
- package/AI_RESPONSES_CLAUDE.md +464 -0
- package/AI_RESPONSES_CONSOLIDATED.md +560 -0
- package/AI_RESPONSES_DEEPSEEK.md +341 -0
- package/AI_RESPONSES_GEMINI.md +262 -0
- package/AI_RESPONSES_GROK.md +335 -0
- package/AI_RESPONSES_MANUS.md +246 -0
- package/AI_RESPONSES_PERPLEXITY.md +295 -0
- package/AI_RESPONSES_QWEN.md +335 -0
- package/AI_REVIEW_REQUEST.md +333 -0
- package/AI_STRATEGIC_CONSENSUS_COMPARISON.md +507 -0
- package/AI_VALIDATION_AND_GAP_ANALYSIS.md +410 -0
- package/ALL_10_AI_RESPONSES_FINAL.md +435 -0
- package/ALL_3_AI_RESPONSES_FINAL.md +305 -0
- package/ALL_4_AI_RESPONSES_FINAL.md +335 -0
- package/ALL_5_AI_RESPONSES_FINAL.md +349 -0
- package/ALL_6_AI_RESPONSES_FINAL.md +354 -0
- package/ALL_7_AI_RESPONSES_FINAL.md +369 -0
- package/ALL_8_AI_RESPONSES_FINAL.md +381 -0
- package/ALL_9_AI_RESPONSES_FINAL.md +398 -0
- package/ALL_AI_RESPONSES_TRACKER.md +152 -0
- package/ALL_AI_RESPONSES_VALIDATED.md +261 -0
- package/ALL_FEATURES_COMPLETE.md +198 -0
- package/BREAK_IT_TEST_RESULTS.md +273 -0
- package/BUDDY_CHECK_STRESS_TEST_PLAN.md +1089 -0
- package/CHANGELOG.md +135 -0
- package/CHATGPT_GAP_ANALYSIS.md +286 -0
- package/CHATGPT_V2_ANALYSIS.md +109 -0
- package/CHECK_MISSING_FEATURES.md +192 -0
- package/CI_CD_INTEGRATION.md +421 -0
- package/COMPETITIVE_LAUNCH_STRATEGY.md +257 -0
- package/COMPLETE_COMPETITIVE_ANALYSIS_ALL_AIS.md +339 -0
- package/COMPLETE_DEVELOPMENT_PLAN_ALL_AIS.md +622 -0
- package/COMPREHENSIVE_FEATURE_ANALYSIS_100_PERCENT.md +423 -0
- package/COMPREHENSIVE_TEST_SUMMARY.md +314 -0
- package/CONTINUOUS_TESTING_COMPLETE.md +268 -0
- package/CONTINUOUS_TESTING_GUIDE.md +328 -0
- package/CONTINUOUS_TEST_FINAL_RESULTS.md +148 -0
- package/CONTINUOUS_TEST_INSTRUCTIONS.md +173 -0
- package/CONTINUOUS_TEST_RESULTS.md +194 -0
- package/CONTINUOUS_TEST_STATUS.md +68 -0
- package/CURSOR_AI_BUDDY_CHECK_GUIDE.md +439 -0
- package/CURSOR_AI_INTEGRATION_GUIDE.md +775 -0
- package/CURSOR_AI_V1.4_NEXT_STEPS.md +314 -0
- package/CURSOR_BREAK_IT_TEST.md +389 -0
- package/CURSOR_DOCUMENTATION_RULES.md +259 -0
- package/CURSOR_HOSTILE_TEST_DOCUMENT.md +343 -0
- package/CURSOR_PROMPTS_FOR_TESTING.md +252 -0
- package/DEPLOYMENT_GUIDE.md +493 -0
- package/DEVELOPMENT_AND_OVERNIGHT_TESTING.md +304 -0
- package/DEVELOPMENT_PROGRESS.md +185 -0
- package/DOCS_CLEANUP_SUMMARY.md +192 -0
- package/DOC_CONFIDENTIALITY_RULES.md +259 -0
- package/E2E_TEST_REPORT_v1.3.0.md +196 -0
- package/E2E_TEST_RESULTS.md +250 -0
- package/E2E_TEST_SCENARIOS.md +357 -0
- package/END_TO_END_TEST_REPORT.md +217 -0
- package/ENHANCEMENT_RECOMMENDATIONS.md +368 -0
- package/EPIPE_FIX_SUMMARY.md +177 -0
- package/FEEDBACK_TEMPLATE.md +173 -0
- package/FINAL_100_PERCENT_CONFIRMATION.md +319 -0
- package/FINAL_8_AI_CONSENSUS_SUMMARY.md +355 -0
- package/FINAL_CONFIRMATION.md +143 -0
- package/FINAL_E2E_TEST_REPORT.md +248 -0
- package/FINAL_E2E_TEST_RESULTS.md +212 -0
- package/FINAL_LAUNCH_CLARIFICATION_SUMMARY.md +101 -0
- package/FINAL_LAUNCH_PLAN_BASED_ON_AI_CONSENSUS.md +410 -0
- package/FINAL_LAUNCH_SUMMARY.md +176 -0
- package/FINAL_PRODUCT_TEST.md +316 -0
- package/FINAL_PROJECT_STATUS.md +407 -0
- package/FINAL_STATUS_REPORT.md +244 -0
- package/FINAL_STRATEGIC_PLAN_9_AIS.md +576 -0
- package/FINAL_TEST_EXECUTION_REPORT.md +252 -0
- package/FINAL_VALIDATION_DOCUMENT.md +238 -0
- package/FINAL_VALIDATION_SUMMARY.md +230 -0
- package/FIX_SPECIAL_CHARS.sh +13 -0
- package/FRESH_SCENARIOS_TEST_RESULTS.md +358 -0
- package/GAP_EVALUATION_TEMPLATE.md +146 -0
- package/GITHUB_SETUP_GUIDE.md +193 -0
- package/HOSTILE_TEST_RESULTS.md +221 -0
- package/HOW_MEMORYLINK_HELPS_AI.md +401 -0
- package/IMPLEMENTATION_PLANS_DETAILED.md +516 -0
- package/LAUNCH_CHECKLIST.md +247 -0
- package/LAUNCH_DOCS_FRAMEWORK.md +378 -0
- package/LAUNCH_READINESS.md +148 -0
- package/LAUNCH_SEQUENCE.md +137 -0
- package/LICENSE +67 -0
- package/MARKET_ANALYSIS_AND_STRATEGY.md +280 -0
- package/MASTER_AI_VERIFICATION_DOCUMENT.md +1085 -0
- package/MASTER_VALIDATION_DOCUMENT.md +818 -0
- package/MINORITY_OPINION_ANALYSIS.md +464 -0
- package/NEW_RANDOM_TEST_RESULTS.md +127 -0
- package/NEW_SCENARIOS_TEST_RESULTS.md +272 -0
- package/NEXT_ACTIONS_COMPLETE.md +137 -0
- package/NEXT_PLAN_BASED_ON_AI_ANALYSES.md +413 -0
- package/NEXT_PLAN_BASED_ON_ALL_AI_RESPONSES.md +558 -0
- package/NEXT_STEPS.md +120 -0
- package/NEXT_STEPS_ACTION_PLAN.md +369 -0
- package/NPM_2FA_FIX.md +113 -0
- package/NPM_PUBLISH_TROUBLESHOOTING.md +230 -0
- package/PERPLEXITY_AI_VALIDATION_REQUEST.md +318 -0
- package/PERPLEXITY_AI_VALIDATION_RESPONSE.md +172 -0
- package/PERPLEXITY_BREAK_IT_VALIDATION.md +262 -0
- package/PERPLEXITY_DOCS_VALIDATION.md +237 -0
- package/PERPLEXITY_FEEDBACK_ACTION_PLAN.md +271 -0
- package/PERPLEXITY_FINAL_E2E_VALIDATION.md +210 -0
- package/PERPLEXITY_FINAL_SUMMARY.md +211 -0
- package/PERPLEXITY_PHASE2_VALIDATION.md +270 -0
- package/PERPLEXITY_PHASE2_VALIDATION_RESPONSE.md +136 -0
- package/PERPLEXITY_PRIORITY2_VALIDATION.md +321 -0
- package/PERPLEXITY_TELEMETRY_EXPLANATION.md +174 -0
- package/PERPLEXITY_TELEMETRY_VALIDATION.md +118 -0
- package/PERPLEXITY_TELEMETRY_VALIDATION_RESPONSE.md +154 -0
- package/PERPLEXITY_USER_GUIDE_VALIDATION.md +236 -0
- package/PERPLEXITY_VALIDATION_REQUEST.md +427 -0
- package/PERPLEXITY_VALIDATION_REQUEST_v1.5.1.md +190 -0
- package/PHASE_2_COMPLETE.md +149 -0
- package/PRE_LAUNCH_SECURITY_AUDIT.md +155 -0
- package/PRE_LAUNCH_TEST_CYCLE.md +326 -0
- package/PRE_LAUNCH_TEST_RESULTS.md +148 -0
- package/PROJECT_STRUCTURE_PLAN.md +104 -0
- package/PUBLIC_DOCS.md +90 -0
- package/PUBLISH_CHECKLIST.md +134 -0
- package/PUSH_INSTRUCTIONS.md +120 -0
- package/QUICK_START_TEST_CYCLE.md +76 -0
- package/README.md +557 -0
- package/README_TEST_INSTRUCTIONS.md +65 -0
- package/README_v1.5.1.md +137 -0
- package/REALISTIC_ASSESSMENT.md +186 -0
- package/REAL_WORLD_VALIDATION_COMPLETE.md +98 -0
- package/RED_TEAM_TESTING_GUIDE.md +302 -0
- package/RELEASE_NOTES_v1.0.0.md +125 -0
- package/RELEASE_NOTES_v1.5.1.md +105 -0
- package/REQUEST_COUNTERS.md +22 -0
- package/ROADMAP_v1.6.md +335 -0
- package/ROUND3_RANDOM_TEST_RESULTS.md +135 -0
- package/SECURITY_MODEL.md +577 -0
- package/SESSION_SUMMARY_CURRENT_STATE.md +206 -0
- package/SESSION_SUMMARY_REVIEW.md +203 -0
- package/SINGLE_RUN_ALL_SCENARIOS_TEST.sh +129 -0
- package/STRATEGIC_QUESTIONS_FOR_AI_VALIDATION.md +277 -0
- package/STRESS_TEST_CHECK_RESULTS.md +154 -0
- package/STRESS_TEST_EXECUTION_GUIDE.md +284 -0
- package/STRESS_TEST_IMPLEMENTATION_SUMMARY.md +221 -0
- package/TELEMETRY.md +370 -0
- package/TELEMETRY_COMPLETE_SUMMARY.md +231 -0
- package/TELEMETRY_CONTROL_POLICY.md +135 -0
- package/TELEMETRY_DESIGN_SUMMARY.md +210 -0
- package/TELEMETRY_FINAL_STATUS.md +178 -0
- package/TELEMETRY_NEXT_STEPS.md +258 -0
- package/TELEMETRY_TESTING_NOTES.md +217 -0
- package/TELEMETRY_WORK_COMPLETE.md +237 -0
- package/TEST_PLAN_v1.0.1.md +194 -0
- package/TEST_RESULTS_SUMMARY.md +128 -0
- package/TREE_SITTER_EXPLANATION.md +303 -0
- package/TROUBLESHOOTING.md +62 -0
- package/ULTIMATE_SCENARIOS_TEST_RESULTS.md +366 -0
- package/USER_FEEDBACK_TEMPLATE.md +104 -0
- package/USER_GUIDE.md +809 -0
- package/V1.1_DEVELOPMENT_COMPLETE.md +299 -0
- package/V1.1_SCENARIOS_ADDED.md +161 -0
- package/V1.2_CODE_STRUCTURE_IMPLEMENTATION.md +243 -0
- package/V1.3_COMPETITIVE_LAUNCH_COMPLETE.md +253 -0
- package/V1.3_COMPETITIVE_LAUNCH_IMPLEMENTATION_PLAN.md +385 -0
- package/V1.3_TEAM_PATTERNS_IMPLEMENTATION.md +183 -0
- package/V1.4_BUILD_PLAN_IMPLEMENTATION.md +698 -0
- package/V1.4_COMPLETE_SUMMARY_FOR_AI_REVIEW.md +516 -0
- package/V1.4_COMPLETE_VALIDATION_DOCUMENT.md +601 -0
- package/V1.4_DEVELOPMENT_PROGRESS.md +117 -0
- package/V1.4_FINAL_STATUS.md +147 -0
- package/V1.4_INTEGRATION_COMPLETE.md +207 -0
- package/V1.4_INTEGRATION_TEST_RESULTS.md +181 -0
- package/V1.4_OBSERVABILITY_AND_OVERRIDE_COMPLETE.md +180 -0
- package/V1.4_PHASE_3_COMPLETE.md +135 -0
- package/V1.4_RUNTIME_TESTING_GUIDE.md +364 -0
- package/V1.4_VERIFICATION_REPORT.md +199 -0
- package/V1.5.1_COMPLETE_SUMMARY.md +234 -0
- package/V1.5.1_RELEASE_NOTES.md +206 -0
- package/V1.5.1_RELEASE_READY.md +198 -0
- package/V1.5_COMPLETE_SUMMARY.md +264 -0
- package/V1.5_COMPLETE_VERIFICATION.md +183 -0
- package/V1.5_DESIGN_NOTES.md +272 -0
- package/V1.5_FINAL_STATUS.md +224 -0
- package/V1.5_IMPLEMENTATION_SUMMARY.md +113 -0
- package/V1.5_IMPROVEMENTS_COMPLETE.md +205 -0
- package/V1.5_PHASE1_COMPLETE.md +183 -0
- package/V1.5_PHASE1_PROGRESS.md +102 -0
- package/V1.5_PHASE2_COMPLETE.md +133 -0
- package/V1.5_PHASE2_PLAN.md +185 -0
- package/V1.5_PRIORITIZATION.md +313 -0
- package/V1.5_PRIORITY2_COMPLETE.md +150 -0
- package/V1.5_TESTING_COMPLETE.md +69 -0
- package/V1.5_TEST_RESULTS.md +178 -0
- package/V1.5_VALIDATION_RESULTS.md +209 -0
- package/V1.6_GAP_TRACKING.md +118 -0
- package/VALIDATION_SUMMARY_FOR_PERPLEXITY.md +83 -0
- package/VERIFICATION_REPORT.md +220 -0
- package/VERSION_UPDATE_VERIFICATION.md +76 -0
- package/config/tsconfig.json +21 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1114 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/archive.d.ts +20 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +231 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/auto-context.d.ts +22 -0
- package/dist/commands/auto-context.d.ts.map +1 -0
- package/dist/commands/auto-context.js +172 -0
- package/dist/commands/auto-context.js.map +1 -0
- package/dist/commands/auto-log.d.ts +30 -0
- package/dist/commands/auto-log.d.ts.map +1 -0
- package/dist/commands/auto-log.js +500 -0
- package/dist/commands/auto-log.js.map +1 -0
- package/dist/commands/change.d.ts +13 -0
- package/dist/commands/change.d.ts.map +1 -0
- package/dist/commands/change.js +254 -0
- package/dist/commands/change.js.map +1 -0
- package/dist/commands/checkpoint.d.ts +26 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +326 -0
- package/dist/commands/checkpoint.js.map +1 -0
- package/dist/commands/configure.d.ts +21 -0
- package/dist/commands/configure.d.ts.map +1 -0
- package/dist/commands/configure.js +283 -0
- package/dist/commands/configure.js.map +1 -0
- package/dist/commands/consolidate.d.ts +19 -0
- package/dist/commands/consolidate.d.ts.map +1 -0
- package/dist/commands/consolidate.js +236 -0
- package/dist/commands/consolidate.js.map +1 -0
- package/dist/commands/context.d.ts +10 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +571 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/detect.d.ts +13 -0
- package/dist/commands/detect.d.ts.map +1 -0
- package/dist/commands/detect.js +187 -0
- package/dist/commands/detect.js.map +1 -0
- package/dist/commands/doctor.d.ts +19 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +1272 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +95 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/graph.d.ts +25 -0
- package/dist/commands/graph.d.ts.map +1 -0
- package/dist/commands/graph.js +208 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +240 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/impact.d.ts +18 -0
- package/dist/commands/impact.d.ts.map +1 -0
- package/dist/commands/impact.js +163 -0
- package/dist/commands/impact.js.map +1 -0
- package/dist/commands/index-vector.d.ts +13 -0
- package/dist/commands/index-vector.d.ts.map +1 -0
- package/dist/commands/index-vector.js +103 -0
- package/dist/commands/index-vector.js.map +1 -0
- package/dist/commands/index.d.ts +37 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +105 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +200 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inject.d.ts +22 -0
- package/dist/commands/inject.d.ts.map +1 -0
- package/dist/commands/inject.js +394 -0
- package/dist/commands/inject.js.map +1 -0
- package/dist/commands/learn.d.ts +13 -0
- package/dist/commands/learn.d.ts.map +1 -0
- package/dist/commands/learn.js +282 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/lock.d.ts +35 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +308 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/memory.d.ts +15 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/memory.js +366 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/migrate.d.ts +22 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +458 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/patterns.d.ts +18 -0
- package/dist/commands/patterns.d.ts.map +1 -0
- package/dist/commands/patterns.js +120 -0
- package/dist/commands/patterns.js.map +1 -0
- package/dist/commands/protect.d.ts +12 -0
- package/dist/commands/protect.d.ts.map +1 -0
- package/dist/commands/protect.js +181 -0
- package/dist/commands/protect.js.map +1 -0
- package/dist/commands/quickstart.d.ts +11 -0
- package/dist/commands/quickstart.d.ts.map +1 -0
- package/dist/commands/quickstart.js +256 -0
- package/dist/commands/quickstart.js.map +1 -0
- package/dist/commands/repair.d.ts +13 -0
- package/dist/commands/repair.d.ts.map +1 -0
- package/dist/commands/repair.js +157 -0
- package/dist/commands/repair.js.map +1 -0
- package/dist/commands/resolve.d.ts +19 -0
- package/dist/commands/resolve.d.ts.map +1 -0
- package/dist/commands/resolve.js +355 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/roadmap.d.ts +5 -0
- package/dist/commands/roadmap.d.ts.map +1 -0
- package/dist/commands/roadmap.js +23 -0
- package/dist/commands/roadmap.js.map +1 -0
- package/dist/commands/scopes.d.ts +10 -0
- package/dist/commands/scopes.d.ts.map +1 -0
- package/dist/commands/scopes.js +80 -0
- package/dist/commands/scopes.js.map +1 -0
- package/dist/commands/search.d.ts +9 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +313 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/setup.d.ts +13 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +405 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/snippet.d.ts +23 -0
- package/dist/commands/snippet.d.ts.map +1 -0
- package/dist/commands/snippet.js +235 -0
- package/dist/commands/snippet.js.map +1 -0
- package/dist/commands/stats.d.ts +15 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +502 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +134 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/suggest-tags.d.ts +9 -0
- package/dist/commands/suggest-tags.d.ts.map +1 -0
- package/dist/commands/suggest-tags.js +95 -0
- package/dist/commands/suggest-tags.js.map +1 -0
- package/dist/commands/sync-rules.d.ts +14 -0
- package/dist/commands/sync-rules.d.ts.map +1 -0
- package/dist/commands/sync-rules.js +211 -0
- package/dist/commands/sync-rules.js.map +1 -0
- package/dist/commands/sync.d.ts +24 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +330 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/telemetry-test.d.ts +24 -0
- package/dist/commands/telemetry-test.d.ts.map +1 -0
- package/dist/commands/telemetry-test.js +84 -0
- package/dist/commands/telemetry-test.js.map +1 -0
- package/dist/commands/template.d.ts +16 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +122 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/validate.d.ts +11 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +144 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/watch-preferences.d.ts +17 -0
- package/dist/commands/watch-preferences.d.ts.map +1 -0
- package/dist/commands/watch-preferences.js +172 -0
- package/dist/commands/watch-preferences.js.map +1 -0
- package/dist/commands/watch.d.ts +11 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +223 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config/thresholds.d.ts +8 -0
- package/dist/config/thresholds.d.ts.map +1 -0
- package/dist/config/thresholds.js +10 -0
- package/dist/config/thresholds.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/memorylink.d.ts +91 -0
- package/dist/memorylink.d.ts.map +1 -0
- package/dist/memorylink.js +208 -0
- package/dist/memorylink.js.map +1 -0
- package/dist/search/local-embeddings.d.ts +21 -0
- package/dist/search/local-embeddings.d.ts.map +1 -0
- package/dist/search/local-embeddings.js +87 -0
- package/dist/search/local-embeddings.js.map +1 -0
- package/dist/search/vector-search.d.ts +58 -0
- package/dist/search/vector-search.d.ts.map +1 -0
- package/dist/search/vector-search.js +535 -0
- package/dist/search/vector-search.js.map +1 -0
- package/dist/server/mcp-server.d.ts +18 -0
- package/dist/server/mcp-server.d.ts.map +1 -0
- package/dist/server/mcp-server.js +293 -0
- package/dist/server/mcp-server.js.map +1 -0
- package/dist/telemetry.d.ts +92 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +339 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/telemetry.test.d.ts +13 -0
- package/dist/telemetry.test.d.ts.map +1 -0
- package/dist/telemetry.test.js +324 -0
- package/dist/telemetry.test.js.map +1 -0
- package/dist/test-runner/TestRunner.d.ts +68 -0
- package/dist/test-runner/TestRunner.d.ts.map +1 -0
- package/dist/test-runner/TestRunner.js +384 -0
- package/dist/test-runner/TestRunner.js.map +1 -0
- package/dist/test-runner/performance-test.d.ts +36 -0
- package/dist/test-runner/performance-test.d.ts.map +1 -0
- package/dist/test-runner/performance-test.js +163 -0
- package/dist/test-runner/performance-test.js.map +1 -0
- package/dist/test-runner/run-tests.d.ts +7 -0
- package/dist/test-runner/run-tests.d.ts.map +1 -0
- package/dist/test-runner/run-tests.js +167 -0
- package/dist/test-runner/run-tests.js.map +1 -0
- package/dist/types.d.ts +400 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +81 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/batch-commits.d.ts +48 -0
- package/dist/utils/batch-commits.d.ts.map +1 -0
- package/dist/utils/batch-commits.js +164 -0
- package/dist/utils/batch-commits.js.map +1 -0
- package/dist/utils/code-structure.d.ts +62 -0
- package/dist/utils/code-structure.d.ts.map +1 -0
- package/dist/utils/code-structure.js +582 -0
- package/dist/utils/code-structure.js.map +1 -0
- package/dist/utils/commit-patterns.d.ts +24 -0
- package/dist/utils/commit-patterns.d.ts.map +1 -0
- package/dist/utils/commit-patterns.js +78 -0
- package/dist/utils/commit-patterns.js.map +1 -0
- package/dist/utils/observability.d.ts +47 -0
- package/dist/utils/observability.d.ts.map +1 -0
- package/dist/utils/observability.js +137 -0
- package/dist/utils/observability.js.map +1 -0
- package/dist/utils/quality.d.ts +32 -0
- package/dist/utils/quality.d.ts.map +1 -0
- package/dist/utils/quality.js +207 -0
- package/dist/utils/quality.js.map +1 -0
- package/dist/utils/semantic-search.d.ts +29 -0
- package/dist/utils/semantic-search.d.ts.map +1 -0
- package/dist/utils/semantic-search.js +167 -0
- package/dist/utils/semantic-search.js.map +1 -0
- package/dist/utils/streaming.d.ts +24 -0
- package/dist/utils/streaming.d.ts.map +1 -0
- package/dist/utils/streaming.js +121 -0
- package/dist/utils/streaming.js.map +1 -0
- package/dist/utils/tag-suggestions.d.ts +18 -0
- package/dist/utils/tag-suggestions.d.ts.map +1 -0
- package/dist/utils/tag-suggestions.js +103 -0
- package/dist/utils/tag-suggestions.js.map +1 -0
- package/dist/utils/team-patterns.d.ts +48 -0
- package/dist/utils/team-patterns.d.ts.map +1 -0
- package/dist/utils/team-patterns.js +413 -0
- package/dist/utils/team-patterns.js.map +1 -0
- package/dist/utils/templates.d.ts +36 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +200 -0
- package/dist/utils/templates.js.map +1 -0
- package/dist/utils/tree-sitter-parser.d.ts +20 -0
- package/dist/utils/tree-sitter-parser.d.ts.map +1 -0
- package/dist/utils/tree-sitter-parser.js +259 -0
- package/dist/utils/tree-sitter-parser.js.map +1 -0
- package/dist/utils/v1.6-patterns.d.ts +117 -0
- package/dist/utils/v1.6-patterns.d.ts.map +1 -0
- package/dist/utils/v1.6-patterns.js +201 -0
- package/dist/utils/v1.6-patterns.js.map +1 -0
- package/dist/utils.d.ts +176 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +822 -0
- package/dist/utils.js.map +1 -0
- package/docs/1000_SCENARIOS_TEST_RESULTS.md +138 -0
- package/docs/1000_UNIQUE_SCENARIOS_TEST.md +171 -0
- package/docs/100_PERCENT_PASS_RATE_VERIFICATION.md +111 -0
- package/docs/5000_SCENARIOS_ISSUE_ANALYSIS.md +96 -0
- package/docs/5000_SCENARIOS_TEST_PLAN.md +281 -0
- package/docs/AGENT_CONTRACT.md +240 -0
- package/docs/AI_RESPONSE_ANALYZER.md +157 -0
- package/docs/AI_RESPONSE_TRACKER.md +923 -0
- package/docs/AI_TESTING_PROMPT.md +307 -0
- package/docs/AI_VALIDATION_PROMPTS.md +366 -0
- package/docs/ALL_AI_ANALYSES_CONSOLIDATED.md +354 -0
- package/docs/ALL_AI_CONSOLIDATION_FINAL.md +372 -0
- package/docs/ALL_AI_TEST_CONSOLIDATION.md +290 -0
- package/docs/ALL_AI_VALIDATION_SYNTHESIS.md +241 -0
- package/docs/BEST_TESTING_SOLUTION.md +227 -0
- package/docs/BLACKBOX_AI_ANALYSIS.md +288 -0
- package/docs/BLACKBOX_AI_CLARIFICATION.md +55 -0
- package/docs/BLACKBOX_AI_STRATEGIC_VALIDATION.md +283 -0
- package/docs/BLACKBOX_AI_VALIDATION_RESPONSE.md +251 -0
- package/docs/BLACKBOX_AI_VALIDATION_RESPONSE_v2.md +402 -0
- package/docs/BLACKBOX_LAUNCH_VALIDATION.md +25 -0
- package/docs/BLACKBOX_SUPERMEMORY_VALIDATION_AND_PLAN.md +50 -0
- package/docs/CAPACITY_AND_ALTERNATIVES_ANALYSIS.md +289 -0
- package/docs/CHATGPT_AI_CLARIFICATION.md +65 -0
- package/docs/CHATGPT_FINAL_VALIDATION.md +348 -0
- package/docs/CHATGPT_IMPLEMENTATION_GUIDE.md +325 -0
- package/docs/CHATGPT_LAUNCH_VALIDATION.md +47 -0
- package/docs/CHATGPT_MEMORY_QUALITY_AND_VSCODE_CHECK.md +43 -0
- package/docs/CHATGPT_SCOPE_REALITY_CHECK.md +35 -0
- package/docs/CHATGPT_STRATEGIC_VALIDATION.md +329 -0
- package/docs/CHATGPT_VALIDATION_RESPONSE.md +332 -0
- package/docs/CHATGPT_VALIDATION_RESPONSE_v2.md +294 -0
- package/docs/CHATGPT_VALIDATION_RESULTS.md +143 -0
- package/docs/CLAUDE_AI_ANALYSIS.md +692 -0
- package/docs/CLAUDE_AI_CLARIFICATION.md +67 -0
- package/docs/CLAUDE_AI_STRATEGIC_VALIDATION.md +578 -0
- package/docs/CLAUDE_AI_VALIDATION_RESPONSE.md +374 -0
- package/docs/CLAUDE_AI_VALIDATION_RESPONSE_v2.md +463 -0
- package/docs/CLAUDE_FINAL_VALIDATION.md +679 -0
- package/docs/CLAUDE_LAUNCH_VALIDATION.md +27 -0
- package/docs/CLAUDE_SUPERMEMORY_LAUNCH_PRIORITIES.md +44 -0
- package/docs/CLAUDE_UNIVERSAL_VISION.md +18 -0
- package/docs/COMPLETE_AI_VALIDATION_SYNTHESIS.md +229 -0
- package/docs/COMPLETE_MEMORY_ANALYSIS_SUMMARY.md +323 -0
- package/docs/COMPLETE_STRATEGIC_LAUNCH_PLAN.md +241 -0
- package/docs/COPILOT_LANGCHAIN_MEMORY_COMPARISON_AND_PLAN.md +43 -0
- package/docs/CRITICAL_FIXES_ACTION_PLAN.md +251 -0
- package/docs/CRITICAL_MEMORY_USAGE_PROMPTS.md +290 -0
- package/docs/CURSOR_AI_MEMORY_ANALYSIS.md +479 -0
- package/docs/CURSOR_AI_MEMORY_WORKFLOW_ANALYSIS.md +267 -0
- package/docs/CURSOR_AI_TEST_RESULTS.md +298 -0
- package/docs/DEEPSEEK_AI_CLARIFICATION.md +52 -0
- package/docs/DEEPSEEK_AI_IMPLEMENTATION_GUIDE.md +398 -0
- package/docs/DEEPSEEK_AI_STRATEGIC_VALIDATION.md +348 -0
- package/docs/DEEPSEEK_AI_VALIDATION_RESPONSE.md +276 -0
- package/docs/DEEPSEEK_AI_VALIDATION_RESPONSE_v2.md +325 -0
- package/docs/DEEPSEEK_FINAL_VALIDATION.md +337 -0
- package/docs/DEEPSEEK_LAUNCH_VALIDATION.md +55 -0
- package/docs/DEEPSEEK_SCOPE_REALITY_CHECK.md +30 -0
- package/docs/DEEPSEEK_SUPERMEMORY_ADOPTION_AND_VSCODE_PIVOT.md +47 -0
- package/docs/DEEPSEEK_VALIDATION_RESULTS.md +165 -0
- package/docs/DEVELOPMENT_TESTING_PROTOCOL.md +378 -0
- package/docs/E2E_TEST_RESULTS.md +102 -0
- package/docs/END_TO_END_MEMORY_ISSUE_ANALYSIS.md +442 -0
- package/docs/FEATURE_1_GIT_SYNC_PLAN.md +228 -0
- package/docs/FEATURE_2_AUTO_LOGGING_PLAN.md +239 -0
- package/docs/FEATURE_3_CODE_SNIPPET_PLAN.md +249 -0
- package/docs/FEATURE_4_TAG_NORMALIZATION_PLAN.md +211 -0
- package/docs/FEATURE_5_WINDOWS_PATH_HANDLING_PLAN.md +199 -0
- package/docs/FEATURE_6_CONFLICT_DETECTION_PLAN.md +126 -0
- package/docs/FEATURE_IMPLEMENTATION_REPORT.md +203 -0
- package/docs/FINAL_COMPLETE_LAUNCH_DECISION.md +255 -0
- package/docs/FINAL_LAUNCH_DECISION.md +235 -0
- package/docs/FINAL_LAUNCH_DECISION_ALL_AIS.md +226 -0
- package/docs/FINAL_SCENARIO_VERIFICATION.md +363 -0
- package/docs/FIX_100_PERCENT_ANALYSIS.md +133 -0
- package/docs/FRAMEWORK_STRUCTURE.md +94 -0
- package/docs/GEMINI_AI_ANALYSIS.md +156 -0
- package/docs/GEMINI_AI_CLARIFICATION.md +47 -0
- package/docs/GEMINI_AI_STRATEGIC_VALIDATION.md +235 -0
- package/docs/GEMINI_AI_VALIDATION_RESPONSE.md +238 -0
- package/docs/GEMINI_AI_VALIDATION_RESPONSE_v2.md +168 -0
- package/docs/GEMINI_FINAL_VALIDATION.md +204 -0
- package/docs/GEMINI_LAUNCH_VALIDATION.md +30 -0
- package/docs/GEMINI_SCOPE_AND_UNIVERSALITY_DEBATE.md +25 -0
- package/docs/GEMINI_SUPERMEMORY_TREE_SITTER_MANDATE.md +43 -0
- package/docs/GEMINI_VALIDATION_RESULTS.md +183 -0
- package/docs/GROK_AI_ANALYSIS.md +278 -0
- package/docs/GROK_AI_CLARIFICATION.md +52 -0
- package/docs/GROK_AI_STRATEGIC_VALIDATION.md +306 -0
- package/docs/GROK_AI_VALIDATION_RESPONSE.md +252 -0
- package/docs/GROK_AI_VALIDATION_RESPONSE_v2.md +264 -0
- package/docs/GROK_FINAL_VALIDATION.md +251 -0
- package/docs/GROK_LAUNCH_VALIDATION.md +24 -0
- package/docs/GROK_SCOPE_REALITY_CHECK.md +28 -0
- package/docs/GROK_SUPERMEMORY_LAUNCH_ANALYSIS.md +44 -0
- package/docs/GROK_VALIDATION_RESULTS.md +180 -0
- package/docs/IMPLEMENTATION_PLAN_16_CRITICAL_FIXES.md +641 -0
- package/docs/LANGCHAIN_AND_LANGGRAPH_INTEGRATION_PLAN.md +51 -0
- package/docs/LAUNCH_DECISION_FINAL.md +243 -0
- package/docs/MANUS_AI_ANALYSIS.md +171 -0
- package/docs/MANUS_AI_CLARIFICATION.md +43 -0
- package/docs/MANUS_AI_VALIDATION_RESPONSE.md +335 -0
- package/docs/MANUS_AI_VALIDATION_RESPONSE_v2.md +226 -0
- package/docs/MANUS_FINAL_VALIDATION.md +257 -0
- package/docs/MANUS_VALIDATION_RESULTS.md +237 -0
- package/docs/MCP_SERVER_SETUP.md +167 -0
- package/docs/MEMORYLINK_7AI_FINAL_CONFIRMATION.md +210 -0
- package/docs/MEMORYLINK_CURSOR_AI_DEVELOPMENT_GUIDE.md +1092 -0
- package/docs/MEMORYLINK_DEVELOPMENT_PLAN_CURSOR_AI.md +629 -0
- package/docs/MEMORYLINK_FINAL_7AI_CLARIFICATION.md +184 -0
- package/docs/MEMORYLINK_MASTER_DOCUMENT_v4.md +1338 -0
- package/docs/MEMORYLINK_NAMING_ANALYSIS.md +427 -0
- package/docs/MEMORYLINK_REAL_WORLD_SCENARIOS.md +3517 -0
- package/docs/MEMORYLINK_STORAGE_COMPARISON.md +498 -0
- package/docs/MEMORYLINK_V1.0_FINAL_IMPLEMENTATION_PLAN.md +285 -0
- package/docs/MEMORYLINK_VALIDATION_COMPLETE_ANALYSIS.md +207 -0
- package/docs/MEMORYLINK_VS_MEMORY_APPS_ANALYSIS.md +667 -0
- package/docs/MEMORYLINK_v1.0_BUILD_DOCUMENT_FINAL.md +1928 -0
- package/docs/MEMORY_USAGE_FIX_IMPLEMENTATION.md +314 -0
- package/docs/MISTRAL_AI_ANALYSIS.md +189 -0
- package/docs/MISTRAL_AI_CLARIFICATION.md +57 -0
- package/docs/MISTRAL_AI_STRATEGIC_VALIDATION.md +334 -0
- package/docs/MISTRAL_AI_TESTING_REQUEST.md +261 -0
- package/docs/MISTRAL_AI_VALIDATION_RESPONSE.md +446 -0
- package/docs/MISTRAL_AI_VALIDATION_RESPONSE_v2.md +227 -0
- package/docs/MISTRAL_FINAL_VALIDATION.md +398 -0
- package/docs/MISTRAL_LAUNCH_VALIDATION.md +32 -0
- package/docs/MISTRAL_SCOPE_REALITY_CHECK.md +32 -0
- package/docs/MISTRAL_SUPERMEMORY_LAUNCH_ANALYSIS.md +43 -0
- package/docs/MISTRAL_VALIDATION_RESULTS.md +371 -0
- package/docs/NEXT_PLAN.md +300 -0
- package/docs/PERPLEXITY_AI_ANALYSIS.md +285 -0
- package/docs/PERPLEXITY_AI_CLARIFICATION.md +57 -0
- package/docs/PERPLEXITY_AI_STRATEGIC_VALIDATION.md +288 -0
- package/docs/PERPLEXITY_AI_VALIDATION_RESPONSE.md +350 -0
- package/docs/PERPLEXITY_AI_VALIDATION_RESPONSE_v2.md +260 -0
- package/docs/PERPLEXITY_FINAL_VALIDATION.md +320 -0
- package/docs/PERPLEXITY_LAUNCH_VALIDATION.md +42 -0
- package/docs/PERPLEXITY_MEMORY_QUALITY_AND_VSCODE_PLAN.md +56 -0
- package/docs/PERPLEXITY_SCOPE_REALITY_CHECK.md +31 -0
- package/docs/PERPLEXITY_VALIDATION_RESULTS.md +154 -0
- package/docs/PRE_LAUNCH_GAP_ANALYSIS.md +663 -0
- package/docs/PROJECT_STRUCTURE_PLAN.md +104 -0
- package/docs/QWEN_AI_ANALYSIS.md +176 -0
- package/docs/QWEN_AI_CLARIFICATION.md +60 -0
- package/docs/QWEN_AI_STRATEGIC_VALIDATION.md +241 -0
- package/docs/QWEN_AI_VALIDATION_RESPONSE.md +197 -0
- package/docs/QWEN_AI_VALIDATION_RESPONSE_v2.md +186 -0
- package/docs/QWEN_FINAL_VALIDATION.md +284 -0
- package/docs/QWEN_LAUNCH_VALIDATION.md +26 -0
- package/docs/QWEN_SCENARIOS_TEST_RESULTS.md +244 -0
- package/docs/QWEN_SCOPE_REALITY_CHECK.md +26 -0
- package/docs/QWEN_SUPERMEMORY_LAUNCH_AND_ENFORCEMENT_PLAN.md +56 -0
- package/docs/QWEN_VALIDATION_RESULTS.md +185 -0
- package/docs/README.md +479 -0
- package/docs/REAL_PRODUCT_LAUNCH_DECISION.md +185 -0
- package/docs/RECIPES.md +424 -0
- package/docs/RELEASE_NOTES_v1.0.0.md +193 -0
- package/docs/SCENARIO_INVENTORY_AND_VERIFICATION.md +284 -0
- package/docs/SINGLE_RUN_1018_SCENARIOS_RESULTS.md +142 -0
- package/docs/TESTING.md +256 -0
- package/docs/TESTING_STRATEGY.md +194 -0
- package/docs/TROUBLESHOOTING.md +188 -0
- package/docs/ULTIMATE_LAUNCH_DECISION.md +246 -0
- package/docs/WHAT_WE_BUILT.md +504 -0
- package/docs/v1.0_LAUNCH_CHECKLIST.md +104 -0
- package/examples/README.md +199 -0
- package/examples/chatgpt-context.js +161 -0
- package/examples/ci-integration.js +288 -0
- package/examples/sync-from-cursor.js +196 -0
- package/extensions/vscode/README.md +25 -0
- package/extensions/vscode/out/buddy-check.js +208 -0
- package/extensions/vscode/out/buddy-check.js.map +1 -0
- package/extensions/vscode/out/extension.js +413 -0
- package/extensions/vscode/out/extension.js.map +1 -0
- package/extensions/vscode/out/sidebar.js +409 -0
- package/extensions/vscode/out/sidebar.js.map +1 -0
- package/extensions/vscode/package.json +92 -0
- package/extensions/vscode/src/buddy-check.ts +220 -0
- package/extensions/vscode/src/extension.ts +425 -0
- package/extensions/vscode/src/shims-vscode.d.ts +2 -0
- package/extensions/vscode/src/sidebar.ts +431 -0
- package/extensions/vscode/tsconfig.json +14 -0
- package/k6-load-test.js +86 -0
- package/package.json +68 -0
- package/run-professional-tests.sh +72 -0
- package/scripts/monitor-continuous-test.sh +17 -0
- package/scripts/reorganize-project.sh +164 -0
- package/scripts/run-tests-parallel.sh +111 -0
- package/scripts/run-tests.sh +30 -0
- package/scripts/setup-framework.sh +139 -0
- package/scripts/setup-testing.sh +96 -0
- package/scripts/stress-test/README.md +86 -0
- package/scripts/stress-test/create-all-scenarios.sh +17 -0
- package/scripts/stress-test/create-remaining-scenarios.sh +3 -0
- package/scripts/stress-test/dev-test.sh +21 -0
- package/scripts/stress-test/monitor-continuous.sh +149 -0
- package/scripts/stress-test/overnight-test.sh +30 -0
- package/scripts/stress-test/quick-test.sh +21 -0
- package/scripts/stress-test/run-all-tests.sh +157 -0
- package/scripts/stress-test/run-continuous.sh +300 -0
- package/scripts/stress-test/run-stress-test.sh +153 -0
- package/scripts/stress-test/set1/1_1_mass_refactoring.sh +117 -0
- package/scripts/stress-test/set1/1_1_mass_refactoring_simple.sh +117 -0
- package/scripts/stress-test/set1/1_2_function_rename.sh +95 -0
- package/scripts/stress-test/set1/1_3_feature_flags.sh +93 -0
- package/scripts/stress-test/set1/1_4_feature_removal.sh +57 -0
- package/scripts/stress-test/set1/1_5_schema_changes.sh +42 -0
- package/scripts/stress-test/set1/1_6_dependency_update.sh +47 -0
- package/scripts/stress-test/set1/1_7_config_modification.sh +53 -0
- package/scripts/stress-test/set2/2_1_payment_logging.sh +49 -0
- package/scripts/stress-test/set2/2_2_test_data_generation.sh +43 -0
- package/scripts/stress-test/set2/2_3_documentation_leak.sh +45 -0
- package/scripts/stress-test/set2/2_4_api_key_rotation.sh +45 -0
- package/scripts/stress-test/set2/2_5_hardcoded_secrets.sh +45 -0
- package/scripts/stress-test/set2/2_6_debug_output.sh +49 -0
- package/scripts/stress-test/set3/3_1_billing_modification.sh +47 -0
- package/scripts/stress-test/set3/3_2_migration_deletion.sh +43 -0
- package/scripts/stress-test/set3/3_3_auth_middleware.sh +52 -0
- package/scripts/stress-test/set3/3_4_permission_bypass.sh +48 -0
- package/scripts/stress-test/set3/3_5_config_modification.sh +43 -0
- package/scripts/stress-test/set3/3_6_core_library.sh +51 -0
- package/scripts/stress-test/set3/3_7_test_infrastructure.sh +49 -0
- package/scripts/stress-test/set4/4_1_concurrent_features.sh +49 -0
- package/scripts/stress-test/set4/4_2_lock_acquisition.sh +32 -0
- package/scripts/stress-test/set4/4_3_migration_hotfix.sh +43 -0
- package/scripts/stress-test/set4/4_4_overlapping_scopes.sh +50 -0
- package/scripts/stress-test/set4/4_5_lock_timeout.sh +34 -0
- package/scripts/stress-test/set4/4_6_concurrent_stats.sh +33 -0
- package/scripts/stress-test/set5/5_1_wrong_decision.sh +41 -0
- package/scripts/stress-test/set5/5_2_outdated_docs.sh +40 -0
- package/scripts/stress-test/set5/5_3_conflicting_memories.sh +34 -0
- package/scripts/stress-test/set5/5_4_deleted_file_references.sh +38 -0
- package/scripts/stress-test/set5/5_5_old_pattern.sh +41 -0
- package/scripts/stress-test/set5/5_6_wrong_architecture.sh +42 -0
- package/scripts/stress-test/set5/5_7_high_trust_stale.sh +46 -0
- package/scripts/stress-test/set5/5_8_observability_stale.sh +36 -0
- package/scripts/stress-test/setup-test-repo-simple.sh +144 -0
- package/scripts/stress-test/setup-test-repo.sh +154 -0
- package/scripts/stress-test/start-continuous.sh +48 -0
- package/scripts/stress-test/stop-continuous.sh +42 -0
- package/scripts/stress-test/template-scenario.sh +115 -0
- package/scripts/test-advanced-scenarios.sh +411 -0
- package/scripts/test-continuous-30min.sh +307 -0
- package/scripts/test-continuous-enhanced.sh +250 -0
- package/scripts/test-e2e-comprehensive.sh +114 -0
- package/scripts/test-e2e-random.sh +359 -0
- package/scripts/test-fresh-scenarios.sh +412 -0
- package/scripts/test-new-scenarios.sh +374 -0
- package/scripts/test-quick-random.sh +97 -0
- package/scripts/test-runtime.sh +129 -0
- package/scripts/test-telemetry-local.sh +193 -0
- package/scripts/test-ultimate-scenarios.sh +428 -0
- package/scripts/test-v1.5-complete.sh +225 -0
- package/scripts/test-v1.5-phase1.sh +222 -0
- package/src/cli.ts +1259 -0
- package/src/commands/archive.ts +252 -0
- package/src/commands/auto-context.ts +159 -0
- package/src/commands/auto-log.ts +531 -0
- package/src/commands/change.ts +298 -0
- package/src/commands/checkpoint.ts +390 -0
- package/src/commands/configure.ts +297 -0
- package/src/commands/consolidate.ts +263 -0
- package/src/commands/context.ts +618 -0
- package/src/commands/detect.ts +181 -0
- package/src/commands/doctor.ts +1468 -0
- package/src/commands/export.ts +77 -0
- package/src/commands/graph.ts +214 -0
- package/src/commands/hooks.ts +245 -0
- package/src/commands/impact.ts +163 -0
- package/src/commands/index-vector.ts +126 -0
- package/src/commands/index.ts +57 -0
- package/src/commands/init.ts +194 -0
- package/src/commands/inject.ts +440 -0
- package/src/commands/learn.ts +328 -0
- package/src/commands/lock.ts +345 -0
- package/src/commands/memory.ts +415 -0
- package/src/commands/migrate.ts +540 -0
- package/src/commands/patterns.ts +158 -0
- package/src/commands/protect.ts +199 -0
- package/src/commands/quickstart.ts +259 -0
- package/src/commands/resolve.ts +373 -0
- package/src/commands/roadmap.ts +25 -0
- package/src/commands/scopes.ts +113 -0
- package/src/commands/search.ts +365 -0
- package/src/commands/setup.ts +430 -0
- package/src/commands/snippet.ts +271 -0
- package/src/commands/stats.ts +591 -0
- package/src/commands/status.ts +127 -0
- package/src/commands/suggest-tags.ts +122 -0
- package/src/commands/sync-rules.ts +218 -0
- package/src/commands/sync.ts +363 -0
- package/src/commands/telemetry-test.ts +97 -0
- package/src/commands/template.ts +166 -0
- package/src/commands/validate.ts +191 -0
- package/src/commands/watch-preferences.ts +162 -0
- package/src/commands/watch.ts +239 -0
- package/src/config/thresholds.ts +14 -0
- package/src/index.ts +12 -0
- package/src/memorylink.ts +308 -0
- package/src/search/local-embeddings.ts +94 -0
- package/src/search/vector-search.ts +608 -0
- package/src/server/mcp-server.ts +355 -0
- package/src/telemetry.ts +391 -0
- package/src/test-runner/TestRunner.ts +421 -0
- package/src/test-runner/performance-test.ts +161 -0
- package/src/test-runner/run-tests.ts +152 -0
- package/src/types.ts +533 -0
- package/src/utils/batch-commits.ts +162 -0
- package/src/utils/code-structure.ts +686 -0
- package/src/utils/commit-patterns.ts +87 -0
- package/src/utils/observability.ts +149 -0
- package/src/utils/quality.ts +230 -0
- package/src/utils/semantic-search.ts +222 -0
- package/src/utils/streaming.ts +109 -0
- package/src/utils/tag-suggestions.ts +117 -0
- package/src/utils/team-patterns.ts +499 -0
- package/src/utils/templates.ts +181 -0
- package/src/utils/tree-sitter-parser.ts +246 -0
- package/src/utils/v1.6-patterns.ts +227 -0
- package/src/utils.ts +885 -0
- package/test-all-features.sh +102 -0
- package/test-all-implemented-features.sh +209 -0
- package/test-all-new-features.sh +171 -0
- package/test-auto-log.txt +1 -0
- package/test-batch-commits.sh +47 -0
- package/test-conflict-resolution.sh +47 -0
- package/test-e2e.sh +22 -0
- package/test-end-to-end.sh +151 -0
- package/test-enhanced-autocapture.sh +164 -0
- package/test-inject.sh +44 -0
- package/test-mcp-server.sh +67 -0
- package/test-pagination.sh +37 -0
- package/test-python-go-structure.sh +164 -0
- package/test-quality-validation.sh +167 -0
- package/test-results-quick-smoke.json +13 -0
- package/test-results-targeted-perf.json +23 -0
- package/test-results.json +2272 -0
- package/test-scenarios/payment-logging.ts +17 -0
- package/test-scenarios/test-config.ts +13 -0
- package/test-semantic-search.sh +161 -0
- package/test-tag-intelligence.sh +49 -0
- package/test-vector-search.sh +64 -0
- package/test-vscode-extension.sh +144 -0
- package/test-watcher-file.txt +2 -0
- package/test-watcher-file2.txt +1 -0
- package/test-watcher.sh +103 -0
- package/test_qwen_scenarios.sh +285 -0
- package/tests/scenarios/4000_HARD_SCENARIOS.sh +4137 -0
- package/tests/scenarios/ADD_V1.1_SCENARIOS.sh +93 -0
- package/tests/scenarios/AGGRESSIVE_RANDOM_E2E_TEST.sh +474 -0
- package/tests/scenarios/COMPLETE_PRODUCT_VALIDATION.sh +227 -0
- package/tests/scenarios/COMPREHENSIVE_E2E_TEST.sh +426 -0
- package/tests/scenarios/CONTINUOUS_RANDOM_STRESS_TEST.sh +240 -0
- package/tests/scenarios/EXECUTE_10000_SCENARIOS.sh +61 -0
- package/tests/scenarios/EXECUTE_1000_UNIQUE_SCENARIOS.sh +190 -0
- package/tests/scenarios/EXECUTE_5000_SCENARIOS_SPLIT.sh +192 -0
- package/tests/scenarios/EXECUTE_5000_TOTAL_SCENARIOS.sh +162 -0
- package/tests/scenarios/EXECUTE_5040_SCENARIOS_WITH_V1.1.sh +251 -0
- package/tests/scenarios/EXECUTE_8_BATCHES_500.sh +51 -0
- package/tests/scenarios/EXECUTE_QUICK_SMOKE.sh +9 -0
- package/tests/scenarios/EXECUTE_SINGLE_BATCH.sh +117 -0
- package/tests/scenarios/EXECUTE_TARGETED_PERF.sh +19 -0
- package/tests/scenarios/GENERATE_1000_SCENARIOS.sh +235 -0
- package/tests/scenarios/GENERATE_4000_HARD_SCENARIOS.sh +266 -0
- package/tests/scenarios/GENERATE_4000_HARD_SCENARIOS_FIXED.sh +267 -0
- package/tests/scenarios/GENERATE_4000_HARD_SCENARIOS_FIXED_V2.sh +267 -0
- package/tests/scenarios/NEW_RANDOM_E2E_TEST.sh +422 -0
- package/tests/scenarios/QUICK_SMOKE_200.sh +38 -0
- package/tests/scenarios/QUICK_SMOKE_MINI.sh +3 -0
- package/tests/scenarios/RANDOM_REAL_WORLD_SCENARIOS.sh +372 -0
- package/tests/scenarios/ROUND3_RANDOM_E2E_TEST.sh +446 -0
- package/tests/scenarios/RUN_AGGRESSIVE_AND_SUMMARY.sh +51 -0
- package/tests/scenarios/RUN_ALL_1018_SCENARIOS.sh +161 -0
- package/tests/scenarios/TARGETED_PERF.sh +75 -0
- package/tests/scenarios/V1.1_FEATURES_SCENARIOS.sh +145 -0
- package/tests/unit/utils.test.ts +52 -0
- package/tests/v1.1-features-scenarios.sh +276 -0
- package/tsconfig.json +21 -0
- package/v1.6_FEATURE_REQUESTS.md +79 -0
|
@@ -0,0 +1,1468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryLink - Doctor Command
|
|
3
|
+
* System health check and troubleshooting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import {
|
|
10
|
+
isInitialized,
|
|
11
|
+
success,
|
|
12
|
+
error,
|
|
13
|
+
warn,
|
|
14
|
+
info,
|
|
15
|
+
log,
|
|
16
|
+
findRoot,
|
|
17
|
+
readConfig,
|
|
18
|
+
getFilePath,
|
|
19
|
+
readJsonlEntries,
|
|
20
|
+
isJsonlFormat,
|
|
21
|
+
safeLog,
|
|
22
|
+
safeError,
|
|
23
|
+
isStdoutWritable,
|
|
24
|
+
} from '../utils.js';
|
|
25
|
+
import { loadGraph } from '../utils/code-structure.js';
|
|
26
|
+
import { MEMORYLINK_DIR, FILES } from '../types.js';
|
|
27
|
+
import { logBuddyCheckRun } from '../utils/observability.js';
|
|
28
|
+
import { checkLock } from './lock.js';
|
|
29
|
+
import type {
|
|
30
|
+
GlobalOptions,
|
|
31
|
+
DoctorCheck,
|
|
32
|
+
MemoryEntry,
|
|
33
|
+
LearningEntry,
|
|
34
|
+
BuddyStatus,
|
|
35
|
+
BuddyIssue,
|
|
36
|
+
BuddyReport,
|
|
37
|
+
BuddyOverride,
|
|
38
|
+
MemoryStatus,
|
|
39
|
+
Sensitivity,
|
|
40
|
+
TrustLevel,
|
|
41
|
+
SourceType,
|
|
42
|
+
} from '../types.js';
|
|
43
|
+
|
|
44
|
+
// Secret patterns to detect (HIGH PRIORITY - All 7 AIs require this)
|
|
45
|
+
const SECRET_PATTERNS = [
|
|
46
|
+
// API Keys
|
|
47
|
+
{ name: 'OpenAI API Key', pattern: /sk-[a-zA-Z0-9]{20,}/g },
|
|
48
|
+
{ name: 'Anthropic API Key', pattern: /sk-ant-[a-zA-Z0-9-]{20,}/g },
|
|
49
|
+
{ name: 'Stripe Secret Key', pattern: /sk_live_[a-zA-Z0-9]{20,}/g },
|
|
50
|
+
{ name: 'Stripe Test Key', pattern: /sk_test_[a-zA-Z0-9]{20,}/g },
|
|
51
|
+
{ name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g },
|
|
52
|
+
{ name: 'AWS Secret Key', pattern: /[a-zA-Z0-9/+=]{40}/g },
|
|
53
|
+
{ name: 'GitHub Token', pattern: /ghp_[a-zA-Z0-9]{36}/g },
|
|
54
|
+
{ name: 'GitHub Token (old)', pattern: /github_pat_[a-zA-Z0-9_]{22,}/g },
|
|
55
|
+
{ name: 'Google API Key', pattern: /AIza[0-9A-Za-z-_]{35}/g },
|
|
56
|
+
{ name: 'Slack Token', pattern: /xox[baprs]-[0-9]{10,13}-[a-zA-Z0-9-]+/g },
|
|
57
|
+
{ name: 'Discord Token', pattern: /[MN][A-Za-z\d]{23,}\.[\w-]{6}\.[\w-]{27}/g },
|
|
58
|
+
|
|
59
|
+
// Generic patterns
|
|
60
|
+
{ name: 'Generic API Key', pattern: /api[_-]?key["\s]*[:=]["\s]*[a-zA-Z0-9_-]{16,}/gi },
|
|
61
|
+
{ name: 'Generic Secret', pattern: /secret["\s]*[:=]["\s]*[a-zA-Z0-9_-]{16,}/gi },
|
|
62
|
+
{ name: 'Generic Token', pattern: /token["\s]*[:=]["\s]*[a-zA-Z0-9_-]{16,}/gi },
|
|
63
|
+
{ name: 'Password', pattern: /password["\s]*[:=]["\s]*[^\s"]{8,}/gi },
|
|
64
|
+
{ name: 'Private Key', pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g },
|
|
65
|
+
{ name: 'Bearer Token', pattern: /bearer\s+[a-zA-Z0-9_-]{20,}/gi },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
interface SecretMatch {
|
|
69
|
+
file: string;
|
|
70
|
+
line: number;
|
|
71
|
+
pattern: string;
|
|
72
|
+
preview: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface Conflict {
|
|
76
|
+
type: 'duplicate' | 'conflict';
|
|
77
|
+
entries: string[];
|
|
78
|
+
message: string;
|
|
79
|
+
suggestion?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Detect Git merge conflict markers in log files
|
|
84
|
+
*/
|
|
85
|
+
function detectGitConflicts(root: string): { file: string; line: number }[] {
|
|
86
|
+
const conflicts: { file: string; line: number }[] = [];
|
|
87
|
+
const filesToScan = ['MEMORY', 'LEARNINGS', 'CHANGES'] as const;
|
|
88
|
+
|
|
89
|
+
for (const fileKey of filesToScan) {
|
|
90
|
+
const filePath = getFilePath(fileKey, root);
|
|
91
|
+
if (!fs.existsSync(filePath)) continue;
|
|
92
|
+
|
|
93
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
94
|
+
const lines = content.split('\n');
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < lines.length; i++) {
|
|
97
|
+
const line = lines[i];
|
|
98
|
+
// Check for Git merge conflict markers
|
|
99
|
+
if (line.trim().startsWith('<<<<<<<') ||
|
|
100
|
+
line.trim().startsWith('=======') ||
|
|
101
|
+
line.trim().startsWith('>>>>>>>')) {
|
|
102
|
+
conflicts.push({
|
|
103
|
+
file: FILES[fileKey],
|
|
104
|
+
line: i + 1,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return conflicts;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function scanForSecrets(root: string): SecretMatch[] {
|
|
114
|
+
const matches: SecretMatch[] = [];
|
|
115
|
+
const filesToScan = ['MEMORY', 'LEARNINGS', 'CHANGES'] as const;
|
|
116
|
+
|
|
117
|
+
for (const fileKey of filesToScan) {
|
|
118
|
+
const filePath = getFilePath(fileKey, root);
|
|
119
|
+
if (!fs.existsSync(filePath)) continue;
|
|
120
|
+
|
|
121
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
122
|
+
const lines = content.split('\n');
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
const line = lines[i];
|
|
126
|
+
|
|
127
|
+
for (const secretPattern of SECRET_PATTERNS) {
|
|
128
|
+
const regex = new RegExp(secretPattern.pattern);
|
|
129
|
+
if (regex.test(line)) {
|
|
130
|
+
// Create a safe preview (mask most of the secret)
|
|
131
|
+
const preview = line.length > 60
|
|
132
|
+
? line.substring(0, 30) + '...' + line.substring(line.length - 10)
|
|
133
|
+
: line;
|
|
134
|
+
|
|
135
|
+
matches.push({
|
|
136
|
+
file: FILES[fileKey],
|
|
137
|
+
line: i + 1,
|
|
138
|
+
pattern: secretPattern.name,
|
|
139
|
+
preview: preview.replace(/([a-zA-Z0-9_-]{8})[a-zA-Z0-9_-]{8,}([a-zA-Z0-9_-]{4})/g, '$1****$2'),
|
|
140
|
+
});
|
|
141
|
+
break; // Only report once per line
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return matches;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Normalize text for comparison (lowercase, trim, remove extra spaces)
|
|
152
|
+
*/
|
|
153
|
+
function normalizeText(text: string): string {
|
|
154
|
+
return text.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Detect duplicate and conflicting memories
|
|
159
|
+
*/
|
|
160
|
+
function detectMemoryConflicts(root: string): Conflict[] {
|
|
161
|
+
const conflicts: Conflict[] = [];
|
|
162
|
+
const memoryPath = getFilePath('MEMORY', root);
|
|
163
|
+
|
|
164
|
+
if (!fs.existsSync(memoryPath) || !isJsonlFormat(memoryPath)) {
|
|
165
|
+
return conflicts;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const memories = readJsonlEntries<MemoryEntry>(memoryPath)
|
|
169
|
+
.filter(e => e.status === 'ACTIVE');
|
|
170
|
+
|
|
171
|
+
// Group by normalized details (exact duplicates)
|
|
172
|
+
const detailsMap = new Map<string, MemoryEntry[]>();
|
|
173
|
+
for (const mem of memories) {
|
|
174
|
+
const normalized = normalizeText(mem.details);
|
|
175
|
+
if (!detailsMap.has(normalized)) {
|
|
176
|
+
detailsMap.set(normalized, []);
|
|
177
|
+
}
|
|
178
|
+
detailsMap.get(normalized)!.push(mem);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Find duplicates
|
|
182
|
+
for (const [normalized, entries] of detailsMap) {
|
|
183
|
+
if (entries.length > 1) {
|
|
184
|
+
const preview = entries[0].details.length > 60
|
|
185
|
+
? entries[0].details.substring(0, 60) + '...'
|
|
186
|
+
: entries[0].details;
|
|
187
|
+
|
|
188
|
+
conflicts.push({
|
|
189
|
+
type: 'duplicate',
|
|
190
|
+
entries: entries.map(e => e.id),
|
|
191
|
+
message: `Duplicate memory: "${preview}"`,
|
|
192
|
+
suggestion: `Consider deprecating older entries or merging them`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Find conflicting rules/decisions (same topic, contradictory)
|
|
198
|
+
const rules = memories.filter(m => m.type === 'rule' || m.type === 'decision');
|
|
199
|
+
const rulesByTopic = new Map<string, MemoryEntry[]>();
|
|
200
|
+
|
|
201
|
+
for (const rule of rules) {
|
|
202
|
+
// Extract topic from details (first few words)
|
|
203
|
+
const topic = normalizeText(rule.details).split(' ').slice(0, 3).join(' ');
|
|
204
|
+
if (!rulesByTopic.has(topic)) {
|
|
205
|
+
rulesByTopic.set(topic, []);
|
|
206
|
+
}
|
|
207
|
+
rulesByTopic.get(topic)!.push(rule);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check for potential conflicts (same topic, different details)
|
|
211
|
+
for (const [topic, entries] of rulesByTopic) {
|
|
212
|
+
if (entries.length > 1) {
|
|
213
|
+
const uniqueDetails = new Set(entries.map(e => normalizeText(e.details)));
|
|
214
|
+
if (uniqueDetails.size > 1) {
|
|
215
|
+
conflicts.push({
|
|
216
|
+
type: 'conflict',
|
|
217
|
+
entries: entries.map(e => e.id),
|
|
218
|
+
message: `Conflicting ${entries[0].type}s about: "${topic}..."`,
|
|
219
|
+
suggestion: `Review and resolve contradictions`,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return conflicts;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Detect duplicate learnings
|
|
230
|
+
*/
|
|
231
|
+
function detectLearningConflicts(root: string): Conflict[] {
|
|
232
|
+
const conflicts: Conflict[] = [];
|
|
233
|
+
const learningsPath = getFilePath('LEARNINGS', root);
|
|
234
|
+
|
|
235
|
+
if (!fs.existsSync(learningsPath) || !isJsonlFormat(learningsPath)) {
|
|
236
|
+
return conflicts;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const learnings = readJsonlEntries<LearningEntry>(learningsPath)
|
|
240
|
+
.filter(e => e.status === 'ACTIVE');
|
|
241
|
+
|
|
242
|
+
// Group by normalized problem + solution (exact duplicates)
|
|
243
|
+
const problemSolutionMap = new Map<string, LearningEntry[]>();
|
|
244
|
+
for (const learning of learnings) {
|
|
245
|
+
const key = `${normalizeText(learning.problem)}|${normalizeText(learning.solution)}`;
|
|
246
|
+
if (!problemSolutionMap.has(key)) {
|
|
247
|
+
problemSolutionMap.set(key, []);
|
|
248
|
+
}
|
|
249
|
+
problemSolutionMap.get(key)!.push(learning);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Find duplicates
|
|
253
|
+
for (const [key, entries] of problemSolutionMap) {
|
|
254
|
+
if (entries.length > 1) {
|
|
255
|
+
const preview = entries[0].problem.length > 50
|
|
256
|
+
? entries[0].problem.substring(0, 50) + '...'
|
|
257
|
+
: entries[0].problem;
|
|
258
|
+
|
|
259
|
+
conflicts.push({
|
|
260
|
+
type: 'duplicate',
|
|
261
|
+
entries: entries.map(e => e.id),
|
|
262
|
+
message: `Duplicate learning: "${preview}"`,
|
|
263
|
+
suggestion: `Keep the most recent entry, deprecate others`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return conflicts;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface DoctorOptions extends GlobalOptions {
|
|
272
|
+
scanSecrets?: boolean;
|
|
273
|
+
autoFix?: boolean; // Auto-fix issues where possible
|
|
274
|
+
preflight?: boolean; // v1.4: Run preflight check
|
|
275
|
+
postflight?: boolean; // v1.4: Run postflight check
|
|
276
|
+
changed?: boolean; // v1.4: Only check changed files (for postflight)
|
|
277
|
+
fix?: boolean; // v1.4: Auto-fix memory records (for postflight)
|
|
278
|
+
json?: boolean; // v1.4: Output as JSON
|
|
279
|
+
for?: string; // v1.4: Agent/user ID for preflight
|
|
280
|
+
pr?: string; // v1.4: PR number for preflight
|
|
281
|
+
branch?: string; // v1.4: Branch name for preflight
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function doctorCommand(options: DoctorOptions = {}): Promise<void> {
|
|
285
|
+
const {
|
|
286
|
+
verbose,
|
|
287
|
+
scanSecrets,
|
|
288
|
+
autoFix = false,
|
|
289
|
+
preflight = false,
|
|
290
|
+
postflight = false,
|
|
291
|
+
changed = false,
|
|
292
|
+
fix = false,
|
|
293
|
+
json = false,
|
|
294
|
+
for: agentId,
|
|
295
|
+
pr: prNumber,
|
|
296
|
+
branch: branchName,
|
|
297
|
+
} = options;
|
|
298
|
+
|
|
299
|
+
// TODO v1.6: Record telemetry event (command start)
|
|
300
|
+
// await recordEvent({
|
|
301
|
+
// command: 'doctor',
|
|
302
|
+
// result: 'success', // Will be updated to 'error' if command fails
|
|
303
|
+
// flags: { preflight, postflight, json, scanSecrets },
|
|
304
|
+
// isGitRepo: checkIsGitRepo(),
|
|
305
|
+
// hasConfig: checkHasConfig(),
|
|
306
|
+
// });
|
|
307
|
+
|
|
308
|
+
// v1.4: Handle preflight/postflight
|
|
309
|
+
if (preflight) {
|
|
310
|
+
const root = findRoot();
|
|
311
|
+
let report = await runPreflightCheck(options);
|
|
312
|
+
|
|
313
|
+
// Handle override if RED status
|
|
314
|
+
if (report.status === 'RED' && !json) {
|
|
315
|
+
report = await handleOverride(report, root, options);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Log to observability
|
|
319
|
+
if (root) {
|
|
320
|
+
logBuddyCheckRun(report, root);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (json) {
|
|
324
|
+
// For JSON output, use safeLog to handle EPIPE
|
|
325
|
+
safeLog(JSON.stringify(report, null, 2));
|
|
326
|
+
} else {
|
|
327
|
+
printBuddyReport(report);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Write to file
|
|
331
|
+
if (root) {
|
|
332
|
+
const reportPath = path.join(root, MEMORYLINK_DIR, 'preflight.json');
|
|
333
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
334
|
+
if (!json) {
|
|
335
|
+
info(`Report saved to: ${reportPath}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Log override to overrides.log if present
|
|
339
|
+
if (report.override) {
|
|
340
|
+
logOverride(report.override, 'preflight', root);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Exit with error code only if RED and not overridden
|
|
345
|
+
if (report.status === 'RED' && !report.override) {
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (postflight) {
|
|
352
|
+
const root = findRoot();
|
|
353
|
+
let report = await runPostflightCheck(options);
|
|
354
|
+
|
|
355
|
+
// Handle override if RED status
|
|
356
|
+
if (report.status === 'RED' && !json) {
|
|
357
|
+
report = await handleOverride(report, root, options);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Log to observability
|
|
361
|
+
if (root) {
|
|
362
|
+
logBuddyCheckRun(report, root);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (json) {
|
|
366
|
+
// For JSON output, use safeLog to handle EPIPE
|
|
367
|
+
safeLog(JSON.stringify(report, null, 2));
|
|
368
|
+
} else {
|
|
369
|
+
printBuddyReport(report);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Write to file
|
|
373
|
+
if (root) {
|
|
374
|
+
const reportPath = path.join(root, MEMORYLINK_DIR, 'postflight.json');
|
|
375
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
376
|
+
if (!json) {
|
|
377
|
+
info(`Report saved to: ${reportPath}`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Log override to overrides.log if present
|
|
381
|
+
if (report.override) {
|
|
382
|
+
logOverride(report.override, 'postflight', root);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Exit with error code only if RED and not overridden
|
|
387
|
+
if (report.status === 'RED' && !report.override) {
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Original doctor command (v1.3)
|
|
394
|
+
log('Running health checks', verbose);
|
|
395
|
+
|
|
396
|
+
console.log('');
|
|
397
|
+
console.log('\x1b[1m🩺 MemoryLink Doctor\x1b[0m');
|
|
398
|
+
console.log('');
|
|
399
|
+
|
|
400
|
+
const checks: DoctorCheck[] = [];
|
|
401
|
+
|
|
402
|
+
// Check 1: MemoryLink initialized
|
|
403
|
+
const root = findRoot();
|
|
404
|
+
const initialized = root && isInitialized(root);
|
|
405
|
+
|
|
406
|
+
checks.push({
|
|
407
|
+
name: 'MemoryLink initialized',
|
|
408
|
+
status: initialized ? 'pass' : 'fail',
|
|
409
|
+
message: initialized
|
|
410
|
+
? `Found .memorylink/ in ${root}`
|
|
411
|
+
: 'Run: memorylink init',
|
|
412
|
+
suggestion: initialized ? undefined : 'Run: memorylink init',
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
if (!initialized) {
|
|
416
|
+
printChecks(checks);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check 2: All required files exist
|
|
421
|
+
const requiredFiles = ['PROTECTED', 'MEMORY', 'CHANGES', 'LEARNINGS', 'CONFIG'] as const;
|
|
422
|
+
let allFilesExist = true;
|
|
423
|
+
const missingFiles: string[] = [];
|
|
424
|
+
|
|
425
|
+
for (const fileKey of requiredFiles) {
|
|
426
|
+
const filePath = getFilePath(fileKey, root);
|
|
427
|
+
if (!fs.existsSync(filePath)) {
|
|
428
|
+
allFilesExist = false;
|
|
429
|
+
missingFiles.push(FILES[fileKey]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
checks.push({
|
|
434
|
+
name: 'Required files exist',
|
|
435
|
+
status: allFilesExist ? 'pass' : 'fail',
|
|
436
|
+
message: allFilesExist
|
|
437
|
+
? 'All 5 files present'
|
|
438
|
+
: `Missing: ${missingFiles.join(', ')}`,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Check 3: Config file valid
|
|
442
|
+
const config = readConfig(root);
|
|
443
|
+
checks.push({
|
|
444
|
+
name: 'Config file valid',
|
|
445
|
+
status: config ? 'pass' : 'fail',
|
|
446
|
+
message: config
|
|
447
|
+
? `Version ${config.version}, initialized ${new Date(config.initialized).toLocaleDateString()}`
|
|
448
|
+
: 'Config file is corrupted or invalid',
|
|
449
|
+
suggestion: config ? undefined : 'Run: memorylink init (will recreate config)',
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// Check 4: Files are readable/writable
|
|
453
|
+
let filesWritable = true;
|
|
454
|
+
try {
|
|
455
|
+
const testFile = path.join(root!, MEMORYLINK_DIR, '.doctor-test');
|
|
456
|
+
fs.writeFileSync(testFile, 'test');
|
|
457
|
+
fs.unlinkSync(testFile);
|
|
458
|
+
} catch {
|
|
459
|
+
filesWritable = false;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
checks.push({
|
|
463
|
+
name: 'Files are writable',
|
|
464
|
+
status: filesWritable ? 'pass' : 'fail',
|
|
465
|
+
message: filesWritable
|
|
466
|
+
? 'Read/write access confirmed'
|
|
467
|
+
: 'Permission denied - check file permissions',
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Check 5: Git repository
|
|
471
|
+
let isGitRepo = false;
|
|
472
|
+
let gitTracked = false;
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
execSync('git rev-parse --git-dir', { cwd: root!, stdio: 'pipe' });
|
|
476
|
+
isGitRepo = true;
|
|
477
|
+
|
|
478
|
+
// Check if .memorylink is tracked
|
|
479
|
+
try {
|
|
480
|
+
const gitStatus = execSync(`git ls-files ${MEMORYLINK_DIR}`, { cwd: root!, stdio: 'pipe' }).toString();
|
|
481
|
+
gitTracked = gitStatus.trim().length > 0;
|
|
482
|
+
} catch {
|
|
483
|
+
gitTracked = false;
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
isGitRepo = false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
checks.push({
|
|
490
|
+
name: 'Git repository',
|
|
491
|
+
status: isGitRepo ? 'pass' : 'warn',
|
|
492
|
+
message: isGitRepo
|
|
493
|
+
? gitTracked
|
|
494
|
+
? '.memorylink/ is tracked in Git (team sharing enabled)'
|
|
495
|
+
: '.memorylink/ exists but not tracked in Git'
|
|
496
|
+
: 'Not a Git repository - team sharing disabled',
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Check 6: Git hooks
|
|
500
|
+
let hooksInstalled = false;
|
|
501
|
+
if (isGitRepo && root) {
|
|
502
|
+
const preCommitPath = path.join(root, '.git', 'hooks', 'pre-commit');
|
|
503
|
+
if (fs.existsSync(preCommitPath)) {
|
|
504
|
+
const hookContent = fs.readFileSync(preCommitPath, 'utf-8');
|
|
505
|
+
hooksInstalled = hookContent.includes('memorylink');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
checks.push({
|
|
510
|
+
name: 'Git hooks installed',
|
|
511
|
+
status: hooksInstalled ? 'pass' : 'warn',
|
|
512
|
+
message: hooksInstalled
|
|
513
|
+
? 'Pre-commit hook protects files'
|
|
514
|
+
: 'Run: memorylink hooks install',
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Check 7: Node.js version
|
|
518
|
+
const nodeVersion = process.version;
|
|
519
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
520
|
+
|
|
521
|
+
checks.push({
|
|
522
|
+
name: 'Node.js version',
|
|
523
|
+
status: majorVersion >= 18 ? 'pass' : 'warn',
|
|
524
|
+
message: `${nodeVersion} ${majorVersion >= 18 ? '(supported)' : '(recommend v18+)'}`,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// Check 8: Protected files exist
|
|
528
|
+
const protectedPath = getFilePath('PROTECTED', root);
|
|
529
|
+
const protectedFiles = fs.readFileSync(protectedPath, 'utf-8')
|
|
530
|
+
.split('\n')
|
|
531
|
+
.filter(l => l.trim() && !l.startsWith('#'))
|
|
532
|
+
.map(l => l.split('#')[0].trim());
|
|
533
|
+
|
|
534
|
+
let allProtectedExist = true;
|
|
535
|
+
const missingProtected: string[] = [];
|
|
536
|
+
|
|
537
|
+
for (const file of protectedFiles) {
|
|
538
|
+
const fullPath = path.join(root!, file);
|
|
539
|
+
if (!fs.existsSync(fullPath)) {
|
|
540
|
+
allProtectedExist = false;
|
|
541
|
+
missingProtected.push(file);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (protectedFiles.length > 0) {
|
|
546
|
+
checks.push({
|
|
547
|
+
name: 'Protected files exist',
|
|
548
|
+
status: allProtectedExist ? 'pass' : 'warn',
|
|
549
|
+
message: allProtectedExist
|
|
550
|
+
? `All ${protectedFiles.length} protected files found`
|
|
551
|
+
: `Missing: ${missingProtected.slice(0, 3).join(', ')}${missingProtected.length > 3 ? '...' : ''}`,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Check 9: SECRET SCANNING (Critical - All 7 AIs require this)
|
|
556
|
+
const secretMatches = scanForSecrets(root!);
|
|
557
|
+
|
|
558
|
+
checks.push({
|
|
559
|
+
name: 'Secret scanning',
|
|
560
|
+
status: secretMatches.length === 0 ? 'pass' : 'fail',
|
|
561
|
+
message: secretMatches.length === 0
|
|
562
|
+
? 'No potential secrets detected in logs'
|
|
563
|
+
: `⚠️ ${secretMatches.length} potential secret(s) detected!`,
|
|
564
|
+
suggestion: secretMatches.length === 0 ? undefined : 'Review detected secrets and remove them from logs',
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Check 10: Common sensitive files protection
|
|
568
|
+
const sensitiveFiles = ['.env', '.env.local', '.env.production', 'secrets.json', '*.pem', 'id_rsa'];
|
|
569
|
+
const unprotectedSensitive: string[] = [];
|
|
570
|
+
|
|
571
|
+
for (const sensitive of sensitiveFiles) {
|
|
572
|
+
// Check if file exists and is not protected
|
|
573
|
+
if (sensitive.includes('*')) continue; // Skip glob patterns for now
|
|
574
|
+
const sensitivePath = path.join(root!, sensitive);
|
|
575
|
+
if (fs.existsSync(sensitivePath) && !protectedFiles.includes(sensitive)) {
|
|
576
|
+
unprotectedSensitive.push(sensitive);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (unprotectedSensitive.length > 0) {
|
|
581
|
+
checks.push({
|
|
582
|
+
name: 'Sensitive files protected',
|
|
583
|
+
status: 'warn',
|
|
584
|
+
message: `Unprotected: ${unprotectedSensitive.join(', ')} - Run: memorylink protect <file>`,
|
|
585
|
+
suggestion: `Run: memorylink protect ${unprotectedSensitive[0]}`,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Check 11: Git merge conflict detection
|
|
590
|
+
const gitConflicts = detectGitConflicts(root!);
|
|
591
|
+
checks.push({
|
|
592
|
+
name: 'Git merge conflicts',
|
|
593
|
+
status: gitConflicts.length === 0 ? 'pass' : 'fail',
|
|
594
|
+
message: gitConflicts.length === 0
|
|
595
|
+
? 'No Git merge conflicts found'
|
|
596
|
+
: `${gitConflicts.length} Git merge conflict(s) detected in log files`,
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// Check 12: Conflict/duplicate detection
|
|
600
|
+
const memoryConflicts = detectMemoryConflicts(root!);
|
|
601
|
+
const learningConflicts = detectLearningConflicts(root!);
|
|
602
|
+
const allConflicts = [...memoryConflicts, ...learningConflicts];
|
|
603
|
+
|
|
604
|
+
checks.push({
|
|
605
|
+
name: 'Conflict/duplicate detection',
|
|
606
|
+
status: allConflicts.length === 0 ? 'pass' : 'warn',
|
|
607
|
+
message: allConflicts.length === 0
|
|
608
|
+
? 'No duplicates or conflicts found'
|
|
609
|
+
: `${allConflicts.length} conflict(s) detected (see details below)`,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// Check 13: Code structure graph freshness (v1.2)
|
|
613
|
+
const graphFile = path.join(root!, '.memorylink', 'code-graph.json');
|
|
614
|
+
if (fs.existsSync(graphFile)) {
|
|
615
|
+
const stats = fs.statSync(graphFile);
|
|
616
|
+
const age = Date.now() - stats.mtimeMs;
|
|
617
|
+
const oneWeek = 7 * 24 * 60 * 60 * 1000;
|
|
618
|
+
|
|
619
|
+
checks.push({
|
|
620
|
+
name: 'Code structure graph',
|
|
621
|
+
status: age < oneWeek ? 'pass' : 'warn',
|
|
622
|
+
message: age < oneWeek
|
|
623
|
+
? `Graph is ${Math.floor(age / (24 * 60 * 60 * 1000))} day(s) old`
|
|
624
|
+
: 'Graph is older than 7 days - Run: memorylink graph build --rebuild',
|
|
625
|
+
suggestion: age >= oneWeek ? 'Run: memorylink graph build --rebuild' : undefined,
|
|
626
|
+
});
|
|
627
|
+
} else {
|
|
628
|
+
checks.push({
|
|
629
|
+
name: 'Code structure graph',
|
|
630
|
+
status: 'warn',
|
|
631
|
+
message: 'No code structure graph found',
|
|
632
|
+
suggestion: 'Run: memorylink graph build to enable code structure memory',
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
printChecks(checks);
|
|
637
|
+
|
|
638
|
+
// If secrets were found, print detailed report
|
|
639
|
+
if (secretMatches.length > 0) {
|
|
640
|
+
console.log('');
|
|
641
|
+
console.log('\x1b[31m\x1b[1m🚨 SECURITY WARNING: Potential Secrets Detected!\x1b[0m');
|
|
642
|
+
console.log('');
|
|
643
|
+
console.log('\x1b[33mThe following entries may contain sensitive data:\x1b[0m');
|
|
644
|
+
console.log('');
|
|
645
|
+
|
|
646
|
+
for (const match of secretMatches) {
|
|
647
|
+
console.log(` \x1b[31m•\x1b[0m ${match.file}:${match.line}`);
|
|
648
|
+
console.log(` \x1b[90mType: ${match.pattern}\x1b[0m`);
|
|
649
|
+
console.log(` \x1b[90mPreview: ${match.preview}\x1b[0m`);
|
|
650
|
+
console.log('');
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
console.log('\x1b[33m⚠️ ACTION REQUIRED:\x1b[0m');
|
|
654
|
+
console.log(' 1. Remove secrets from MemoryLink logs immediately');
|
|
655
|
+
console.log(' 2. Rotate any exposed credentials');
|
|
656
|
+
console.log(' 3. Use environment variables instead of storing secrets');
|
|
657
|
+
console.log(' 4. Add sensitive files to protected.txt');
|
|
658
|
+
console.log('');
|
|
659
|
+
console.log('\x1b[90mTip: Never store API keys, passwords, or tokens in MemoryLink logs.\x1b[0m');
|
|
660
|
+
console.log('\x1b[90mUse: memorylink protect .env\x1b[0m');
|
|
661
|
+
console.log('');
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Auto-fix suggestions
|
|
665
|
+
const failCount = checks.filter(c => c.status === 'fail').length;
|
|
666
|
+
if (autoFix && failCount > 0) {
|
|
667
|
+
const fixableChecks = checks.filter(c => c.status === 'fail' && c.suggestion);
|
|
668
|
+
if (fixableChecks.length > 0) {
|
|
669
|
+
console.log('');
|
|
670
|
+
info('🔧 Auto-fix suggestions:');
|
|
671
|
+
console.log('');
|
|
672
|
+
for (const check of fixableChecks) {
|
|
673
|
+
if (check.suggestion) {
|
|
674
|
+
console.log(` ${check.name}:`);
|
|
675
|
+
console.log(` → ${check.suggestion}`);
|
|
676
|
+
console.log('');
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// If conflicts were found, print detailed report
|
|
683
|
+
if (allConflicts.length > 0) {
|
|
684
|
+
console.log('');
|
|
685
|
+
console.log('\x1b[33m\x1b[1m⚠️ CONFLICTS & DUPLICATES DETECTED\x1b[0m');
|
|
686
|
+
console.log('');
|
|
687
|
+
console.log('\x1b[33mThe following entries may need attention:\x1b[0m');
|
|
688
|
+
console.log('');
|
|
689
|
+
|
|
690
|
+
for (const conflict of allConflicts) {
|
|
691
|
+
const icon = conflict.type === 'duplicate' ? '📋' : '⚠️';
|
|
692
|
+
const color = conflict.type === 'duplicate' ? '\x1b[33m' : '\x1b[31m';
|
|
693
|
+
console.log(` ${icon} ${color}${conflict.message}\x1b[0m`);
|
|
694
|
+
console.log(` \x1b[90mEntry IDs: ${conflict.entries.slice(0, 3).join(', ')}${conflict.entries.length > 3 ? ` (+${conflict.entries.length - 3} more)` : ''}\x1b[0m`);
|
|
695
|
+
if (conflict.suggestion) {
|
|
696
|
+
console.log(` \x1b[90mSuggestion: ${conflict.suggestion}\x1b[0m`);
|
|
697
|
+
}
|
|
698
|
+
console.log('');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
console.log('\x1b[33m💡 TIPS:\x1b[0m');
|
|
702
|
+
console.log(' • Review duplicate entries and keep the most relevant');
|
|
703
|
+
console.log(' • Resolve conflicting rules/decisions');
|
|
704
|
+
console.log(' • Use memorylink search to find entries by ID');
|
|
705
|
+
console.log(' • Consider deprecating outdated entries');
|
|
706
|
+
console.log('');
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Print all checks
|
|
710
|
+
printChecks(checks);
|
|
711
|
+
|
|
712
|
+
// Summary
|
|
713
|
+
console.log('');
|
|
714
|
+
const passCount = checks.filter(c => c.status === 'pass').length;
|
|
715
|
+
const warnCount = checks.filter(c => c.status === 'warn').length;
|
|
716
|
+
|
|
717
|
+
if (failCount > 0) {
|
|
718
|
+
error(`${failCount} issue(s) need attention`);
|
|
719
|
+
if (!autoFix) {
|
|
720
|
+
info(' Run with --auto-fix to see fix suggestions');
|
|
721
|
+
}
|
|
722
|
+
} else if (warnCount > 0) {
|
|
723
|
+
warn(`${warnCount} warning(s), ${passCount} passed`);
|
|
724
|
+
} else {
|
|
725
|
+
success(`All ${passCount} checks passed! MemoryLink is healthy.`);
|
|
726
|
+
}
|
|
727
|
+
console.log('');
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function printChecks(checks: DoctorCheck[]): void {
|
|
731
|
+
for (const check of checks) {
|
|
732
|
+
let icon: string;
|
|
733
|
+
let color: string;
|
|
734
|
+
|
|
735
|
+
switch (check.status) {
|
|
736
|
+
case 'pass':
|
|
737
|
+
icon = '✓';
|
|
738
|
+
color = '\x1b[32m';
|
|
739
|
+
break;
|
|
740
|
+
case 'warn':
|
|
741
|
+
icon = '⚠';
|
|
742
|
+
color = '\x1b[33m';
|
|
743
|
+
break;
|
|
744
|
+
case 'fail':
|
|
745
|
+
icon = '✗';
|
|
746
|
+
color = '\x1b[31m';
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
console.log(` ${color}${icon}\x1b[0m ${check.name}`);
|
|
751
|
+
console.log(` \x1b[90m${check.message}\x1b[0m`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// ============================================
|
|
756
|
+
// V1.4 BUDDY-CHECK: PREFLIGHT & POSTFLIGHT
|
|
757
|
+
// ============================================
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Run preflight check before AI agent operations
|
|
761
|
+
*/
|
|
762
|
+
async function runPreflightCheck(options: DoctorOptions): Promise<BuddyReport> {
|
|
763
|
+
const startTime = Date.now();
|
|
764
|
+
const root = findRoot();
|
|
765
|
+
|
|
766
|
+
if (!root || !isInitialized(root)) {
|
|
767
|
+
return {
|
|
768
|
+
status: 'RED',
|
|
769
|
+
summary: 'MemoryLink not initialized',
|
|
770
|
+
issues: [{
|
|
771
|
+
type: 'INIT',
|
|
772
|
+
severity: 'error',
|
|
773
|
+
message: 'MemoryLink is not initialized. Run: memorylink init',
|
|
774
|
+
}],
|
|
775
|
+
timestamp: new Date().toISOString(),
|
|
776
|
+
runType: 'preflight',
|
|
777
|
+
durationMs: Date.now() - startTime,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const issues: BuddyIssue[] = [];
|
|
782
|
+
|
|
783
|
+
// Check 0: Agent locking (v1.5)
|
|
784
|
+
const lockStatus = checkLock(root);
|
|
785
|
+
if (lockStatus.locked) {
|
|
786
|
+
issues.push({
|
|
787
|
+
type: 'LOCK',
|
|
788
|
+
severity: 'warning',
|
|
789
|
+
message: `Agent lock active: ${lockStatus.agentId} (expires ${lockStatus.expiresAt?.toLocaleString()})`,
|
|
790
|
+
projectPath: root,
|
|
791
|
+
suggestion: 'Wait for lock to expire or release with: memorylink lock release',
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const memoryEntries = readJsonlEntries<MemoryEntry>(getFilePath('MEMORY', root));
|
|
796
|
+
|
|
797
|
+
// Check 1: Schema validation
|
|
798
|
+
const schemaIssues = validateSchema(memoryEntries, root);
|
|
799
|
+
issues.push(...schemaIssues);
|
|
800
|
+
|
|
801
|
+
// Check 2: Referential integrity
|
|
802
|
+
const refIssues = validateReferentialIntegrity(memoryEntries, root);
|
|
803
|
+
issues.push(...refIssues);
|
|
804
|
+
|
|
805
|
+
// Check 3: Security checks (secrets, PII)
|
|
806
|
+
const securityIssues = validateSecurity(memoryEntries, root);
|
|
807
|
+
issues.push(...securityIssues);
|
|
808
|
+
|
|
809
|
+
// Check 4: Staleness detection
|
|
810
|
+
const stalenessIssues = detectStaleness(memoryEntries, root);
|
|
811
|
+
issues.push(...stalenessIssues);
|
|
812
|
+
|
|
813
|
+
// Check 5: Policy validation
|
|
814
|
+
const policyIssues = validatePolicies(memoryEntries, root);
|
|
815
|
+
issues.push(...policyIssues);
|
|
816
|
+
|
|
817
|
+
// Calculate status
|
|
818
|
+
const status = calculateBuddyStatus(issues);
|
|
819
|
+
|
|
820
|
+
// Count trust levels
|
|
821
|
+
const trustCounts = countTrustLevels(memoryEntries);
|
|
822
|
+
|
|
823
|
+
const duration = Date.now() - startTime;
|
|
824
|
+
|
|
825
|
+
return {
|
|
826
|
+
status,
|
|
827
|
+
summary: generateSummary(status, issues.length, memoryEntries.length),
|
|
828
|
+
issues,
|
|
829
|
+
timestamp: new Date().toISOString(),
|
|
830
|
+
runType: 'preflight',
|
|
831
|
+
agentId: options.for,
|
|
832
|
+
branch: options.branch,
|
|
833
|
+
recordsUsed: memoryEntries.length,
|
|
834
|
+
highTrust: trustCounts.high,
|
|
835
|
+
mediumTrust: trustCounts.medium,
|
|
836
|
+
lowTrust: trustCounts.low,
|
|
837
|
+
staleCount: stalenessIssues.length,
|
|
838
|
+
durationMs: duration,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Run postflight check after AI agent operations
|
|
844
|
+
*/
|
|
845
|
+
async function runPostflightCheck(options: DoctorOptions): Promise<BuddyReport> {
|
|
846
|
+
const startTime = Date.now();
|
|
847
|
+
const root = findRoot();
|
|
848
|
+
|
|
849
|
+
if (!root || !isInitialized(root)) {
|
|
850
|
+
return {
|
|
851
|
+
status: 'RED',
|
|
852
|
+
summary: 'MemoryLink not initialized',
|
|
853
|
+
issues: [{
|
|
854
|
+
type: 'INIT',
|
|
855
|
+
severity: 'error',
|
|
856
|
+
message: 'MemoryLink is not initialized. Run: memorylink init',
|
|
857
|
+
}],
|
|
858
|
+
timestamp: new Date().toISOString(),
|
|
859
|
+
runType: 'postflight',
|
|
860
|
+
durationMs: Date.now() - startTime,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const issues: BuddyIssue[] = [];
|
|
865
|
+
|
|
866
|
+
// Get changed files if --changed flag is set
|
|
867
|
+
// TODO v1.6: Use `git diff` to detect changed files instead of file watching
|
|
868
|
+
// This will enable detection of:
|
|
869
|
+
// - Protected file modifications (via git diff --name-only + protected.txt check)
|
|
870
|
+
// - File deletions (via git diff --diff-filter=D)
|
|
871
|
+
// - Large-scale changes (via git diff --stat line count)
|
|
872
|
+
const changedFiles = options.changed ? getChangedFiles(root) : [];
|
|
873
|
+
|
|
874
|
+
// Read memory entries (filter to changed if needed)
|
|
875
|
+
let memoryEntries = readJsonlEntries<MemoryEntry>(getFilePath('MEMORY', root));
|
|
876
|
+
if (options.changed && changedFiles.length > 0) {
|
|
877
|
+
// Filter entries related to changed files
|
|
878
|
+
memoryEntries = memoryEntries.filter(entry => {
|
|
879
|
+
if (entry.file && changedFiles.includes(entry.file)) return true;
|
|
880
|
+
if (entry.path && changedFiles.includes(entry.path)) return true;
|
|
881
|
+
// Check if entry was created/updated recently (last 5 minutes)
|
|
882
|
+
const entryTime = new Date(entry.ts).getTime();
|
|
883
|
+
const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
|
|
884
|
+
return entryTime > fiveMinutesAgo;
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Run same checks as preflight
|
|
889
|
+
const schemaIssues = validateSchema(memoryEntries, root);
|
|
890
|
+
issues.push(...schemaIssues);
|
|
891
|
+
|
|
892
|
+
const refIssues = validateReferentialIntegrity(memoryEntries, root);
|
|
893
|
+
issues.push(...refIssues);
|
|
894
|
+
|
|
895
|
+
const securityIssues = validateSecurity(memoryEntries, root);
|
|
896
|
+
issues.push(...securityIssues);
|
|
897
|
+
|
|
898
|
+
const stalenessIssues = detectStaleness(memoryEntries, root);
|
|
899
|
+
issues.push(...stalenessIssues);
|
|
900
|
+
|
|
901
|
+
const policyIssues = validatePolicies(memoryEntries, root);
|
|
902
|
+
issues.push(...policyIssues);
|
|
903
|
+
|
|
904
|
+
// Additional postflight checks
|
|
905
|
+
const consistencyIssues = validateConsistency(memoryEntries, root, changedFiles);
|
|
906
|
+
issues.push(...consistencyIssues);
|
|
907
|
+
|
|
908
|
+
// Mark new agent records as DRAFT
|
|
909
|
+
if (options.fix) {
|
|
910
|
+
await markAgentRecordsAsDraft(memoryEntries, root);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Calculate status
|
|
914
|
+
const status = calculateBuddyStatus(issues);
|
|
915
|
+
|
|
916
|
+
const trustCounts = countTrustLevels(memoryEntries);
|
|
917
|
+
const duration = Date.now() - startTime;
|
|
918
|
+
|
|
919
|
+
return {
|
|
920
|
+
status,
|
|
921
|
+
summary: generateSummary(status, issues.length, memoryEntries.length),
|
|
922
|
+
issues,
|
|
923
|
+
timestamp: new Date().toISOString(),
|
|
924
|
+
runType: 'postflight',
|
|
925
|
+
recordsUsed: memoryEntries.length,
|
|
926
|
+
highTrust: trustCounts.high,
|
|
927
|
+
mediumTrust: trustCounts.medium,
|
|
928
|
+
lowTrust: trustCounts.low,
|
|
929
|
+
staleCount: stalenessIssues.length,
|
|
930
|
+
durationMs: duration,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Validate schema and required fields
|
|
936
|
+
*/
|
|
937
|
+
function validateSchema(entries: MemoryEntry[], root: string): BuddyIssue[] {
|
|
938
|
+
const issues: BuddyIssue[] = [];
|
|
939
|
+
|
|
940
|
+
for (const entry of entries) {
|
|
941
|
+
// Check required fields
|
|
942
|
+
if (!entry.id) {
|
|
943
|
+
issues.push({
|
|
944
|
+
id: entry.id,
|
|
945
|
+
type: 'SCHEMA',
|
|
946
|
+
severity: 'error',
|
|
947
|
+
message: 'Missing required field: id',
|
|
948
|
+
projectPath: root,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (!entry.ts) {
|
|
953
|
+
issues.push({
|
|
954
|
+
id: entry.id,
|
|
955
|
+
type: 'SCHEMA',
|
|
956
|
+
severity: 'error',
|
|
957
|
+
message: 'Missing required field: ts (timestamp)',
|
|
958
|
+
projectPath: root,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (!entry.type) {
|
|
963
|
+
issues.push({
|
|
964
|
+
id: entry.id,
|
|
965
|
+
type: 'SCHEMA',
|
|
966
|
+
severity: 'error',
|
|
967
|
+
message: 'Missing required field: type',
|
|
968
|
+
projectPath: root,
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// v1.4: Check schema version if present
|
|
973
|
+
if (entry.schemaVersion && entry.schemaVersion !== '1.4.0' && entry.schemaVersion !== '1.3.0') {
|
|
974
|
+
issues.push({
|
|
975
|
+
id: entry.id,
|
|
976
|
+
type: 'SCHEMA',
|
|
977
|
+
severity: 'warning',
|
|
978
|
+
message: `Unknown schema version: ${entry.schemaVersion}`,
|
|
979
|
+
projectPath: root,
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return issues;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Validate referential integrity (files exist, lines in range, commits exist)
|
|
989
|
+
*/
|
|
990
|
+
function validateReferentialIntegrity(entries: MemoryEntry[], root: string): BuddyIssue[] {
|
|
991
|
+
const issues: BuddyIssue[] = [];
|
|
992
|
+
|
|
993
|
+
for (const entry of entries) {
|
|
994
|
+
// Check file references
|
|
995
|
+
if (entry.file) {
|
|
996
|
+
const filePath = path.join(root, entry.file);
|
|
997
|
+
if (!fs.existsSync(filePath)) {
|
|
998
|
+
issues.push({
|
|
999
|
+
id: entry.id,
|
|
1000
|
+
type: 'BROKEN_REF',
|
|
1001
|
+
severity: 'warning',
|
|
1002
|
+
message: `Referenced file does not exist: ${entry.file}`,
|
|
1003
|
+
projectPath: root,
|
|
1004
|
+
});
|
|
1005
|
+
} else if (entry.lines) {
|
|
1006
|
+
// Check line numbers are in range
|
|
1007
|
+
const lineRange = parseLineRange(entry.lines);
|
|
1008
|
+
if (lineRange) {
|
|
1009
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
1010
|
+
const totalLines = fileContent.split('\n').length;
|
|
1011
|
+
if (lineRange.end > totalLines) {
|
|
1012
|
+
issues.push({
|
|
1013
|
+
id: entry.id,
|
|
1014
|
+
type: 'BROKEN_REF',
|
|
1015
|
+
severity: 'warning',
|
|
1016
|
+
message: `Line range ${entry.lines} exceeds file length (${totalLines} lines)`,
|
|
1017
|
+
projectPath: root,
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Check commit OID if location.commit is present (v1.4)
|
|
1025
|
+
if (entry.location?.commit) {
|
|
1026
|
+
try {
|
|
1027
|
+
execSync(`git cat-file -e ${entry.location.commit}`, {
|
|
1028
|
+
cwd: root,
|
|
1029
|
+
stdio: 'ignore'
|
|
1030
|
+
});
|
|
1031
|
+
} catch {
|
|
1032
|
+
issues.push({
|
|
1033
|
+
id: entry.id,
|
|
1034
|
+
type: 'BROKEN_REF',
|
|
1035
|
+
severity: 'warning',
|
|
1036
|
+
message: `Referenced commit does not exist: ${entry.location.commit}`,
|
|
1037
|
+
projectPath: root,
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return issues;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Validate security (secrets, PII)
|
|
1048
|
+
*
|
|
1049
|
+
* TODO v1.6: Enhanced pattern matching
|
|
1050
|
+
* - Add credit card patterns (Luhn algorithm validation)
|
|
1051
|
+
* - Add CVV patterns (3-4 digits)
|
|
1052
|
+
* - Add SSN patterns (XXX-XX-XXXX)
|
|
1053
|
+
* - Add API key patterns (common formats)
|
|
1054
|
+
* - Add file-based logging detection (console.log with sensitive data)
|
|
1055
|
+
* - See: src/utils/v1.6-patterns.ts (commented out for v1.6)
|
|
1056
|
+
*/
|
|
1057
|
+
function validateSecurity(entries: MemoryEntry[], root: string): BuddyIssue[] {
|
|
1058
|
+
const issues: BuddyIssue[] = [];
|
|
1059
|
+
|
|
1060
|
+
for (const entry of entries) {
|
|
1061
|
+
const content = entry.details || entry.code || '';
|
|
1062
|
+
|
|
1063
|
+
// Check for secrets
|
|
1064
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
1065
|
+
const matches = content.match(pattern.pattern);
|
|
1066
|
+
if (matches) {
|
|
1067
|
+
// RED if agent-origin, YELLOW if human
|
|
1068
|
+
const severity = entry.sourceType === 'agent' ? 'error' : 'warning';
|
|
1069
|
+
issues.push({
|
|
1070
|
+
id: entry.id,
|
|
1071
|
+
type: 'SECRET',
|
|
1072
|
+
severity,
|
|
1073
|
+
message: `Potential secret detected (${pattern.name}): ${matches[0].substring(0, 20)}...`,
|
|
1074
|
+
projectPath: root,
|
|
1075
|
+
suggestion: 'Remove secret from memory entry',
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Check for PII (emails, IDs)
|
|
1081
|
+
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
|
|
1082
|
+
const emailMatches = content.match(emailPattern);
|
|
1083
|
+
if (emailMatches && entry.sourceType === 'agent') {
|
|
1084
|
+
issues.push({
|
|
1085
|
+
id: entry.id,
|
|
1086
|
+
type: 'PII',
|
|
1087
|
+
severity: 'error',
|
|
1088
|
+
message: `PII detected (email): ${emailMatches[0]}`,
|
|
1089
|
+
projectPath: root,
|
|
1090
|
+
suggestion: 'Remove PII from memory entry',
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// TODO v1.6: Enhanced patterns (see src/utils/v1.6-patterns.ts)
|
|
1095
|
+
// - Credit card: /(?:\d{4}[-\s]?){3}\d{4}/g + Luhn validation
|
|
1096
|
+
// - CVV: /\b\d{3,4}\b/g (context-dependent)
|
|
1097
|
+
// - SSN: /\b\d{3}-\d{2}-\d{4}\b/g
|
|
1098
|
+
// - API keys: Common formats (AWS, Google, etc.)
|
|
1099
|
+
// - File logging: console.log/logger.* with sensitive patterns
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return issues;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Detect stale records (file changed since commit)
|
|
1107
|
+
*/
|
|
1108
|
+
function detectStaleness(entries: MemoryEntry[], root: string): BuddyIssue[] {
|
|
1109
|
+
const issues: BuddyIssue[] = [];
|
|
1110
|
+
|
|
1111
|
+
for (const entry of entries) {
|
|
1112
|
+
if (entry.location?.commit && entry.file) {
|
|
1113
|
+
try {
|
|
1114
|
+
// Check if file has changed since commit
|
|
1115
|
+
const filePath = path.join(root, entry.file);
|
|
1116
|
+
if (fs.existsSync(filePath)) {
|
|
1117
|
+
const currentCommit = execSync('git rev-parse HEAD', {
|
|
1118
|
+
cwd: root,
|
|
1119
|
+
encoding: 'utf-8'
|
|
1120
|
+
}).trim();
|
|
1121
|
+
|
|
1122
|
+
if (currentCommit !== entry.location.commit) {
|
|
1123
|
+
// File has changed - mark as stale
|
|
1124
|
+
issues.push({
|
|
1125
|
+
id: entry.id,
|
|
1126
|
+
type: 'STALE',
|
|
1127
|
+
severity: 'warning',
|
|
1128
|
+
message: `File changed since commit ${entry.location.commit.substring(0, 8)}`,
|
|
1129
|
+
projectPath: root,
|
|
1130
|
+
suggestion: 'Update memory entry or rebuild graph',
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
} catch {
|
|
1135
|
+
// Git not available or file doesn't exist - skip
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return issues;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Validate policy rules
|
|
1145
|
+
*
|
|
1146
|
+
* TODO v1.6: Add Git-based protected file detection
|
|
1147
|
+
* - Use `git diff --name-only HEAD` to get changed files
|
|
1148
|
+
* - Cross-reference with protected.txt
|
|
1149
|
+
* - Detect protected file modifications (RED status)
|
|
1150
|
+
* - Detect file deletions (via `git diff --diff-filter=D`)
|
|
1151
|
+
* - Detect large-scale changes (via `git diff --stat` line count > threshold)
|
|
1152
|
+
*/
|
|
1153
|
+
function validatePolicies(entries: MemoryEntry[], root: string): BuddyIssue[] {
|
|
1154
|
+
const issues: BuddyIssue[] = [];
|
|
1155
|
+
|
|
1156
|
+
// Policy: "Prefer fresh code over memory"
|
|
1157
|
+
// Policy: "No secrets in memory"
|
|
1158
|
+
// (Already checked in validateSecurity)
|
|
1159
|
+
|
|
1160
|
+
// TODO v1.6: Protected file detection via Git diff
|
|
1161
|
+
// const protectedFiles = readLines(getFilePath('PROTECTED', root));
|
|
1162
|
+
// const changedFiles = getChangedFiles(root);
|
|
1163
|
+
// for (const file of changedFiles) {
|
|
1164
|
+
// if (protectedFiles.includes(file)) {
|
|
1165
|
+
// issues.push({
|
|
1166
|
+
// type: 'PROTECTED_FILE',
|
|
1167
|
+
// severity: 'error',
|
|
1168
|
+
// message: `Protected file modified: ${file}`,
|
|
1169
|
+
// projectPath: root,
|
|
1170
|
+
// suggestion: 'Review changes or remove from protected.txt',
|
|
1171
|
+
// });
|
|
1172
|
+
// }
|
|
1173
|
+
// }
|
|
1174
|
+
|
|
1175
|
+
// Policy: "Agent records should be DRAFT until verified"
|
|
1176
|
+
for (const entry of entries) {
|
|
1177
|
+
if (entry.sourceType === 'agent' && entry.memoryStatus !== 'DRAFT') {
|
|
1178
|
+
issues.push({
|
|
1179
|
+
id: entry.id,
|
|
1180
|
+
type: 'POLICY',
|
|
1181
|
+
severity: 'info',
|
|
1182
|
+
message: 'Agent-origin record should be DRAFT until verified',
|
|
1183
|
+
projectPath: root,
|
|
1184
|
+
suggestion: 'Mark as DRAFT or promote to VERIFIED after review',
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
return issues;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Validate consistency (code + memory changes)
|
|
1194
|
+
*/
|
|
1195
|
+
function validateConsistency(entries: MemoryEntry[], root: string, changedFiles: string[]): BuddyIssue[] {
|
|
1196
|
+
const issues: BuddyIssue[] = [];
|
|
1197
|
+
|
|
1198
|
+
// Check if memory entries match changed files
|
|
1199
|
+
for (const entry of entries) {
|
|
1200
|
+
if (entry.file && changedFiles.length > 0 && !changedFiles.includes(entry.file)) {
|
|
1201
|
+
// Entry references file that wasn't changed - might be inconsistent
|
|
1202
|
+
issues.push({
|
|
1203
|
+
id: entry.id,
|
|
1204
|
+
type: 'CONFLICT',
|
|
1205
|
+
severity: 'info',
|
|
1206
|
+
message: `Memory entry references unchanged file: ${entry.file}`,
|
|
1207
|
+
projectPath: root,
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
return issues;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Calculate Buddy-Check status from issues
|
|
1217
|
+
*/
|
|
1218
|
+
function calculateBuddyStatus(issues: BuddyIssue[]): BuddyStatus {
|
|
1219
|
+
// RED: Any error-level issues
|
|
1220
|
+
if (issues.some(i => i.severity === 'error')) {
|
|
1221
|
+
return 'RED';
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// YELLOW: Any warnings
|
|
1225
|
+
if (issues.some(i => i.severity === 'warning')) {
|
|
1226
|
+
return 'YELLOW';
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// GREEN: No errors or warnings
|
|
1230
|
+
return 'GREEN';
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* Generate summary text
|
|
1235
|
+
*/
|
|
1236
|
+
function generateSummary(status: BuddyStatus, issueCount: number, recordCount: number): string {
|
|
1237
|
+
if (status === 'RED') {
|
|
1238
|
+
return `RED: ${issueCount} critical issue(s) found in ${recordCount} records`;
|
|
1239
|
+
} else if (status === 'YELLOW') {
|
|
1240
|
+
return `YELLOW: ${issueCount} warning(s) found in ${recordCount} records`;
|
|
1241
|
+
} else {
|
|
1242
|
+
return `GREEN: All checks passed for ${recordCount} records`;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* Count trust levels in entries
|
|
1248
|
+
*/
|
|
1249
|
+
function countTrustLevels(entries: MemoryEntry[]): { high: number; medium: number; low: number } {
|
|
1250
|
+
let high = 0;
|
|
1251
|
+
let medium = 0;
|
|
1252
|
+
let low = 0;
|
|
1253
|
+
|
|
1254
|
+
for (const entry of entries) {
|
|
1255
|
+
const trust = entry.trustLevel || 'medium';
|
|
1256
|
+
if (trust === 'high') high++;
|
|
1257
|
+
else if (trust === 'low') low++;
|
|
1258
|
+
else medium++;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
return { high, medium, low };
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Get changed files from Git
|
|
1266
|
+
*/
|
|
1267
|
+
function getChangedFiles(root: string): string[] {
|
|
1268
|
+
try {
|
|
1269
|
+
const output = execSync('git diff --name-only HEAD', {
|
|
1270
|
+
cwd: root,
|
|
1271
|
+
encoding: 'utf-8'
|
|
1272
|
+
});
|
|
1273
|
+
return output.split('\n').filter(f => f.trim()).map(f => f.trim());
|
|
1274
|
+
} catch {
|
|
1275
|
+
return [];
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* Parse line range (e.g., "42-50" or "42")
|
|
1281
|
+
*/
|
|
1282
|
+
function parseLineRange(lines: string): { start: number; end: number } | null {
|
|
1283
|
+
if (lines.includes('-')) {
|
|
1284
|
+
const [start, end] = lines.split('-').map(n => parseInt(n, 10));
|
|
1285
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
1286
|
+
return { start, end };
|
|
1287
|
+
}
|
|
1288
|
+
} else {
|
|
1289
|
+
const line = parseInt(lines, 10);
|
|
1290
|
+
if (!isNaN(line)) {
|
|
1291
|
+
return { start: line, end: line };
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* Mark agent records as DRAFT (for postflight --fix)
|
|
1299
|
+
*/
|
|
1300
|
+
async function markAgentRecordsAsDraft(entries: MemoryEntry[], root: string): Promise<void> {
|
|
1301
|
+
const memoryPath = getFilePath('MEMORY', root);
|
|
1302
|
+
const updatedEntries: MemoryEntry[] = [];
|
|
1303
|
+
let updated = false;
|
|
1304
|
+
|
|
1305
|
+
for (const entry of entries) {
|
|
1306
|
+
if (entry.sourceType === 'agent' && entry.memoryStatus !== 'DRAFT') {
|
|
1307
|
+
updatedEntries.push({
|
|
1308
|
+
...entry,
|
|
1309
|
+
memoryStatus: 'DRAFT',
|
|
1310
|
+
recordVersion: (entry.recordVersion || 1) + 1,
|
|
1311
|
+
});
|
|
1312
|
+
updated = true;
|
|
1313
|
+
} else {
|
|
1314
|
+
updatedEntries.push(entry);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (updated) {
|
|
1319
|
+
// Write updated entries back
|
|
1320
|
+
const jsonl = updatedEntries.map(e => JSON.stringify(e)).join('\n') + '\n';
|
|
1321
|
+
fs.writeFileSync(memoryPath, jsonl);
|
|
1322
|
+
info('Marked agent records as DRAFT');
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Handle human override for RED status
|
|
1328
|
+
*/
|
|
1329
|
+
async function handleOverride(
|
|
1330
|
+
report: BuddyReport,
|
|
1331
|
+
root: string | null,
|
|
1332
|
+
options: DoctorOptions
|
|
1333
|
+
): Promise<BuddyReport> {
|
|
1334
|
+
if (!root) {
|
|
1335
|
+
return report;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// Check if in strict mode (would require override)
|
|
1339
|
+
const config = readConfig(root);
|
|
1340
|
+
const isStrict = config?.strict === true || config?.mode === 'enterprise';
|
|
1341
|
+
|
|
1342
|
+
if (!isStrict) {
|
|
1343
|
+
// Developer mode: allow override but don't require it
|
|
1344
|
+
return report;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// In strict mode, prompt for override
|
|
1348
|
+
console.log('');
|
|
1349
|
+
warn('⚠️ RED status detected - override required in strict mode');
|
|
1350
|
+
console.log('');
|
|
1351
|
+
|
|
1352
|
+
// For now, we'll just return the report without override
|
|
1353
|
+
// In a full implementation, this would prompt for:
|
|
1354
|
+
// - approvedBy (user name/ID)
|
|
1355
|
+
// - reason (why override is needed)
|
|
1356
|
+
|
|
1357
|
+
// For CLI, we can use environment variables or flags
|
|
1358
|
+
const overrideBy = process.env.MEMORYLINK_OVERRIDE_BY || options.for || 'unknown';
|
|
1359
|
+
const overrideReason = process.env.MEMORYLINK_OVERRIDE_REASON || 'Manual override via CLI';
|
|
1360
|
+
|
|
1361
|
+
if (overrideBy !== 'unknown' || overrideReason !== 'Manual override via CLI') {
|
|
1362
|
+
const override: BuddyOverride = {
|
|
1363
|
+
approvedBy: overrideBy,
|
|
1364
|
+
reason: overrideReason,
|
|
1365
|
+
timestamp: new Date().toISOString(),
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
return {
|
|
1369
|
+
...report,
|
|
1370
|
+
override,
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
return report;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Log override to overrides.log (JSONL)
|
|
1379
|
+
*
|
|
1380
|
+
* TODO v1.6: Enhanced override logging
|
|
1381
|
+
* - Add report ID reference
|
|
1382
|
+
* - Add issue IDs that were overridden
|
|
1383
|
+
* - Add context (branch, PR, agent)
|
|
1384
|
+
* - Add audit trail fields (who, when, why, what)
|
|
1385
|
+
* - Format: { approvedBy, reason, timestamp, runType, reportId, issueIds[], context: { branch, pr, agent } }
|
|
1386
|
+
*/
|
|
1387
|
+
function logOverride(override: BuddyOverride, runType: 'preflight' | 'postflight', root: string): void {
|
|
1388
|
+
try {
|
|
1389
|
+
const overridesPath = path.join(root, MEMORYLINK_DIR, 'overrides.log');
|
|
1390
|
+
const entry = {
|
|
1391
|
+
...override,
|
|
1392
|
+
runType,
|
|
1393
|
+
// TODO v1.6: Add reportId, issueIds[], context
|
|
1394
|
+
};
|
|
1395
|
+
const jsonlLine = JSON.stringify(entry) + '\n';
|
|
1396
|
+
fs.appendFileSync(overridesPath, jsonlLine, 'utf-8');
|
|
1397
|
+
} catch (err) {
|
|
1398
|
+
// Silently fail - override logging should not break the main flow
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Print Buddy-Check report
|
|
1404
|
+
*/
|
|
1405
|
+
function printBuddyReport(report: BuddyReport): void {
|
|
1406
|
+
// Check if stdout is writable before printing
|
|
1407
|
+
if (!isStdoutWritable()) {
|
|
1408
|
+
return; // Silently return if stdout is closed
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
safeLog('');
|
|
1412
|
+
safeLog('\x1b[1m🔍 Buddy-Check Report\x1b[0m');
|
|
1413
|
+
safeLog('');
|
|
1414
|
+
|
|
1415
|
+
// Status indicator
|
|
1416
|
+
let statusColor = '\x1b[32m'; // GREEN
|
|
1417
|
+
let statusIcon = '●';
|
|
1418
|
+
if (report.status === 'YELLOW') {
|
|
1419
|
+
statusColor = '\x1b[33m'; // YELLOW
|
|
1420
|
+
} else if (report.status === 'RED') {
|
|
1421
|
+
statusColor = '\x1b[31m'; // RED
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
safeLog(` Status: ${statusColor}${statusIcon} ${report.status}\x1b[0m`);
|
|
1425
|
+
safeLog(` ${report.summary}`);
|
|
1426
|
+
safeLog('');
|
|
1427
|
+
|
|
1428
|
+
if (report.issues.length > 0) {
|
|
1429
|
+
safeLog(` Issues (${report.issues.length}):`);
|
|
1430
|
+
for (const issue of report.issues.slice(0, 10)) {
|
|
1431
|
+
const severityColor = issue.severity === 'error' ? '\x1b[31m' :
|
|
1432
|
+
issue.severity === 'warning' ? '\x1b[33m' : '\x1b[36m';
|
|
1433
|
+
safeLog(` ${severityColor}${issue.severity.toUpperCase()}\x1b[0m [${issue.type}] ${issue.message}`);
|
|
1434
|
+
if (issue.suggestion) {
|
|
1435
|
+
safeLog(` 💡 ${issue.suggestion}`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (report.issues.length > 10) {
|
|
1439
|
+
safeLog(` ... and ${report.issues.length - 10} more`);
|
|
1440
|
+
}
|
|
1441
|
+
} else {
|
|
1442
|
+
safeLog(' ✓ No issues found');
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if (report.recordsUsed !== undefined) {
|
|
1446
|
+
safeLog('');
|
|
1447
|
+
safeLog(` Records: ${report.recordsUsed} checked`);
|
|
1448
|
+
if (report.highTrust !== undefined) {
|
|
1449
|
+
safeLog(` Trust: ${report.highTrust} high, ${report.mediumTrust} medium, ${report.lowTrust} low`);
|
|
1450
|
+
}
|
|
1451
|
+
if (report.staleCount !== undefined && report.staleCount > 0) {
|
|
1452
|
+
safeLog(` Stale: ${report.staleCount}`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
if (report.durationMs !== undefined) {
|
|
1457
|
+
safeLog(` Duration: ${report.durationMs}ms`);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
if (report.override) {
|
|
1461
|
+
safeLog('');
|
|
1462
|
+
warn(`⚠️ Override: Approved by ${report.override.approvedBy}`);
|
|
1463
|
+
safeLog(` Reason: ${report.override.reason}`);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
safeLog('');
|
|
1467
|
+
}
|
|
1468
|
+
|