@cachly-dev/mcp-server 0.10.48 โ 0.10.50
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/.github/workflows/eas-build-android.yml +58 -0
- package/.github/workflows/eas-update.yml +55 -0
- package/BENCH.md +80 -0
- package/README.md +5 -33
- package/STRATEGY.md +218 -0
- package/apps/travel-chaos-organizer/.env.example +49 -0
- package/apps/travel-chaos-organizer/DEPLOY.md +328 -0
- package/apps/travel-chaos-organizer/Makefile +57 -0
- package/apps/travel-chaos-organizer/README.md +159 -0
- package/apps/travel-chaos-organizer/backend/.env.example +10 -0
- package/apps/travel-chaos-organizer/backend/Dockerfile +25 -0
- package/apps/travel-chaos-organizer/backend/alembic/env.py +56 -0
- package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +24 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +24 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +24 -0
- package/apps/travel-chaos-organizer/backend/alembic.ini +39 -0
- package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +81 -0
- package/apps/travel-chaos-organizer/backend/app/config.py +66 -0
- package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/db/database.py +20 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +71 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +31 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +4 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +5 -0
- package/apps/travel-chaos-organizer/backend/app/limiter.py +4 -0
- package/apps/travel-chaos-organizer/backend/app/logging_config.py +25 -0
- package/apps/travel-chaos-organizer/backend/app/main.py +77 -0
- package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/models/schemas.py +142 -0
- package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/routers/admin.py +237 -0
- package/apps/travel-chaos-organizer/backend/app/routers/cache.py +13 -0
- package/apps/travel-chaos-organizer/backend/app/routers/events.py +29 -0
- package/apps/travel-chaos-organizer/backend/app/routers/export.py +122 -0
- package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +122 -0
- package/apps/travel-chaos-organizer/backend/app/routers/items.py +130 -0
- package/apps/travel-chaos-organizer/backend/app/routers/mail.py +108 -0
- package/apps/travel-chaos-organizer/backend/app/routers/parse.py +303 -0
- package/apps/travel-chaos-organizer/backend/app/routers/payments.py +179 -0
- package/apps/travel-chaos-organizer/backend/app/routers/trips.py +134 -0
- package/apps/travel-chaos-organizer/backend/app/routers/users.py +169 -0
- package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +37 -0
- package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/services/cache.py +131 -0
- package/apps/travel-chaos-organizer/backend/app/services/drip.py +128 -0
- package/apps/travel-chaos-organizer/backend/app/services/email.py +61 -0
- package/apps/travel-chaos-organizer/backend/app/services/errors.py +44 -0
- package/apps/travel-chaos-organizer/backend/app/services/notifier.py +10 -0
- package/apps/travel-chaos-organizer/backend/app/services/ollama.py +101 -0
- package/apps/travel-chaos-organizer/backend/app/services/parser.py +22 -0
- package/apps/travel-chaos-organizer/backend/app/services/quota.py +85 -0
- package/apps/travel-chaos-organizer/backend/app/services/sentry.py +11 -0
- package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +35 -0
- package/apps/travel-chaos-organizer/backend/pytest.ini +4 -0
- package/apps/travel-chaos-organizer/backend/requirements-dev.txt +6 -0
- package/apps/travel-chaos-organizer/backend/requirements.txt +25 -0
- package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/tests/conftest.py +223 -0
- package/apps/travel-chaos-organizer/backend/tests/test_admin.py +99 -0
- package/apps/travel-chaos-organizer/backend/tests/test_cache.py +142 -0
- package/apps/travel-chaos-organizer/backend/tests/test_drip.py +32 -0
- package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +121 -0
- package/apps/travel-chaos-organizer/backend/tests/test_export.py +48 -0
- package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +107 -0
- package/apps/travel-chaos-organizer/backend/tests/test_items.py +138 -0
- package/apps/travel-chaos-organizer/backend/tests/test_mail.py +113 -0
- package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +145 -0
- package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +98 -0
- package/apps/travel-chaos-organizer/backend/tests/test_parser.py +72 -0
- package/apps/travel-chaos-organizer/backend/tests/test_payments.py +163 -0
- package/apps/travel-chaos-organizer/backend/tests/test_quota.py +46 -0
- package/apps/travel-chaos-organizer/backend/tests/test_search.py +67 -0
- package/apps/travel-chaos-organizer/backend/tests/test_trips.py +123 -0
- package/apps/travel-chaos-organizer/backend/tests/test_users.py +52 -0
- package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
- package/apps/travel-chaos-organizer/caddy/Caddyfile +27 -0
- package/apps/travel-chaos-organizer/docker-compose.node1.yml +31 -0
- package/apps/travel-chaos-organizer/docker-compose.override.yml +14 -0
- package/apps/travel-chaos-organizer/docker-compose.yml +128 -0
- package/apps/travel-chaos-organizer/frontend/.env.example +13 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +80 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +166 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +130 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +381 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +346 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +363 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +34 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +35 -0
- package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +91 -0
- package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +71 -0
- package/apps/travel-chaos-organizer/frontend/app/auth.tsx +29 -0
- package/apps/travel-chaos-organizer/frontend/app/index.tsx +16 -0
- package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +119 -0
- package/apps/travel-chaos-organizer/frontend/app.config.ts +54 -0
- package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +66 -0
- package/apps/travel-chaos-organizer/frontend/assets/icon.svg +79 -0
- package/apps/travel-chaos-organizer/frontend/assets/splash.svg +15 -0
- package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +102 -0
- package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +107 -0
- package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +109 -0
- package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +45 -0
- package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +19 -0
- package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +155 -0
- package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +55 -0
- package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +80 -0
- package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +39 -0
- package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +39 -0
- package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +121 -0
- package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +73 -0
- package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +130 -0
- package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +60 -0
- package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +42 -0
- package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +146 -0
- package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +44 -0
- package/apps/travel-chaos-organizer/frontend/eas.json +22 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +24 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +60 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +112 -0
- package/apps/travel-chaos-organizer/frontend/lib/api.ts +223 -0
- package/apps/travel-chaos-organizer/frontend/lib/auth.ts +93 -0
- package/apps/travel-chaos-organizer/frontend/lib/db.ts +86 -0
- package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +54 -0
- package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +13 -0
- package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +112 -0
- package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +145 -0
- package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +43 -0
- package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +28 -0
- package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +44 -0
- package/apps/travel-chaos-organizer/frontend/package-lock.json +13221 -0
- package/apps/travel-chaos-organizer/frontend/package.json +44 -0
- package/apps/travel-chaos-organizer/frontend/tsconfig.json +23 -0
- package/apps/travel-chaos-organizer/keycloak/realm-export.json +59 -0
- package/apps/travel-chaos-organizer/landing/index.html +773 -0
- package/apps/travel-chaos-organizer/scripts/backup.sh +14 -0
- package/apps/travel-chaos-organizer/scripts/configure-env.sh +82 -0
- package/apps/travel-chaos-organizer/scripts/deploy.sh +54 -0
- package/apps/travel-chaos-organizer/scripts/health-check.sh +36 -0
- package/apps/travel-chaos-organizer/scripts/migrate.sh +7 -0
- package/apps/travel-chaos-organizer/scripts/server-setup.sh +85 -0
- package/apps/travel-chaos-organizer/scripts/smoke-test.sh +29 -0
- package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +71 -0
- package/apps/travel-chaos-organizer/telegram-bot/.env.example +3 -0
- package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +6 -0
- package/apps/travel-chaos-organizer/telegram-bot/bot.py +504 -0
- package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +3 -0
- package/dist/packages/telegram-notify/client.d.ts +19 -0
- package/dist/packages/telegram-notify/client.js +75 -0
- package/dist/packages/telegram-notify/client.js.map +1 -0
- package/dist/src/__tests__/auth.test.js.map +1 -0
- package/dist/{__tests__ โ src/__tests__}/brain-flow.test.js +141 -0
- package/dist/src/__tests__/brain-flow.test.js.map +1 -0
- package/dist/src/__tests__/confidence.test.js.map +1 -0
- package/dist/src/__tests__/e2e/api-auth.test.js.map +1 -0
- package/dist/src/__tests__/e2e/api-health.test.js.map +1 -0
- package/dist/src/__tests__/e2e/cli.test.js.map +1 -0
- package/dist/src/__tests__/e2e/config.js.map +1 -0
- package/dist/src/__tests__/e2e/funnel.test.js.map +1 -0
- package/dist/src/__tests__/e2e/mcp-tools.test.js.map +1 -0
- package/dist/src/__tests__/e2e/provisioning.test.js.map +1 -0
- package/dist/{__tests__ โ src/__tests__}/handlers-unit.test.js +0 -2
- package/dist/src/__tests__/handlers-unit.test.js.map +1 -0
- package/dist/src/__tests__/namespace.test.js.map +1 -0
- package/dist/src/__tests__/rerank.test.js +98 -0
- package/dist/src/__tests__/rerank.test.js.map +1 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/bench/cachly-bench.d.ts +35 -0
- package/dist/src/bench/cachly-bench.js +182 -0
- package/dist/src/bench/cachly-bench.js.map +1 -0
- package/dist/src/bench/fixtures.d.ts +34 -0
- package/dist/src/bench/fixtures.js +199 -0
- package/dist/src/bench/fixtures.js.map +1 -0
- package/dist/src/ckg.js.map +1 -0
- package/dist/src/confidence.js.map +1 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/handlers/advanced.js.map +1 -0
- package/dist/{handlers โ src/handlers}/brain.js +261 -87
- package/dist/src/handlers/brain.js.map +1 -0
- package/dist/src/handlers/cache.js.map +1 -0
- package/dist/{handlers โ src/handlers}/context.js +9 -4
- package/dist/src/handlers/context.js.map +1 -0
- package/dist/{handlers โ src/handlers}/fedbrain.d.ts +6 -0
- package/dist/{handlers โ src/handlers}/fedbrain.js +19 -7
- package/dist/src/handlers/fedbrain.js.map +1 -0
- package/dist/src/handlers/instances.js.map +1 -0
- package/dist/{handlers โ src/handlers}/roadmap.js +7 -3
- package/dist/src/handlers/roadmap.js.map +1 -0
- package/dist/src/handlers/syndicate.js.map +1 -0
- package/dist/src/handlers/tco.d.ts +16 -0
- package/dist/src/handlers/tco.js +303 -0
- package/dist/src/handlers/tco.js.map +1 -0
- package/dist/src/handlers/team.js.map +1 -0
- package/dist/{index.js โ src/index.js} +153 -218
- package/dist/src/index.js.map +1 -0
- package/dist/src/namespace.js.map +1 -0
- package/dist/src/notifier.d.ts +5 -0
- package/dist/src/notifier.js +6 -0
- package/dist/src/notifier.js.map +1 -0
- package/dist/src/rerank.d.ts +50 -0
- package/dist/src/rerank.js +92 -0
- package/dist/src/rerank.js.map +1 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/search.test.d.ts +1 -0
- package/dist/src/search.test.js.map +1 -0
- package/dist/{tools.d.ts โ src/tools.d.ts} +196 -0
- package/dist/{tools.js โ src/tools.js} +178 -0
- package/dist/src/tools.js.map +1 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +4 -3
- package/packages/telegram-notify/SPLIT_PLAN.md +106 -0
- package/packages/telegram-notify/client.ts +88 -0
- package/packages/telegram-notify/emojis.json +22 -0
- package/packages/telegram-notify/pyproject.toml +17 -0
- package/packages/telegram-notify/telegram_notify/__init__.py +3 -0
- package/packages/telegram-notify/telegram_notify/client.py +87 -0
- package/packages/telegram-notify/telegram_notify/emojis.json +22 -0
- package/scripts/generate-icons.js +28 -0
- package/smithery.yaml +21 -8
- package/src/__tests__/brain-flow.test.ts +174 -0
- package/src/__tests__/handlers-unit.test.ts +1 -2
- package/src/__tests__/rerank.test.ts +111 -0
- package/src/bench/cachly-bench.ts +217 -0
- package/src/bench/fixtures.ts +226 -0
- package/src/handlers/brain.ts +276 -92
- package/src/handlers/context.ts +9 -4
- package/src/handlers/fedbrain.ts +21 -7
- package/src/handlers/roadmap.ts +7 -3
- package/src/handlers/tco.ts +347 -0
- package/src/index.ts +139 -220
- package/src/notifier.ts +5 -0
- package/src/rerank.ts +123 -0
- package/src/tools.ts +191 -0
- package/tsconfig.json +2 -3
- package/dist/__tests__/auth.test.js.map +0 -1
- package/dist/__tests__/brain-flow.test.js.map +0 -1
- package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
- package/dist/__tests__/brain-predict-failures.test.js +0 -339
- package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
- package/dist/__tests__/confidence.test.js.map +0 -1
- package/dist/__tests__/e2e/api-auth.test.js.map +0 -1
- package/dist/__tests__/e2e/api-health.test.js.map +0 -1
- package/dist/__tests__/e2e/cli.test.js.map +0 -1
- package/dist/__tests__/e2e/config.js.map +0 -1
- package/dist/__tests__/e2e/funnel.test.js.map +0 -1
- package/dist/__tests__/e2e/mcp-tools.test.js.map +0 -1
- package/dist/__tests__/e2e/provisioning.test.js.map +0 -1
- package/dist/__tests__/handlers-unit.test.js.map +0 -1
- package/dist/__tests__/namespace.test.js.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/ckg.js.map +0 -1
- package/dist/confidence.js.map +0 -1
- package/dist/embeddings.js.map +0 -1
- package/dist/handlers/advanced.js.map +0 -1
- package/dist/handlers/brain.js.map +0 -1
- package/dist/handlers/cache.js.map +0 -1
- package/dist/handlers/context.js.map +0 -1
- package/dist/handlers/fedbrain.js.map +0 -1
- package/dist/handlers/instances.js.map +0 -1
- package/dist/handlers/roadmap.js.map +0 -1
- package/dist/handlers/syndicate.js.map +0 -1
- package/dist/handlers/team.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/namespace.js.map +0 -1
- package/dist/search.js.map +0 -1
- package/dist/search.test.js.map +0 -1
- package/dist/tools.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/src/__tests__/brain-predict-failures.test.ts +0 -430
- /package/dist/{__tests__ โ src/__tests__}/auth.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/auth.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/brain-flow.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/confidence.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/confidence.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/api-auth.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/api-auth.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/api-health.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/api-health.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/cli.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/cli.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/config.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/config.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/funnel.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/funnel.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/mcp-tools.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/mcp-tools.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/provisioning.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/e2e/provisioning.test.js +0 -0
- /package/dist/{__tests__ โ src/__tests__}/handlers-unit.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/namespace.test.d.ts +0 -0
- /package/dist/{__tests__ โ src/__tests__}/namespace.test.js +0 -0
- /package/dist/{search.test.d.ts โ src/__tests__/rerank.test.d.ts} +0 -0
- /package/dist/{auth.d.ts โ src/auth.d.ts} +0 -0
- /package/dist/{auth.js โ src/auth.js} +0 -0
- /package/dist/{ckg.d.ts โ src/ckg.d.ts} +0 -0
- /package/dist/{ckg.js โ src/ckg.js} +0 -0
- /package/dist/{confidence.d.ts โ src/confidence.d.ts} +0 -0
- /package/dist/{confidence.js โ src/confidence.js} +0 -0
- /package/dist/{embeddings.d.ts โ src/embeddings.d.ts} +0 -0
- /package/dist/{embeddings.js โ src/embeddings.js} +0 -0
- /package/dist/{handlers โ src/handlers}/advanced.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/advanced.js +0 -0
- /package/dist/{handlers โ src/handlers}/brain.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/cache.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/cache.js +0 -0
- /package/dist/{handlers โ src/handlers}/context.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/instances.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/instances.js +0 -0
- /package/dist/{handlers โ src/handlers}/roadmap.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/syndicate.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/syndicate.js +0 -0
- /package/dist/{handlers โ src/handlers}/team.d.ts +0 -0
- /package/dist/{handlers โ src/handlers}/team.js +0 -0
- /package/dist/{index.d.ts โ src/index.d.ts} +0 -0
- /package/dist/{namespace.d.ts โ src/namespace.d.ts} +0 -0
- /package/dist/{namespace.js โ src/namespace.js} +0 -0
- /package/dist/{search.d.ts โ src/search.d.ts} +0 -0
- /package/dist/{search.js โ src/search.js} +0 -0
- /package/dist/{search.test.js โ src/search.test.js} +0 -0
- /package/dist/{utils.d.ts โ src/utils.d.ts} +0 -0
- /package/dist/{utils.js โ src/utils.js} +0 -0
|
@@ -3,17 +3,26 @@ import { calculateConfidence, confidenceBadge, STRUCTURED_TEMPLATES, CONFIDENCE_
|
|
|
3
3
|
import { ckgSlug, extractProblemConcept, ckgUpsertNode, ckgUpdateEdge } from '../ckg.js';
|
|
4
4
|
import { safeJsonParse } from '../utils.js';
|
|
5
5
|
import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _indexVocab } from '../search.js';
|
|
6
|
+
import { rerankByQuality } from '../rerank.js';
|
|
6
7
|
import { computeEmbedding, hasEmbedProvider } from '../embeddings.js';
|
|
7
8
|
// โโ Changelog (shown once per version in session_start) โโโโโโโโโโโโโโโโโโโโโโ
|
|
8
|
-
const MCP_VERSION = '0.10.
|
|
9
|
+
const MCP_VERSION = '0.10.50';
|
|
9
10
|
const WHATS_NEW = {
|
|
10
|
-
'0.10.
|
|
11
|
+
'0.10.50': [
|
|
11
12
|
`๐ **What's new in v${MCP_VERSION}:**`,
|
|
12
|
-
` โ
|
|
13
|
-
` โ
|
|
14
|
-
` โ
|
|
15
|
-
` โ
|
|
16
|
-
`
|
|
13
|
+
` โ
\`smart_recall\` โ unified keyword + semantic hybrid list (one ranked result, not two sections)`,
|
|
14
|
+
` โ
\`smart_recall\` โ "Brain saved you here" banner surfaces time saved inline per proven lesson`,
|
|
15
|
+
` โ
Quality reranking โ proven successes outrank symptom-dense failures (+11% Precision@1)`,
|
|
16
|
+
` โ
Cachly-Bench โ IR benchmark with CI regression guard (see BENCH.md for the proof)`,
|
|
17
|
+
` ๐ง Contradiction resolution now persisted to Redis (90-day TTL)`,
|
|
18
|
+
` ๐ก Run \`smart_recall\` to see your Brain's recall quality in action`,
|
|
19
|
+
],
|
|
20
|
+
'0.10.49': [
|
|
21
|
+
` โ
\`brain_from_git\` โ auto-seed your Brain from git history in seconds`,
|
|
22
|
+
` โ
\`brain_predict_failures\` โ predict CI/build failures before they happen`,
|
|
23
|
+
` โ
VS Code extension โ Brain status bar, ambient learning, CodeLens hints`,
|
|
24
|
+
` โ
Team Brain โ share lessons across your whole engineering team`,
|
|
25
|
+
` ๐ง Stability โ auto-expiring memory keys, longer provisioning wait, robust recall`,
|
|
17
26
|
],
|
|
18
27
|
};
|
|
19
28
|
export const BRAIN_TOOL_NAMES = new Set([
|
|
@@ -69,6 +78,19 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
69
78
|
await ckgUpdateEdge(redis, cId, 'contradicts', resId, false);
|
|
70
79
|
}
|
|
71
80
|
catch { /* non-critical */ }
|
|
81
|
+
// Persist a queryable resolution record โ so contradiction history survives
|
|
82
|
+
// (CKG edges alone aren't easily auditable). Bounded + TTL'd like other history.
|
|
83
|
+
try {
|
|
84
|
+
const resolutionKey = `cachly:contradictions:${topic}`;
|
|
85
|
+
const direction = prev.outcome === 'success' && outcome === 'failure'
|
|
86
|
+
? 'kept-success' : 'overwrote-failure';
|
|
87
|
+
await redis.rpush(resolutionKey, JSON.stringify({
|
|
88
|
+
ts, prev_outcome: prev.outcome, new_outcome: outcome, direction,
|
|
89
|
+
}));
|
|
90
|
+
await redis.ltrim(resolutionKey, -50, -1);
|
|
91
|
+
await redis.expire(resolutionKey, 180 * 86400);
|
|
92
|
+
}
|
|
93
|
+
catch { /* non-critical */ }
|
|
72
94
|
contradictionWarning.push(`๐ณ๏ธ Run \`madc_deliberate(topic="${topic}")\` to resolve via expert agent voting.`);
|
|
73
95
|
}
|
|
74
96
|
}
|
|
@@ -114,7 +136,7 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
114
136
|
const depTopics = safeJsonParse(existing, []);
|
|
115
137
|
if (!depTopics.includes(topic))
|
|
116
138
|
depTopics.push(topic);
|
|
117
|
-
await redis.set(depKey, JSON.stringify(depTopics));
|
|
139
|
+
await redis.set(depKey, JSON.stringify(depTopics), 'EX', 90 * 86400);
|
|
118
140
|
}
|
|
119
141
|
const lessonObj = {
|
|
120
142
|
topic,
|
|
@@ -136,9 +158,11 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
136
158
|
version: 3,
|
|
137
159
|
};
|
|
138
160
|
const lesson = JSON.stringify(lessonObj);
|
|
139
|
-
// Always append to the history list (audit log)
|
|
161
|
+
// Always append to the history list (audit log); keep last 100 entries, 90-day TTL
|
|
140
162
|
const listKey = `cachly:lessons:${topic}`;
|
|
141
163
|
await redis.rpush(listKey, lesson);
|
|
164
|
+
await redis.ltrim(listKey, -100, -1);
|
|
165
|
+
await redis.expire(listKey, 90 * 86400);
|
|
142
166
|
// Update best key for success/partial; for failure only update if no success exists
|
|
143
167
|
if (outcome === 'success' || outcome === 'partial') {
|
|
144
168
|
await redis.set(`cachly:lesson:best:${topic}`, lesson);
|
|
@@ -334,8 +358,29 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
334
358
|
const suggestPin = updatedLesson.recall_count === 3 && !lesson.pinned
|
|
335
359
|
? `๐ **You've looked this up 3 times.** Consider pinning it for instant access: add \`pinned: true\` via \`learn_from_attempts\` to always surface it first.`
|
|
336
360
|
: '';
|
|
361
|
+
// โโ Trust signal (today-safe consensus layer) โโโโโโโโโโโโโโโโโโโโโโโ
|
|
362
|
+
// A lesson recalled many times โ or confirmed by multiple distinct
|
|
363
|
+
// authors โ has proven its value. Surface that as social proof.
|
|
364
|
+
const lessonAuthors = lesson.authors
|
|
365
|
+
?? (lesson.author ? [lesson.author] : []);
|
|
366
|
+
const distinctAuthors = [...new Set(lessonAuthors.filter(Boolean))];
|
|
367
|
+
const rc = updatedLesson.recall_count;
|
|
368
|
+
let trustBadge = '';
|
|
369
|
+
if (distinctAuthors.length >= 2 && rc >= 5) {
|
|
370
|
+
trustBadge = `๐ **Battle-tested** โ recalled ${rc}ร ยท verified by ${distinctAuthors.length} developers. Trust this.`;
|
|
371
|
+
}
|
|
372
|
+
else if (rc >= 10) {
|
|
373
|
+
trustBadge = `๐ **Battle-tested** โ recalled ${rc}ร. This is one of your most-proven solutions.`;
|
|
374
|
+
}
|
|
375
|
+
else if (distinctAuthors.length >= 2) {
|
|
376
|
+
trustBadge = `โ
**Team-verified** โ confirmed by ${distinctAuthors.length} developers.`;
|
|
377
|
+
}
|
|
378
|
+
else if (rc >= 5) {
|
|
379
|
+
trustBadge = `โ
**Proven** โ recalled ${rc}ร without contradiction.`;
|
|
380
|
+
}
|
|
337
381
|
return [
|
|
338
382
|
rememberWhen,
|
|
383
|
+
trustBadge,
|
|
339
384
|
`${badge} **Best solution for \`${topic}\`** ${sevEmoji}${lesson.severity ? ` (${lesson.severity})` : ''} ยท recalled ${updatedLesson.recall_count}ร`,
|
|
340
385
|
``,
|
|
341
386
|
`**What worked:** ${lesson.what_worked}`,
|
|
@@ -385,57 +430,37 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
385
430
|
const { instance_id, query, threshold = 0.78, } = args;
|
|
386
431
|
const redis = await getConnection(instance_id);
|
|
387
432
|
// โโ Layer 1: Keyword search across ALL brain data (always works, no embedding) โโ
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
//
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
.replace('cachly:idx:', '๐ ');
|
|
413
|
-
const preview = m.content.slice(0, 300).replace(/\n/g, ' ');
|
|
414
|
-
lines.push(` **${label}** _(BM25: ${m.score.toFixed(2)}, matched: ${m.matchedWords.join(', ')})_`);
|
|
415
|
-
lines.push(` > ${preview}${m.content.length > 300 ? 'โฆ' : ''}\n`);
|
|
433
|
+
const rawMatches = await keywordSearch(redis, ['cachly:ctx:*', 'cachly:lesson:best:*', 'cachly:idx:*'], query, 10);
|
|
434
|
+
// โโ Layer 1.5: Quality-aware rerank โ proven success lessons outrank
|
|
435
|
+
// text-similar failed attempts (the moat; see src/rerank.ts + Cachly-Bench). โโ
|
|
436
|
+
const kwMatches = rerankByQuality(rawMatches);
|
|
437
|
+
const savedHere = [];
|
|
438
|
+
const lessonMatches = kwMatches.filter(m => m.key.startsWith('cachly:lesson:best:'));
|
|
439
|
+
for (const m of lessonMatches.slice(0, 5)) {
|
|
440
|
+
const existing = await redis.get(m.key).catch(() => null);
|
|
441
|
+
if (existing) {
|
|
442
|
+
const lesson = safeJsonParse(existing, null);
|
|
443
|
+
if (lesson) {
|
|
444
|
+
const updated = { ...lesson, recall_count: (lesson.recall_count ?? 0) + 1, verified_at: new Date().toISOString() };
|
|
445
|
+
redis.set(m.key, JSON.stringify(updated)).catch(() => { });
|
|
446
|
+
const sev = lesson.severity;
|
|
447
|
+
const savedMins = sev === 'critical' ? 240 : sev === 'major' ? 60 : 30;
|
|
448
|
+
redis.incrbyfloat(`cachly:stats:time_saved_mins:${instance_id}`, savedMins).catch(() => { });
|
|
449
|
+
// Surface banner for proven successes (recall_count >= 1 means it's been validated)
|
|
450
|
+
if (lesson.outcome === 'success' && (lesson.recall_count ?? 0) >= 1) {
|
|
451
|
+
savedHere.push({
|
|
452
|
+
topic: m.key.replace('cachly:lesson:best:', ''),
|
|
453
|
+
severity: sev ?? 'major',
|
|
454
|
+
recall_count: (lesson.recall_count ?? 0) + 1,
|
|
455
|
+
savedMins,
|
|
456
|
+
});
|
|
416
457
|
}
|
|
417
458
|
}
|
|
418
|
-
// Summary: which sub-queries had matches
|
|
419
|
-
const matched = [...grouped.keys()];
|
|
420
|
-
const unmatched = subQueries.filter(sq => !matched.includes(sq));
|
|
421
|
-
if (unmatched.length > 0) {
|
|
422
|
-
lines.push(`\nโ ๏ธ **No results for:** ${unmatched.map(s => `"${s}"`).join(', ')}`);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
for (const m of kwMatches.slice(0, 8)) {
|
|
427
|
-
const label = m.key
|
|
428
|
-
.replace('cachly:ctx:', '๐ ')
|
|
429
|
-
.replace('cachly:lesson:best:', '๐ก ')
|
|
430
|
-
.replace('cachly:idx:', '๐ ');
|
|
431
|
-
const preview = m.content.slice(0, 400).replace(/\n/g, ' ');
|
|
432
|
-
lines.push(`**${label}** _(BM25: ${m.score.toFixed(2)}, matched: ${m.matchedWords.join(', ')})_`);
|
|
433
|
-
lines.push(`> ${preview}${m.content.length > 400 ? 'โฆ' : ''}\n`);
|
|
434
|
-
}
|
|
435
459
|
}
|
|
436
460
|
}
|
|
437
|
-
// โโ Layer 2: Semantic search (
|
|
461
|
+
// โโ Layer 2: Semantic search (parallel, optional) โโโโโโโโโโโโโโโโโโโโโโโโ
|
|
438
462
|
const inst = await apiFetch(`/api/v1/instances/${instance_id}`);
|
|
463
|
+
const semHits = [];
|
|
439
464
|
if (inst.vector_token && hasEmbedProvider()) {
|
|
440
465
|
try {
|
|
441
466
|
const embedding = await computeEmbedding(query);
|
|
@@ -447,16 +472,13 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
447
472
|
});
|
|
448
473
|
if (searchRes.ok) {
|
|
449
474
|
const results = (await searchRes.json());
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const content = await redis.get(`cachly:ctx:${category}:${key}`);
|
|
458
|
-
lines.push(`**${key}** _(${((hit.similarity ?? 0) * 100).toFixed(0)}% similar)_`, `> ${content?.slice(0, 300) ?? '(evicted)'}${(content?.length ?? 0) > 300 ? 'โฆ' : ''}\n`);
|
|
459
|
-
}
|
|
475
|
+
for (const hit of results.filter(r => r.found && r.id)) {
|
|
476
|
+
const parts = hit.id.replace('ctx:', '').split(':');
|
|
477
|
+
const category = parts[0];
|
|
478
|
+
const key = parts.slice(1).join(':');
|
|
479
|
+
const redisKey = `cachly:ctx:${category}:${key}`;
|
|
480
|
+
const content = await redis.get(redisKey).catch(() => null);
|
|
481
|
+
semHits.push({ key: redisKey, similarity: hit.similarity ?? 0, content: content ?? '(evicted)' });
|
|
460
482
|
}
|
|
461
483
|
}
|
|
462
484
|
}
|
|
@@ -464,7 +486,96 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
464
486
|
// Semantic search failed silently โ keyword results are enough
|
|
465
487
|
}
|
|
466
488
|
}
|
|
467
|
-
|
|
489
|
+
const bm25Scores = kwMatches.map(m => m.score);
|
|
490
|
+
const bm25Min = bm25Scores.length ? Math.min(...bm25Scores) : 0;
|
|
491
|
+
const bm25Range = bm25Scores.length ? (Math.max(...bm25Scores) - bm25Min) || 1 : 1;
|
|
492
|
+
const bm25Norm = (s) => (s - bm25Min) / bm25Range;
|
|
493
|
+
const hybridMap = new Map();
|
|
494
|
+
for (const m of kwMatches) {
|
|
495
|
+
const n = bm25Norm(m.score);
|
|
496
|
+
hybridMap.set(m.key, {
|
|
497
|
+
key: m.key, content: m.content, bm25Score: n,
|
|
498
|
+
hybridScore: n * (semHits.length > 0 ? 0.7 : 1.0),
|
|
499
|
+
matchedWords: m.matchedWords, matchType: 'keyword', subQuery: m.subQuery,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
for (const hit of semHits) {
|
|
503
|
+
const existing = hybridMap.get(hit.key);
|
|
504
|
+
if (existing) {
|
|
505
|
+
existing.semScore = hit.similarity;
|
|
506
|
+
existing.hybridScore = (existing.bm25Score ?? 0) * 0.6 + hit.similarity * 0.4;
|
|
507
|
+
existing.matchType = 'hybrid';
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
hybridMap.set(hit.key, {
|
|
511
|
+
key: hit.key, content: hit.content, semScore: hit.similarity,
|
|
512
|
+
hybridScore: hit.similarity * 0.5, matchType: 'semantic',
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const hybridResults = [...hybridMap.values()].sort((a, b) => b.hybridScore - a.hybridScore);
|
|
517
|
+
// โโ Build output โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
518
|
+
const lines = [`๐ง **Smart Recall** for: _"${query}"_\n`];
|
|
519
|
+
// "Brain saved you here" banner โ surfaces value inline for proven recalled lessons
|
|
520
|
+
const SORDER = { critical: 0, major: 1, minor: 2 };
|
|
521
|
+
const topLesson = savedHere.sort((a, b) => (SORDER[a.severity] ?? 1) - (SORDER[b.severity] ?? 1))[0];
|
|
522
|
+
if (topLesson) {
|
|
523
|
+
lines.push(`> ๐ก **Brain saved you ~${topLesson.savedMins}m here** โ ${topLesson.severity} issue from \`${topLesson.topic}\` recalled ${topLesson.recall_count}ร proven\n`);
|
|
524
|
+
}
|
|
525
|
+
// Show sub-query info if multi-topic was detected
|
|
526
|
+
const subQueries = splitMultiQuery(query);
|
|
527
|
+
if (subQueries.length > 1) {
|
|
528
|
+
lines.push(`_Detected ${subQueries.length} sub-topics:_ ${subQueries.map((s, i) => `${i + 1}. "${s}"`).join(', ')}\n`);
|
|
529
|
+
}
|
|
530
|
+
if (hybridResults.length > 0) {
|
|
531
|
+
const modeLabel = semHits.length > 0
|
|
532
|
+
? `keyword + semantic hybrid`
|
|
533
|
+
: `keyword`;
|
|
534
|
+
lines.push(`### ๐ Results (${hybridResults.length} โ ${modeLabel})\n`);
|
|
535
|
+
// Group by sub-query for multi-topic queries
|
|
536
|
+
if (subQueries.length > 1) {
|
|
537
|
+
const grouped = new Map();
|
|
538
|
+
for (const r of hybridResults.slice(0, 12)) {
|
|
539
|
+
const sq = r.subQuery ?? query;
|
|
540
|
+
if (!grouped.has(sq))
|
|
541
|
+
grouped.set(sq, []);
|
|
542
|
+
grouped.get(sq).push(r);
|
|
543
|
+
}
|
|
544
|
+
for (const [sq, results] of grouped) {
|
|
545
|
+
lines.push(`**Topic: "${sq}"** (${results.length} results)\n`);
|
|
546
|
+
for (const r of results.slice(0, 4)) {
|
|
547
|
+
const label = r.key.replace('cachly:ctx:', '๐ ').replace('cachly:lesson:best:', '๐ก ').replace('cachly:idx:', '๐ ');
|
|
548
|
+
const scorePart = r.matchType === 'hybrid'
|
|
549
|
+
? `BM25: ${(r.bm25Score ?? 0).toFixed(2)}, sem: ${((r.semScore ?? 0) * 100).toFixed(0)}%, ๐ hybrid`
|
|
550
|
+
: r.matchType === 'semantic'
|
|
551
|
+
? `sem: ${((r.semScore ?? 0) * 100).toFixed(0)}%, ๐ฏ semantic`
|
|
552
|
+
: `BM25: ${(r.bm25Score ?? 0).toFixed(2)}, matched: ${r.matchedWords?.join(', ')}`;
|
|
553
|
+
const preview = r.content.slice(0, 300).replace(/\n/g, ' ');
|
|
554
|
+
lines.push(` **${label}** _(${scorePart})_`);
|
|
555
|
+
lines.push(` > ${preview}${r.content.length > 300 ? 'โฆ' : ''}\n`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const matched = [...grouped.keys()];
|
|
559
|
+
const unmatched = subQueries.filter(sq => !matched.includes(sq));
|
|
560
|
+
if (unmatched.length > 0) {
|
|
561
|
+
lines.push(`\nโ ๏ธ **No results for:** ${unmatched.map(s => `"${s}"`).join(', ')}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
for (const r of hybridResults.slice(0, 8)) {
|
|
566
|
+
const label = r.key.replace('cachly:ctx:', '๐ ').replace('cachly:lesson:best:', '๐ก ').replace('cachly:idx:', '๐ ');
|
|
567
|
+
const scorePart = r.matchType === 'hybrid'
|
|
568
|
+
? `BM25: ${(r.bm25Score ?? 0).toFixed(2)}, sem: ${((r.semScore ?? 0) * 100).toFixed(0)}%, ๐ hybrid`
|
|
569
|
+
: r.matchType === 'semantic'
|
|
570
|
+
? `sem: ${((r.semScore ?? 0) * 100).toFixed(0)}%, ๐ฏ semantic`
|
|
571
|
+
: `BM25: ${(r.bm25Score ?? 0).toFixed(2)}, matched: ${r.matchedWords?.join(', ')}`;
|
|
572
|
+
const preview = r.content.slice(0, 400).replace(/\n/g, ' ');
|
|
573
|
+
lines.push(`**${label}** _(${scorePart})_`);
|
|
574
|
+
lines.push(`> ${preview}${r.content.length > 400 ? 'โฆ' : ''}\n`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
468
579
|
lines.push(`โ ๏ธ No matches found for: "${query}"`);
|
|
469
580
|
// Did-You-Mean: find nearest token in index vocab
|
|
470
581
|
const queryTokens = tokenize(query);
|
|
@@ -780,6 +891,29 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
780
891
|
}
|
|
781
892
|
// Brain health
|
|
782
893
|
lines.push(`๐ **Brain:** ${lessons.length} lessons ยท ${ctxCount} context entries`, '');
|
|
894
|
+
// โโ ๐ Proven Laws โ auto-crystallized lessons (quick win 3) โโโโโโโโโโโโโ
|
|
895
|
+
// A lesson that has proven itself (recalled โฅ CRYSTALLIZE_RECALLS times) or
|
|
896
|
+
// was explicitly pinned becomes a "law" โ surfaced at the top of every
|
|
897
|
+
// briefing so the most battle-tested knowledge is never buried. This is
|
|
898
|
+
// the today-safe version of swarm crystallization: enough confirmations
|
|
899
|
+
// auto-promote a lesson without any manual step.
|
|
900
|
+
{
|
|
901
|
+
const CRYSTALLIZE_RECALLS = 5;
|
|
902
|
+
const laws = lessons
|
|
903
|
+
.filter(l => l.outcome === 'success' && (l.pinned === true || (l.recall_count ?? 0) >= CRYSTALLIZE_RECALLS))
|
|
904
|
+
.sort((a, b) => (b.recall_count ?? 0) - (a.recall_count ?? 0))
|
|
905
|
+
.slice(0, 5);
|
|
906
|
+
if (laws.length > 0) {
|
|
907
|
+
lines.push(`๐ **Proven Laws** (auto-crystallized โ your most-trusted knowledge):`);
|
|
908
|
+
for (const l of laws) {
|
|
909
|
+
const sev = l.severity === 'critical' ? '๐ด' : l.severity === 'major' ? '๐ก' : '';
|
|
910
|
+
const rc = l.recall_count ?? 0;
|
|
911
|
+
const why = l.pinned === true ? 'pinned' : `recalled ${rc}ร`;
|
|
912
|
+
lines.push(` ๐${sev} \`${l.topic}\` _(${why})_ โ ${l.what_worked.slice(0, 100)}`);
|
|
913
|
+
}
|
|
914
|
+
lines.push('');
|
|
915
|
+
}
|
|
916
|
+
}
|
|
783
917
|
// โโ Layer 7: MCM Domain Coverage Map โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
784
918
|
if (lessons.length >= 3) {
|
|
785
919
|
const domainMap = new Map();
|
|
@@ -882,8 +1016,14 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
882
1016
|
for (const l of topRecalled) {
|
|
883
1017
|
const emoji = l.outcome === 'success' ? 'โ
' : l.outcome === 'partial' ? 'โ ๏ธ' : 'โ';
|
|
884
1018
|
const sev = l.severity === 'critical' ? '๐ด' : l.severity === 'major' ? '๐ก' : '';
|
|
885
|
-
const
|
|
886
|
-
|
|
1019
|
+
const rc = l.recall_count ?? 0;
|
|
1020
|
+
const la = l.authors
|
|
1021
|
+
?? (l.author ? [l.author] : []);
|
|
1022
|
+
const nAuthors = new Set(la.filter(Boolean)).size;
|
|
1023
|
+
const trust = (nAuthors >= 2 && rc >= 5) || rc >= 10 ? ' ๐'
|
|
1024
|
+
: nAuthors >= 2 || rc >= 5 ? ' โ
' : '';
|
|
1025
|
+
const rct = rc > 1 ? ` _(${rc}ร recalled)_` : '';
|
|
1026
|
+
lines.push(` ${emoji}${sev}${trust} \`${l.topic}\`${rct} โ ${l.what_worked.slice(0, 100)}`);
|
|
887
1027
|
}
|
|
888
1028
|
lines.push('');
|
|
889
1029
|
}
|
|
@@ -949,25 +1089,59 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
949
1089
|
lines.push('');
|
|
950
1090
|
}
|
|
951
1091
|
// โโ ๐ฎ Predictive Pre-Warning โ intent-based danger detection โโโโโโโโโโโโ
|
|
952
|
-
// Fires BEFORE work starts.
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1092
|
+
// Fires BEFORE work starts. Uses explicit focus when given; otherwise
|
|
1093
|
+
// derives the likely work area from the last session's changed files +
|
|
1094
|
+
// summary (you usually keep working where you left off). This makes the
|
|
1095
|
+
// warning fire even when the caller forgets to pass `focus`.
|
|
1096
|
+
{
|
|
1097
|
+
// Common path noise / extensions to drop when deriving terms from files.
|
|
1098
|
+
const PATH_NOISE = new Set([
|
|
1099
|
+
'src', 'lib', 'test', 'tests', 'dist', 'index', 'main', 'app',
|
|
1100
|
+
'internal', 'pkg', 'cmd', 'node_modules', 'components', 'utils',
|
|
1101
|
+
'ts', 'tsx', 'js', 'jsx', 'go', 'py', 'rs', 'java', 'json', 'yaml', 'yml',
|
|
1102
|
+
]);
|
|
1103
|
+
const deriveTermsFromFiles = (files) => {
|
|
1104
|
+
const terms = new Set();
|
|
1105
|
+
for (const f of files) {
|
|
1106
|
+
for (const seg of f.toLowerCase().split(/[/\\._-]/)) {
|
|
1107
|
+
if (seg.length > 3 && !PATH_NOISE.has(seg))
|
|
1108
|
+
terms.add(seg);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return [...terms];
|
|
1112
|
+
};
|
|
1113
|
+
let warnTerms = focusTerms;
|
|
1114
|
+
let warnLabel = focus;
|
|
1115
|
+
let derived = false;
|
|
1116
|
+
if (warnTerms.length === 0 && lastSession) {
|
|
1117
|
+
const fromFiles = deriveTermsFromFiles(lastSession.files_changed ?? []);
|
|
1118
|
+
const fromSummary = (lastSession.summary ?? '').toLowerCase()
|
|
1119
|
+
.replace(/[^a-z0-9\s:_-]/g, ' ').split(/\s+/).filter(t => t.length > 3 && !PATH_NOISE.has(t));
|
|
1120
|
+
warnTerms = [...new Set([...fromFiles, ...fromSummary])];
|
|
1121
|
+
warnLabel = 'where you left off last session';
|
|
1122
|
+
derived = true;
|
|
1123
|
+
}
|
|
1124
|
+
if (warnTerms.length > 0) {
|
|
1125
|
+
const dangerLessons = lessons.filter(l => {
|
|
1126
|
+
if (l.outcome === 'success')
|
|
1127
|
+
return false;
|
|
1128
|
+
const topicCategory = l.topic.split(':')[0];
|
|
1129
|
+
return warnTerms.some(term => l.topic.toLowerCase().includes(term) ||
|
|
1130
|
+
topicCategory === term ||
|
|
1131
|
+
(l.tags ?? []).some((t) => t.toLowerCase() === term));
|
|
1132
|
+
});
|
|
1133
|
+
if (dangerLessons.length >= 1) {
|
|
1134
|
+
const headline = derived
|
|
1135
|
+
? ` You're likely to continue **${warnLabel}** โ ${dangerLessons.length} known pitfall${dangerLessons.length > 1 ? 's' : ''} there:`
|
|
1136
|
+
: ` Known pitfalls for **"${warnLabel}"** (${dangerLessons.length} past failure${dangerLessons.length > 1 ? 's' : ''}):`;
|
|
1137
|
+
const warning = [
|
|
1138
|
+
`๐จ **PRE-WARNING** โ Read this BEFORE starting:`,
|
|
1139
|
+
headline,
|
|
1140
|
+
...dangerLessons.slice(0, 3).map(l => ` โ \`${l.topic}\` โ ${(l.what_failed ?? l.what_worked).slice(0, 80)}`),
|
|
1141
|
+
'',
|
|
1142
|
+
];
|
|
1143
|
+
lines.splice(2, 0, ...warning); // after '๐ง **Session Briefing**' + empty line
|
|
1144
|
+
}
|
|
971
1145
|
}
|
|
972
1146
|
}
|
|
973
1147
|
// โโ ๐ฅ Team Telepathy โ what teammates learned this week โโโโโโโโโโโโโโโโโ
|