@cohortapp/agent-sdk 2.3.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/.claude/commands/init-agent.md +104 -0
- package/.claude/commands/init-maestro.md +1187 -0
- package/.claude/settings.json +161 -0
- package/.env.example +216 -0
- package/README.md +632 -0
- package/agents/browser-operator/agent.md +52 -0
- package/agents/calendar-ops/agent.md +50 -0
- package/agents/communications/agent.md +96 -0
- package/agents/decision-log/agent.md +65 -0
- package/agents/desktop-operator/agent.md +59 -0
- package/agents/gmail-operator/agent.md +62 -0
- package/agents/inbound-dispatcher/agent.md +66 -0
- package/agents/inbox-processor/agent.md +39 -0
- package/agents/pmo-execution/agent.md +60 -0
- package/agents/session-spawner/agent.md +64 -0
- package/agents/slack-operator/agent.md +60 -0
- package/agents/whatsapp-operator/agent.md +60 -0
- package/agents/workflow-automation/agent.md +61 -0
- package/archetypes/altitudes/c-suite.yaml +58 -0
- package/archetypes/altitudes/founder.yaml +68 -0
- package/archetypes/altitudes/senior-manager.yaml +63 -0
- package/archetypes/altitudes/svp.yaml +60 -0
- package/archetypes/altitudes/vp.yaml +50 -0
- package/archetypes/archetype.schema.json +77 -0
- package/archetypes/base.yaml +47 -0
- package/archetypes/capabilities/commercial-leader.yaml +159 -0
- package/archetypes/capabilities/compliance-officer.yaml +159 -0
- package/archetypes/capabilities/executive-operator.yaml +169 -0
- package/archetypes/capabilities/finance-leader.yaml +162 -0
- package/archetypes/capabilities/operations-leader.yaml +154 -0
- package/archetypes/capabilities/product-leader.yaml +148 -0
- package/archetypes/capabilities/technical-leader.yaml +146 -0
- package/archetypes/functions/commercial-leader.yaml +62 -0
- package/archetypes/functions/compliance-officer.yaml +64 -0
- package/archetypes/functions/executive-operator.yaml +70 -0
- package/archetypes/functions/finance-leader.yaml +70 -0
- package/archetypes/functions/operations-leader.yaml +62 -0
- package/archetypes/functions/product-leader.yaml +61 -0
- package/archetypes/functions/technical-leader.yaml +57 -0
- package/bin/cohort-mcp.mjs +81 -0
- package/bin/maestro.mjs +3516 -0
- package/bin/maestro.test.mjs +1015 -0
- package/desktop-control/README.md +56 -0
- package/desktop-control/app-profiles/gmail.yaml +120 -0
- package/desktop-control/app-profiles/slack.yaml +315 -0
- package/desktop-control/app-profiles/whatsapp.yaml +107 -0
- package/docs/architecture/agent-topology.md +2239 -0
- package/docs/architecture/archetype-agent-factory.md +110 -0
- package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
- package/docs/architecture/continuous-monitoring.md +221 -0
- package/docs/architecture/mcp-capability-map.md +585 -0
- package/docs/architecture/system-architecture.md +1272 -0
- package/docs/company-context/README.md +40 -0
- package/docs/guides/agent-persona-setup.md +600 -0
- package/docs/guides/agents-observe-setup.md +64 -0
- package/docs/guides/billing-console-keys.md +88 -0
- package/docs/guides/ccxray-diagnostics.md +65 -0
- package/docs/guides/channel-bus.md +127 -0
- package/docs/guides/claude-mem-setup.md +79 -0
- package/docs/guides/claude-pace-setup.md +56 -0
- package/docs/guides/claudraband-sessions.md +98 -0
- package/docs/guides/clawteam-swarm.md +116 -0
- package/docs/guides/code-review-graph-setup.md +86 -0
- package/docs/guides/email-setup.md +431 -0
- package/docs/guides/mac-mini.md +119 -0
- package/docs/guides/media-generation-setup.md +349 -0
- package/docs/guides/model-routing.md +162 -0
- package/docs/guides/observability-otel.md +265 -0
- package/docs/guides/org-onboarding.md +132 -0
- package/docs/guides/outbound-governance-setup.md +437 -0
- package/docs/guides/pdf-generation-setup.md +315 -0
- package/docs/guides/poller-daemon-setup.md +563 -0
- package/docs/guides/rag-context-setup.md +459 -0
- package/docs/guides/self-optimization-pattern.md +82 -0
- package/docs/guides/setup-wizard.md +178 -0
- package/docs/guides/slack-setup.md +350 -0
- package/docs/guides/telegram-setup.md +227 -0
- package/docs/guides/twilio-subaccounts-setup.md +223 -0
- package/docs/guides/verification.md +128 -0
- package/docs/guides/voice-mode.md +188 -0
- package/docs/guides/voice-sms-setup.md +698 -0
- package/docs/guides/webhook-relay-setup.md +349 -0
- package/docs/guides/whatsapp-setup.md +288 -0
- package/docs/prompts/board-pack-cover-template.md +36 -0
- package/docs/prompts/decision-recommendation-template.md +88 -0
- package/docs/prompts/followup-message-template.md +141 -0
- package/docs/prompts/investor-letter-template.md +52 -0
- package/docs/prompts/morning-brief-template.md +82 -0
- package/docs/prompts/presentation-template.md +58 -0
- package/docs/prompts/weekly-strategic-memo-template.md +104 -0
- package/docs/research/hallucinated-tool-output-investigation.md +151 -0
- package/docs/runbooks/backup-restore.md +205 -0
- package/docs/runbooks/cohort-cutover.md +129 -0
- package/docs/runbooks/fleet-operations.md +200 -0
- package/docs/runbooks/incident-response.md +226 -0
- package/docs/runbooks/mac-mini-bootstrap.md +431 -0
- package/docs/runbooks/perpetual-operations.md +509 -0
- package/docs/runbooks/recovery-and-failover.md +260 -0
- package/framework-features.json +267 -0
- package/ingest/README.md +87 -0
- package/lib/action-executor.js +689 -0
- package/lib/action-executor.test.mjs +871 -0
- package/lib/agent-root.mjs +37 -0
- package/lib/archetype.mjs +236 -0
- package/lib/archetype.test.mjs +132 -0
- package/lib/autonomy.mjs +114 -0
- package/lib/autonomy.test.mjs +66 -0
- package/lib/backlog.mjs +358 -0
- package/lib/backlog.test.mjs +266 -0
- package/lib/budget-guard.mjs +279 -0
- package/lib/budget-guard.test.mjs +291 -0
- package/lib/cadence-bus-schedule.test.mjs +194 -0
- package/lib/cadence-bus.mjs +1120 -0
- package/lib/cadence-bus.test.mjs +720 -0
- package/lib/cadences.mjs +205 -0
- package/lib/cadences.test.mjs +125 -0
- package/lib/capability.mjs +154 -0
- package/lib/capability.test.mjs +78 -0
- package/lib/channels/base-adapter.mjs +719 -0
- package/lib/channels/base-adapter.test.mjs +590 -0
- package/lib/channels/channel.mjs +128 -0
- package/lib/channels/channels.test.mjs +371 -0
- package/lib/channels/contract.mjs +215 -0
- package/lib/channels/contract.test.mjs +137 -0
- package/lib/channels/conversation-resolver.mjs +95 -0
- package/lib/channels/gmail/adapter.mjs +87 -0
- package/lib/channels/inbox-item.mjs +255 -0
- package/lib/channels/inbox-item.test.mjs +335 -0
- package/lib/channels/index.mjs +94 -0
- package/lib/channels/orgmail/adapter.mjs +353 -0
- package/lib/channels/orgmail/adapter.test.mjs +311 -0
- package/lib/channels/pairing.mjs +363 -0
- package/lib/channels/pairing.test.mjs +270 -0
- package/lib/channels/registry.mjs +164 -0
- package/lib/channels/slack/adapter.mjs +317 -0
- package/lib/channels/slack-adapter.test.mjs +212 -0
- package/lib/channels/sms/adapter.mjs +43 -0
- package/lib/channels/telegram/adapter.mjs +432 -0
- package/lib/channels/telegram-adapter.test.mjs +306 -0
- package/lib/channels/voice/adapter.mjs +301 -0
- package/lib/channels/voice/adapter.test.mjs +278 -0
- package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
- package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
- package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
- package/lib/charter.mjs +256 -0
- package/lib/charter.test.mjs +89 -0
- package/lib/claude-bin.mjs +134 -0
- package/lib/claude-bin.test.mjs +75 -0
- package/lib/collective/capture.mjs +185 -0
- package/lib/collective/capture.test.mjs +121 -0
- package/lib/collective/cards.mjs +201 -0
- package/lib/collective/cards.test.mjs +114 -0
- package/lib/collective/config.mjs +186 -0
- package/lib/collective/config.test.mjs +123 -0
- package/lib/collective/global-config.mjs +113 -0
- package/lib/collective/global-config.test.mjs +75 -0
- package/lib/collective/presence.mjs +201 -0
- package/lib/collective/presence.test.mjs +95 -0
- package/lib/collective/recall.mjs +215 -0
- package/lib/collective/recall.test.mjs +116 -0
- package/lib/comms/send-gate.mjs +554 -0
- package/lib/comms/send-gate.test.mjs +577 -0
- package/lib/comms.mjs +67 -0
- package/lib/comms.test.mjs +41 -0
- package/lib/diagnostics/alerts.mjs +424 -0
- package/lib/diagnostics/alerts.test.mjs +318 -0
- package/lib/diagnostics/backup-freshness.mjs +188 -0
- package/lib/diagnostics/backup-freshness.test.mjs +185 -0
- package/lib/diagnostics/counters.mjs +269 -0
- package/lib/diagnostics/counters.test.mjs +206 -0
- package/lib/diagnostics/events.mjs +188 -0
- package/lib/diagnostics/events.test.mjs +290 -0
- package/lib/diagnostics/otel.mjs +237 -0
- package/lib/diagnostics/otel.test.mjs +196 -0
- package/lib/diagnostics/trace.mjs +216 -0
- package/lib/diagnostics/trace.test.mjs +251 -0
- package/lib/env-compat.mjs +74 -0
- package/lib/env-compat.test.mjs +104 -0
- package/lib/feature-init.mjs +331 -0
- package/lib/fs-atomic.mjs +112 -0
- package/lib/fs-atomic.test.mjs +72 -0
- package/lib/fs-ownership.mjs +111 -0
- package/lib/fs-ownership.test.mjs +158 -0
- package/lib/hooks/bus.mjs +347 -0
- package/lib/hooks/bus.test.mjs +387 -0
- package/lib/index.js +16 -0
- package/lib/learning/config.mjs +106 -0
- package/lib/learning/config.test.mjs +75 -0
- package/lib/learning/counters.mjs +156 -0
- package/lib/learning/counters.test.mjs +69 -0
- package/lib/learning/curator-consolidate.test.mjs +238 -0
- package/lib/learning/curator.mjs +453 -0
- package/lib/learning/curator.test.mjs +106 -0
- package/lib/learning/log.mjs +40 -0
- package/lib/learning/reflect.mjs +534 -0
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.mjs +352 -0
- package/lib/learning/session-index.test.mjs +125 -0
- package/lib/learning/skill-writer.mjs +474 -0
- package/lib/learning/skill-writer.test.mjs +210 -0
- package/lib/mcp/server.mjs +328 -0
- package/lib/mcp/server.test.mjs +400 -0
- package/lib/model-router/auth-profiles.mjs +758 -0
- package/lib/model-router/auth-profiles.test.mjs +580 -0
- package/lib/model-router/catalog/anthropic.yaml +153 -0
- package/lib/model-router/catalog/deepseek.yaml +86 -0
- package/lib/model-router/catalog/moonshot.yaml +81 -0
- package/lib/model-router/catalog/qwen.yaml +114 -0
- package/lib/model-router/catalog.mjs +925 -0
- package/lib/model-router/catalog.test.mjs +385 -0
- package/lib/model-router/economics.mjs +564 -0
- package/lib/model-router/economics.test.mjs +344 -0
- package/lib/model-router/failover.mjs +298 -0
- package/lib/model-router/failover.test.mjs +439 -0
- package/lib/model-router/health.mjs +453 -0
- package/lib/model-router/health.test.mjs +338 -0
- package/lib/model-router/integration-coverage.test.mjs +829 -0
- package/lib/model-router/integration.test.mjs +564 -0
- package/lib/model-router/ledger.mjs +402 -0
- package/lib/model-router/ledger.test.mjs +382 -0
- package/lib/model-router/llm-task.mjs +515 -0
- package/lib/model-router/llm-task.test.mjs +392 -0
- package/lib/model-router/org-credentials.mjs +260 -0
- package/lib/model-router/org-credentials.test.mjs +265 -0
- package/lib/model-router/pricing-refresh.mjs +463 -0
- package/lib/model-router/pricing-refresh.test.mjs +286 -0
- package/lib/model-router/reconcile.mjs +429 -0
- package/lib/model-router/reconcile.test.mjs +316 -0
- package/lib/model-router/repair.mjs +471 -0
- package/lib/model-router/repair.test.mjs +180 -0
- package/lib/model-router/resolve.mjs +1206 -0
- package/lib/model-router/spawn.mjs +497 -0
- package/lib/model-router/spawn.test.mjs +425 -0
- package/lib/model-router/taxonomy.mjs +893 -0
- package/lib/model-router/taxonomy.test.mjs +410 -0
- package/lib/model-router.mjs +677 -0
- package/lib/model-router.test.mjs +907 -0
- package/lib/org/activity.mjs +211 -0
- package/lib/org/activity.test.mjs +134 -0
- package/lib/org/approvals.mjs +448 -0
- package/lib/org/approvals.test.mjs +216 -0
- package/lib/org/awareness.mjs +222 -0
- package/lib/org/awareness.test.mjs +159 -0
- package/lib/org/board.mjs +229 -0
- package/lib/org/board.test.mjs +177 -0
- package/lib/org/bootstrap-context.mjs +169 -0
- package/lib/org/bootstrap-context.test.mjs +153 -0
- package/lib/org/client.mjs +1628 -0
- package/lib/org/client.test.mjs +1107 -0
- package/lib/org/cohort-client.mjs +67 -0
- package/lib/org/cohort-client.test.mjs +126 -0
- package/lib/org/cost-sync.mjs +227 -0
- package/lib/org/cost-sync.test.mjs +153 -0
- package/lib/org/doctor.mjs +212 -0
- package/lib/org/doctor.test.mjs +212 -0
- package/lib/org/handoff.mjs +293 -0
- package/lib/org/handoff.test.mjs +269 -0
- package/lib/org/integration-tools.mjs +182 -0
- package/lib/org/integration-tools.test.mjs +160 -0
- package/lib/org/keys.mjs +131 -0
- package/lib/org/keys.test.mjs +92 -0
- package/lib/org/knowledge.mjs +463 -0
- package/lib/org/knowledge.test.mjs +319 -0
- package/lib/org/leases.mjs +335 -0
- package/lib/org/leases.test.mjs +235 -0
- package/lib/org/mesh-integration.test.mjs +127 -0
- package/lib/org/mesh.mjs +459 -0
- package/lib/org/mesh.test.mjs +345 -0
- package/lib/org/messaging.mjs +503 -0
- package/lib/org/messaging.test.mjs +238 -0
- package/lib/org/policy.mjs +345 -0
- package/lib/org/policy.test.mjs +237 -0
- package/lib/org/protocol.checksum +1 -0
- package/lib/org/protocol.checksum.test.mjs +90 -0
- package/lib/org/protocol.mjs +967 -0
- package/lib/org/protocol.test.mjs +264 -0
- package/lib/org/registry.mjs +194 -0
- package/lib/org/registry.test.mjs +100 -0
- package/lib/org/tool-surface-integration.test.mjs +120 -0
- package/lib/org/tool-surface.mjs +2535 -0
- package/lib/org/tool-surface.test.mjs +589 -0
- package/lib/org/ui-parity.mjs +3236 -0
- package/lib/org/ui-parity.test.mjs +348 -0
- package/lib/org/verify.mjs +176 -0
- package/lib/org/verify.test.mjs +194 -0
- package/lib/rag/embed.mjs +188 -0
- package/lib/rag/indexer.mjs +425 -0
- package/lib/rag/rag.test.mjs +505 -0
- package/lib/rag/search.mjs +475 -0
- package/lib/rate-guard.mjs +246 -0
- package/lib/rate-guard.test.mjs +201 -0
- package/lib/render.mjs +112 -0
- package/lib/render.test.mjs +68 -0
- package/lib/resource-governor.mjs +297 -0
- package/lib/resource-governor.test.mjs +262 -0
- package/lib/scheduling/dynamic-jobs.mjs +675 -0
- package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
- package/lib/scheduling/jitter.mjs +0 -0
- package/lib/scheduling/jitter.test.mjs +140 -0
- package/lib/secrets/broker.mjs +315 -0
- package/lib/secrets/broker.test.mjs +280 -0
- package/lib/secrets/providers.mjs +461 -0
- package/lib/secrets/providers.test.mjs +274 -0
- package/lib/security/audit-engine.mjs +684 -0
- package/lib/security/audit-engine.test.mjs +389 -0
- package/lib/security/coerce-args.mjs +552 -0
- package/lib/security/coerce-args.test.mjs +281 -0
- package/lib/security/dangerous-tools.mjs +97 -0
- package/lib/security/dangerous-tools.test.mjs +68 -0
- package/lib/security/external-content.mjs +145 -0
- package/lib/security/external-content.test.mjs +67 -0
- package/lib/security/redact.mjs +592 -0
- package/lib/security/redact.test.mjs +441 -0
- package/lib/security/secret-equal.mjs +73 -0
- package/lib/security/secret-equal.test.mjs +55 -0
- package/lib/session-permissions.mjs +101 -0
- package/lib/session-permissions.test.mjs +100 -0
- package/lib/setup/claude-probe.mjs +74 -0
- package/lib/setup/completeness.mjs +175 -0
- package/lib/setup/completeness.test.mjs +110 -0
- package/lib/setup/context-pack.mjs +173 -0
- package/lib/setup/context-pack.test.mjs +89 -0
- package/lib/setup/enrich.mjs +277 -0
- package/lib/setup/enrich.test.mjs +115 -0
- package/lib/setup/enroll-from-cohort.mjs +441 -0
- package/lib/setup/enroll-from-cohort.test.mjs +233 -0
- package/lib/setup/integration.test.mjs +162 -0
- package/lib/setup/io.mjs +360 -0
- package/lib/setup/io.test.mjs +77 -0
- package/lib/setup/run-generator.mjs +81 -0
- package/lib/setup/runner.mjs +244 -0
- package/lib/setup/runner.test.mjs +132 -0
- package/lib/setup/sections/comms.mjs +173 -0
- package/lib/setup/sections/company.mjs +120 -0
- package/lib/setup/sections/enrich.mjs +138 -0
- package/lib/setup/sections/identity.mjs +182 -0
- package/lib/setup/sections/identity.test.mjs +140 -0
- package/lib/setup/sections/learning.mjs +153 -0
- package/lib/setup/sections/learning.test.mjs +81 -0
- package/lib/setup/sections/messaging.mjs +219 -0
- package/lib/setup/sections/messaging.test.mjs +127 -0
- package/lib/setup/sections/model.mjs +102 -0
- package/lib/setup/sections/operating-model.mjs +78 -0
- package/lib/setup/sections/org.mjs +475 -0
- package/lib/setup/sections/org.test.mjs +313 -0
- package/lib/setup/sections/orgmail.mjs +173 -0
- package/lib/setup/sections/orgmail.test.mjs +118 -0
- package/lib/setup/sections/recovery.mjs +159 -0
- package/lib/setup/sections/recovery.test.mjs +98 -0
- package/lib/setup/sections/tools.mjs +132 -0
- package/lib/setup/sections/verify.mjs +97 -0
- package/lib/setup/sot.mjs +205 -0
- package/lib/setup/sot.test.mjs +81 -0
- package/lib/setup/state.mjs +151 -0
- package/lib/setup/state.test.mjs +92 -0
- package/lib/singleton.js +229 -0
- package/lib/singleton.test.mjs +135 -0
- package/lib/telemetry/alerts.mjs +216 -0
- package/lib/telemetry/alerts.test.mjs +109 -0
- package/lib/telemetry/collect.mjs +512 -0
- package/lib/telemetry/collect.test.mjs +202 -0
- package/lib/tool-definitions-integration.test.mjs +83 -0
- package/lib/tool-definitions.js +738 -0
- package/lib/tool-definitions.test.mjs +437 -0
- package/lib/util/fetch-timeout.mjs +136 -0
- package/lib/util/fetch-timeout.test.mjs +202 -0
- package/lib/util/reconnect.mjs +343 -0
- package/lib/util/reconnect.test.mjs +369 -0
- package/lib/util/unhandled.mjs +205 -0
- package/lib/util/unhandled.test.mjs +216 -0
- package/lib/voice/context-loader.mjs +466 -0
- package/lib/voice/index.mjs +100 -0
- package/lib/voice/openai-realtime.mjs +510 -0
- package/lib/voice/outbound.mjs +542 -0
- package/lib/voice/outbound.test.mjs +69 -0
- package/lib/voice/post-call-brief.mjs +428 -0
- package/lib/voice/provider.mjs +52 -0
- package/lib/voice/session-rotation.mjs +257 -0
- package/lib/voice/stt.mjs +161 -0
- package/lib/voice/stt.test.mjs +226 -0
- package/lib/voice/tool-bridge.mjs +370 -0
- package/lib/voice/tts.mjs +104 -0
- package/lib/voice/twilio-sip-bridge.mjs +288 -0
- package/lib/voice/voice.test.mjs +990 -0
- package/mcp/README.md +80 -0
- package/package.json +151 -0
- package/plugins/maestro-skills/plugin.json +139 -0
- package/plugins/maestro-skills/skills/agents-observe.md +110 -0
- package/plugins/maestro-skills/skills/board-deck.md +68 -0
- package/plugins/maestro-skills/skills/books-close.md +77 -0
- package/plugins/maestro-skills/skills/brand-steward.md +121 -0
- package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
- package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
- package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
- package/plugins/maestro-skills/skills/claude-pace.md +61 -0
- package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
- package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
- package/plugins/maestro-skills/skills/decision-brief.md +89 -0
- package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
- package/plugins/maestro-skills/skills/draft-comms.md +84 -0
- package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
- package/plugins/maestro-skills/skills/files-find.md +65 -0
- package/plugins/maestro-skills/skills/generative-ui.md +228 -0
- package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
- package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
- package/plugins/maestro-skills/skills/mail-triage.md +86 -0
- package/plugins/maestro-skills/skills/morning-brief.md +54 -0
- package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
- package/plugins/maestro-skills/skills/org-board.md +133 -0
- package/plugins/maestro-skills/skills/org-credential.md +68 -0
- package/plugins/maestro-skills/skills/org-recall.md +81 -0
- package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
- package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
- package/plugins/maestro-skills/skills/router-why.md +78 -0
- package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
- package/plugins/maestro-skills/skills/session-search.md +71 -0
- package/plugins/maestro-skills/skills/set-reminder.md +93 -0
- package/plugins/maestro-skills/skills/slack-followup.md +64 -0
- package/plugins/maestro-skills/skills/team-activity.md +86 -0
- package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
- package/policies/action-classification.yaml +114 -0
- package/policies/ai-disclosure.yaml +294 -0
- package/policies/communication-style.md +139 -0
- package/policies/information-barriers.yaml +118 -0
- package/policies/prompt-injection-defence.yaml +138 -0
- package/public/assets/icon-dark.png +0 -0
- package/public/assets/icon-dark.svg +9 -0
- package/public/assets/icon-light.svg +9 -0
- package/public/assets/logo-dark.svg +15 -0
- package/public/assets/logo-light.svg +15 -0
- package/scaffold/.mcp.json +7 -0
- package/scaffold/CLAUDE.md +368 -0
- package/scaffold/config/agent.json +55 -0
- package/scaffold/config/agent.ts +76 -0
- package/scaffold/config/agent.ts.example +89 -0
- package/scaffold/config/alerts.yaml +23 -0
- package/scaffold/config/allowlist.yaml.example +25 -0
- package/scaffold/config/caller-id-map.yaml +46 -0
- package/scaffold/config/collective.yaml +49 -0
- package/scaffold/config/company.json +20 -0
- package/scaffold/config/known-agents.json +6 -0
- package/scaffold/config/learning.yaml +55 -0
- package/scaffold/config/model-routing.yaml.example +104 -0
- package/scaffold/config/org.yaml +25 -0
- package/scaffold/config/orgmail.yaml.example +19 -0
- package/scaffold/config/recovery.yaml +72 -0
- package/scaffold/config/secrets.yaml +27 -0
- package/scaffold/config/slack.yaml.example +35 -0
- package/scaffold/config/telegram.yaml.example +38 -0
- package/scaffold/config/voice.yaml.example +89 -0
- package/scaffold/config/whatsapp.yaml.example +39 -0
- package/schedules/README.md +49 -0
- package/schedules/triggers/backlog-executor.md +102 -0
- package/schedules/triggers/brand-steward.md +72 -0
- package/schedules/triggers/daily-evening-wrap.md +159 -0
- package/schedules/triggers/daily-midday-sweep.md +58 -0
- package/schedules/triggers/daily-morning-brief.md +55 -0
- package/schedules/triggers/directory-hygiene.md +81 -0
- package/schedules/triggers/dynamic-jobs.md +40 -0
- package/schedules/triggers/inbox-processor.md +115 -0
- package/schedules/triggers/meeting-action-capture.md +60 -0
- package/schedules/triggers/meeting-prep.md +69 -0
- package/schedules/triggers/messaging-inbound.md +50 -0
- package/schedules/triggers/org-pulse.md +24 -0
- package/schedules/triggers/quarterly-self-assessment.md +54 -0
- package/schedules/triggers/weekly-engineering-health.md +37 -0
- package/schedules/triggers/weekly-execution.md +65 -0
- package/schedules/triggers/weekly-hiring.md +53 -0
- package/schedules/triggers/weekly-priorities.md +38 -0
- package/schedules/triggers/weekly-strategic-memo.md +124 -0
- package/scripts/archive-email.sh +55 -0
- package/scripts/cadence/cadence-status.mjs +36 -0
- package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
- package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
- package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
- package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
- package/scripts/ci/check-docs-accuracy.mjs +493 -0
- package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
- package/scripts/ci/check-exports-exist.mjs +140 -0
- package/scripts/ci/check-files-exist.mjs +107 -0
- package/scripts/ci/check-no-build-artifacts.mjs +111 -0
- package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
- package/scripts/ci/check-no-confidential.mjs +198 -0
- package/scripts/ci/check-no-conflict-markers.mjs +169 -0
- package/scripts/ci/check-no-residual-identity.mjs +163 -0
- package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
- package/scripts/ci/check-tarball-fidelity.mjs +205 -0
- package/scripts/ci/check-unresolved-tokens.mjs +83 -0
- package/scripts/ci/check.mjs +109 -0
- package/scripts/ci/check.test.mjs +194 -0
- package/scripts/ci/run-coverage.mjs +82 -0
- package/scripts/ci/run-tests.mjs +71 -0
- package/scripts/cloud-relay/README.md +59 -0
- package/scripts/cloud-relay/index.mjs +233 -0
- package/scripts/cloud-relay/package.json +15 -0
- package/scripts/cloud-relay/railway.json +13 -0
- package/scripts/cloud-relay/voice/README.md +94 -0
- package/scripts/cloud-relay/voice/package-lock.json +39 -0
- package/scripts/cloud-relay/voice/package.json +16 -0
- package/scripts/cloud-relay/voice/railway.json +13 -0
- package/scripts/cloud-relay/voice/server.mjs +532 -0
- package/scripts/collective/hook-runner.mjs +211 -0
- package/scripts/collective/hook-runner.test.mjs +90 -0
- package/scripts/collective/org-pulse.mjs +72 -0
- package/scripts/collective/org-sync.mjs +61 -0
- package/scripts/collective/recall.mjs +45 -0
- package/scripts/collective/who.mjs +30 -0
- package/scripts/comms-monitor.sh +288 -0
- package/scripts/configure-whatsapp-sandbox.sh +201 -0
- package/scripts/continuous-monitor.sh +91 -0
- package/scripts/cost/fleet-digest.mjs +407 -0
- package/scripts/cost/fleet-digest.test.mjs +207 -0
- package/scripts/cost/track-claude-usage.mjs +169 -0
- package/scripts/daemon/agent-daemon.mjs +989 -0
- package/scripts/daemon/agent-daemon.test.mjs +525 -0
- package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
- package/scripts/daemon/cadence-consumer.mjs +1080 -0
- package/scripts/daemon/cadence-consumer.test.mjs +770 -0
- package/scripts/daemon/cadence-handlers.mjs +1121 -0
- package/scripts/daemon/cadence-handlers.test.mjs +617 -0
- package/scripts/daemon/classifier.mjs +704 -0
- package/scripts/daemon/classifier.test.mjs +238 -0
- package/scripts/daemon/classify-kind.mjs +54 -0
- package/scripts/daemon/classify-kind.test.mjs +40 -0
- package/scripts/daemon/context-compiler.mjs +605 -0
- package/scripts/daemon/context-compiler.test.mjs +300 -0
- package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
- package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
- package/scripts/daemon/dispatcher.mjs +1516 -0
- package/scripts/daemon/health.mjs +72 -0
- package/scripts/daemon/inbox-deferral.mjs +210 -0
- package/scripts/daemon/inbox-deferral.test.mjs +242 -0
- package/scripts/daemon/integration.test.mjs +149 -0
- package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
- package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
- package/scripts/daemon/launchd-wrapper.sh +91 -0
- package/scripts/daemon/lib/session-router.mjs +274 -0
- package/scripts/daemon/lib/session-router.test.mjs +295 -0
- package/scripts/daemon/maestro-daemon.mjs +275 -0
- package/scripts/daemon/prompt-builder.mjs +685 -0
- package/scripts/daemon/prompt-builder.test.mjs +213 -0
- package/scripts/daemon/responder.mjs +854 -0
- package/scripts/daemon/session-lock.mjs +721 -0
- package/scripts/daemon/session-lock.test.mjs +252 -0
- package/scripts/daemon/session-outcomes.mjs +640 -0
- package/scripts/daemon/session-outcomes.test.mjs +533 -0
- package/scripts/daemon/typing-registry.mjs +90 -0
- package/scripts/daemon/typing-registry.test.mjs +77 -0
- package/scripts/daemon/voice-webhook-server.mjs +804 -0
- package/scripts/decisions/capture-decision.mjs +116 -0
- package/scripts/disclosure_assessment.py +873 -0
- package/scripts/disclosure_boundaries.py +562 -0
- package/scripts/email-signature-principal.html +52 -0
- package/scripts/email-signature.html +60 -0
- package/scripts/email_quote_thread.py +167 -0
- package/scripts/email_thread_dedup.py +362 -0
- package/scripts/emergency-stop.sh +81 -0
- package/scripts/healthcheck.sh +116 -0
- package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
- package/scripts/hooks/block-mcp-slack-send.sh +7 -0
- package/scripts/hooks/post-action-log.sh +126 -0
- package/scripts/hooks/pre-send-audit.sh +174 -0
- package/scripts/hooks/pre-send-audit.test.mjs +215 -0
- package/scripts/hooks/session-end-log.sh +27 -0
- package/scripts/hooks/session-start-banner.sh +115 -0
- package/scripts/huddle/audio-bridge.mjs +664 -0
- package/scripts/huddle/boot-slack-cdp.sh +102 -0
- package/scripts/huddle/huddle-controller.mjs +942 -0
- package/scripts/huddle/huddle-server.mjs +1229 -0
- package/scripts/huddle/launch-slack.sh +232 -0
- package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
- package/scripts/huddle/package-lock.json +62 -0
- package/scripts/huddle/package.json +22 -0
- package/scripts/huddle/setup-audio.sh +239 -0
- package/scripts/huddle/start-call.mjs +318 -0
- package/scripts/huddle/test-pipeline.mjs +263 -0
- package/scripts/learning/consolidate-skills.mjs +72 -0
- package/scripts/learning/session-search.mjs +125 -0
- package/scripts/llm_email_dedup.py +442 -0
- package/scripts/local-triggers/generate-plists.sh +432 -0
- package/scripts/local-triggers/generate-plists.test.mjs +413 -0
- package/scripts/local-triggers/install-all.sh +49 -0
- package/scripts/local-triggers/plists/.gitkeep +0 -0
- package/scripts/local-triggers/run-trigger.sh +63 -0
- package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
- package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
- package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
- package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
- package/scripts/maintenance/backup-to-cloud.sh +124 -0
- package/scripts/maintenance/health-check.sh +377 -0
- package/scripts/media-generation/README.md +105 -0
- package/scripts/media-generation/gemini-image-client.mjs +173 -0
- package/scripts/media-generation/generate-assets.mjs +289 -0
- package/scripts/media-generation/veo-video-client.mjs +219 -0
- package/scripts/org/send-orgmail.mjs +227 -0
- package/scripts/outbound-dedup-cleanup.sh +43 -0
- package/scripts/outbound-dedup.sh +477 -0
- package/scripts/outbound_dedup.py +115 -0
- package/scripts/parse-voice-transcript.mjs +481 -0
- package/scripts/pdf-generation/README.md +63 -0
- package/scripts/pdf-generation/build-document.mjs +247 -0
- package/scripts/pdf-generation/templates/board-pack.latex +136 -0
- package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
- package/scripts/pdf-generation/templates/memo.latex +114 -0
- package/scripts/poll-slack-events.sh +35 -0
- package/scripts/poller/calendar-poller.mjs +12 -0
- package/scripts/poller/gmail-poller.mjs +192 -0
- package/scripts/poller/imap-client.mjs +289 -0
- package/scripts/poller/inbox-scan-poller.mjs +156 -0
- package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
- package/scripts/poller/index.mjs +73 -0
- package/scripts/poller/intra-session-check.mjs +285 -0
- package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
- package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
- package/scripts/poller/secondary-gmail-poller.mjs +132 -0
- package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
- package/scripts/poller/slack-poller.mjs +854 -0
- package/scripts/poller/slack-socket-mode.mjs +917 -0
- package/scripts/poller/slack-socket-mode.test.mjs +753 -0
- package/scripts/poller/trigger.mjs +75 -0
- package/scripts/poller/utils.mjs +371 -0
- package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
- package/scripts/poller/voice-poller.mjs +236 -0
- package/scripts/poller-launchd/install.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +40 -0
- package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
- package/scripts/post-interaction-indexer.py +1598 -0
- package/scripts/pre-draft-context.py +994 -0
- package/scripts/pre_draft_lookup.py +258 -0
- package/scripts/rag/build-index.mjs +47 -0
- package/scripts/rag/ingest.mjs +111 -0
- package/scripts/rag/search.mjs +119 -0
- package/scripts/rag-indexer.py +629 -0
- package/scripts/restore-from-backup.sh +248 -0
- package/scripts/restore-from-backup.test.mjs +178 -0
- package/scripts/resume-operations.sh +80 -0
- package/scripts/search-secondary-inbox.py +181 -0
- package/scripts/secondary-inbox-poller.py +437 -0
- package/scripts/self-optimization/compute-metrics.py +398 -0
- package/scripts/send-email-as-principal.py +369 -0
- package/scripts/send-email-threaded.py +392 -0
- package/scripts/send-email-with-attachment.py +377 -0
- package/scripts/send-email.sh +131 -0
- package/scripts/send-sms.sh +175 -0
- package/scripts/send-whatsapp.sh +292 -0
- package/scripts/session-start.sh +106 -0
- package/scripts/setup/boot-claude-session.sh +94 -0
- package/scripts/setup/configure-macos.sh +674 -0
- package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
- package/scripts/setup/configure-voice-tunnel.mjs +182 -0
- package/scripts/setup/generate-agent-env.mjs +92 -0
- package/scripts/setup/generate-agent-package-json.mjs +222 -0
- package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
- package/scripts/setup/generate-autonomy.mjs +60 -0
- package/scripts/setup/generate-backlog.mjs +101 -0
- package/scripts/setup/generate-cadences.mjs +92 -0
- package/scripts/setup/generate-capability.mjs +162 -0
- package/scripts/setup/generate-charter.mjs +76 -0
- package/scripts/setup/generate-comms.mjs +59 -0
- package/scripts/setup/generate-company.mjs +92 -0
- package/scripts/setup/init-agent.sh +547 -0
- package/scripts/setup/init-agent.test.mjs +151 -0
- package/scripts/setup/init-archetype.mjs +74 -0
- package/scripts/setup/init-backup.mjs +54 -0
- package/scripts/setup/init-cadence-bus.mjs +60 -0
- package/scripts/setup/init-channel-bus.mjs +46 -0
- package/scripts/setup/init-cost-tracking.mjs +45 -0
- package/scripts/setup/init-decision-capture.mjs +66 -0
- package/scripts/setup/init-known-agents.mjs +57 -0
- package/scripts/setup/init-learning.mjs +70 -0
- package/scripts/setup/init-memory-executive.mjs +45 -0
- package/scripts/setup/init-model-router.mjs +124 -0
- package/scripts/setup/init-rag.mjs +174 -0
- package/scripts/setup/init-session-router.mjs +38 -0
- package/scripts/setup/init-slack-socket-mode.mjs +260 -0
- package/scripts/setup/init-telegram.mjs +165 -0
- package/scripts/setup/init-voice-realtime.mjs +204 -0
- package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
- package/scripts/setup/install-dev-tools.sh +150 -0
- package/scripts/setup/lib/install-plist.mjs +95 -0
- package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
- package/scripts/setup/render-environment-yaml.mjs +133 -0
- package/scripts/slack-events-ctl.sh +177 -0
- package/scripts/slack-events-server.mjs +1045 -0
- package/scripts/slack-react.mjs +89 -0
- package/scripts/slack-responded.sh +232 -0
- package/scripts/slack-send.sh +287 -0
- package/scripts/slack-typing.mjs +196 -0
- package/scripts/slack-upload-v2.py +95 -0
- package/scripts/sms-handler.mjs +450 -0
- package/scripts/spawn-session.sh +120 -0
- package/scripts/sync-protocol.mjs +217 -0
- package/scripts/system-verify.sh +184 -0
- package/scripts/test-email-thread-dedup.py +239 -0
- package/scripts/test-information-barriers.py +484 -0
- package/scripts/test-llm-email-dedup.py +251 -0
- package/scripts/test-pre-draft-integration.py +203 -0
- package/scripts/test-rag-phase2.sh +442 -0
- package/scripts/test-rag-search.sh +251 -0
- package/scripts/test-voice-parser.mjs +316 -0
- package/scripts/user-context-search.py +659 -0
- package/scripts/validate_outbound.py +1504 -0
- package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
- package/scripts/watchdog/force-reboot.sh +157 -0
- package/scripts/watchdog/memory-watchdog.sh +473 -0
- package/scripts/whatsapp-handler.mjs +538 -0
- package/teams/desktop-operations.yaml +34 -0
- package/teams/executive-office.yaml +27 -0
- package/teams/legal-and-regulatory.yaml +24 -0
- package/teams/platform-and-engineering.yaml +23 -0
- package/teams/strategy-and-growth.yaml +29 -0
- package/workflows/continuous/backlog-executor.yaml +141 -0
- package/workflows/continuous/inbound-monitor.yaml +168 -0
- package/workflows/daily/applicant-triage.yaml +197 -0
- package/workflows/daily/comms-triage.yaml +80 -0
- package/workflows/daily/evening-wrap.yaml +105 -0
- package/workflows/daily/morning-brief.yaml +164 -0
- package/workflows/daily/slack-followup-sweep.yaml +87 -0
- package/workflows/event-driven/README.md +50 -0
- package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
- package/workflows/event-driven/pr-review.yaml +107 -0
- package/workflows/monthly/board-readiness.yaml +76 -0
- package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
- package/workflows/session-protocol.md +171 -0
- package/workflows/weekly/engineering-health.yaml +154 -0
- package/workflows/weekly/hiring-review.yaml +169 -0
- package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
- package/workflows/weekly/strategic-memo.yaml +79 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
rag-indexer.py — SQLite FTS5 indexer for per-user ring-fenced RAG (Phase 2)
|
|
4
|
+
|
|
5
|
+
Indexes all content sources into a SQLite FTS5 database for fast full-text search
|
|
6
|
+
with per-user access control (ring-fencing).
|
|
7
|
+
|
|
8
|
+
Sources indexed:
|
|
9
|
+
- memory/interactions/**/*.jsonl (Slack DMs, channels, emails — each line = one doc)
|
|
10
|
+
- knowledge/sources/*.yaml (public)
|
|
11
|
+
- knowledge/decisions/*.yaml (leadership+)
|
|
12
|
+
- knowledge/syntheses/*.yaml (public, except strategy-state.yaml = ceo)
|
|
13
|
+
- state/queues/*.yaml (leadership+)
|
|
14
|
+
|
|
15
|
+
Each document gets a user_scope:
|
|
16
|
+
- "public" — visible to all authenticated users
|
|
17
|
+
- "leadership" — visible to leadership + CEO
|
|
18
|
+
- "ceo" — CEO only
|
|
19
|
+
- "{user-slug}" — private to that specific user (DMs, emails)
|
|
20
|
+
|
|
21
|
+
Incremental indexing: tracks file modification times in state/rag/index-state.json.
|
|
22
|
+
Only re-indexes files that have changed since last run.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
python3 scripts/rag-indexer.py # incremental index
|
|
26
|
+
python3 scripts/rag-indexer.py --full # full re-index (drop + rebuild)
|
|
27
|
+
python3 scripts/rag-indexer.py --stats # show index statistics
|
|
28
|
+
python3 scripts/rag-indexer.py --db /path/to/db # custom DB path
|
|
29
|
+
|
|
30
|
+
Author: Northwind
|
|
31
|
+
Date: 2026-04-03
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import hashlib
|
|
36
|
+
import json
|
|
37
|
+
import os
|
|
38
|
+
import re
|
|
39
|
+
import sqlite3
|
|
40
|
+
import sys
|
|
41
|
+
import time
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
|
|
44
|
+
# ── Constants ──────────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
BASE_DIR = Path(os.environ.get("AGENT_DIR", str(Path(__file__).resolve().parent.parent))) # agent repo root
|
|
47
|
+
DEFAULT_DB_PATH = BASE_DIR / "state" / "rag" / "search.db"
|
|
48
|
+
INDEX_STATE_PATH = BASE_DIR / "state" / "rag" / "index-state.json"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ── Database Schema ────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
SCHEMA_SQL = """
|
|
54
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
55
|
+
id TEXT PRIMARY KEY,
|
|
56
|
+
source_type TEXT NOT NULL,
|
|
57
|
+
source_path TEXT NOT NULL,
|
|
58
|
+
user_scope TEXT NOT NULL,
|
|
59
|
+
content TEXT NOT NULL,
|
|
60
|
+
title TEXT,
|
|
61
|
+
timestamp TEXT,
|
|
62
|
+
metadata_json TEXT
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_user_scope ON documents(user_scope);
|
|
66
|
+
CREATE INDEX IF NOT EXISTS idx_source_type ON documents(source_type);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_source_path ON documents(source_path);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_timestamp ON documents(timestamp);
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
FTS_SQL = """
|
|
72
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
73
|
+
content,
|
|
74
|
+
title,
|
|
75
|
+
content='documents',
|
|
76
|
+
content_rowid='rowid'
|
|
77
|
+
);
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
# Triggers to keep FTS in sync with documents table
|
|
81
|
+
TRIGGER_SQL = """
|
|
82
|
+
CREATE TRIGGER IF NOT EXISTS documents_ai AFTER INSERT ON documents BEGIN
|
|
83
|
+
INSERT INTO documents_fts(rowid, content, title)
|
|
84
|
+
VALUES (new.rowid, new.content, new.title);
|
|
85
|
+
END;
|
|
86
|
+
|
|
87
|
+
CREATE TRIGGER IF NOT EXISTS documents_ad AFTER DELETE ON documents BEGIN
|
|
88
|
+
INSERT INTO documents_fts(documents_fts, rowid, content, title)
|
|
89
|
+
VALUES ('delete', old.rowid, old.content, old.title);
|
|
90
|
+
END;
|
|
91
|
+
|
|
92
|
+
CREATE TRIGGER IF NOT EXISTS documents_au AFTER UPDATE ON documents BEGIN
|
|
93
|
+
INSERT INTO documents_fts(documents_fts, rowid, content, title)
|
|
94
|
+
VALUES ('delete', old.rowid, old.content, old.title);
|
|
95
|
+
INSERT INTO documents_fts(rowid, content, title)
|
|
96
|
+
VALUES (new.rowid, new.content, new.title);
|
|
97
|
+
END;
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ── Database Initialization ───────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
def init_db(db_path):
|
|
104
|
+
"""Create or open the SQLite database with FTS5 schema."""
|
|
105
|
+
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
conn = sqlite3.connect(str(db_path))
|
|
107
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
108
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
109
|
+
|
|
110
|
+
# Create main table and indexes
|
|
111
|
+
conn.executescript(SCHEMA_SQL)
|
|
112
|
+
|
|
113
|
+
# Create FTS5 virtual table
|
|
114
|
+
conn.executescript(FTS_SQL)
|
|
115
|
+
|
|
116
|
+
# Create sync triggers
|
|
117
|
+
conn.executescript(TRIGGER_SQL)
|
|
118
|
+
|
|
119
|
+
conn.commit()
|
|
120
|
+
return conn
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def drop_and_rebuild(db_path):
|
|
124
|
+
"""Drop all tables and recreate from scratch."""
|
|
125
|
+
if db_path.exists():
|
|
126
|
+
os.remove(str(db_path))
|
|
127
|
+
# Also clear the index state
|
|
128
|
+
if INDEX_STATE_PATH.exists():
|
|
129
|
+
os.remove(str(INDEX_STATE_PATH))
|
|
130
|
+
return init_db(db_path)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ── Index State Management ────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
def load_index_state():
|
|
136
|
+
"""Load the incremental indexing state (file modification times)."""
|
|
137
|
+
if INDEX_STATE_PATH.exists():
|
|
138
|
+
with open(INDEX_STATE_PATH, "r") as f:
|
|
139
|
+
return json.load(f)
|
|
140
|
+
return {"files": {}}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def save_index_state(state):
|
|
144
|
+
"""Save the incremental indexing state."""
|
|
145
|
+
INDEX_STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
with open(INDEX_STATE_PATH, "w") as f:
|
|
147
|
+
json.dump(state, f, indent=2)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ── Source Classification ─────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
def classify_source(rel_path):
|
|
153
|
+
"""
|
|
154
|
+
Determine source_type and user_scope from a file's relative path.
|
|
155
|
+
|
|
156
|
+
Returns (source_type, user_scope) where:
|
|
157
|
+
source_type: "slack_dm", "slack_channel", "email", "knowledge", "decision",
|
|
158
|
+
"synthesis", "queue"
|
|
159
|
+
user_scope: "public", "leadership", "ceo", or a user slug for private data
|
|
160
|
+
"""
|
|
161
|
+
parts = Path(rel_path).parts
|
|
162
|
+
|
|
163
|
+
# memory/interactions/slack/dm-{user-slug}/**
|
|
164
|
+
if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
|
|
165
|
+
and parts[2] == "slack"):
|
|
166
|
+
dir_name = parts[3]
|
|
167
|
+
if dir_name.startswith("dm-"):
|
|
168
|
+
user_slug = dir_name[3:] # "dm-alex-chen" -> "alex-chen"
|
|
169
|
+
return "slack_dm", user_slug
|
|
170
|
+
elif dir_name == "dm":
|
|
171
|
+
# memory/interactions/slack/dm/{something} — treat as private
|
|
172
|
+
if len(parts) >= 5:
|
|
173
|
+
return "slack_dm", parts[4]
|
|
174
|
+
return "slack_dm", "unknown"
|
|
175
|
+
else:
|
|
176
|
+
# Channel (ceo-office, channels, etc.)
|
|
177
|
+
return "slack_channel", "leadership"
|
|
178
|
+
|
|
179
|
+
# memory/interactions/slack/{user-slug}/** (alternate DM format like alex-chen/)
|
|
180
|
+
if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
|
|
181
|
+
and parts[2] == "slack"):
|
|
182
|
+
dir_name = parts[3]
|
|
183
|
+
# Check if it looks like a user slug (contains a hyphen, isn't a known channel)
|
|
184
|
+
known_channels = {"ceo-office", "channels", "dm", "general", "engineering"}
|
|
185
|
+
if dir_name not in known_channels and "-" in dir_name:
|
|
186
|
+
return "slack_dm", dir_name
|
|
187
|
+
|
|
188
|
+
# memory/interactions/email/{user-slug}/**
|
|
189
|
+
if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
|
|
190
|
+
and parts[2] == "email"):
|
|
191
|
+
user_slug = parts[3]
|
|
192
|
+
return "email", user_slug
|
|
193
|
+
|
|
194
|
+
# knowledge/sources/*.yaml — public
|
|
195
|
+
if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "sources"):
|
|
196
|
+
return "knowledge", "public"
|
|
197
|
+
|
|
198
|
+
# knowledge/decisions/**/*.yaml — leadership+
|
|
199
|
+
if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "decisions"):
|
|
200
|
+
return "decision", "leadership"
|
|
201
|
+
|
|
202
|
+
# knowledge/syntheses/*.yaml — public, except strategy-state = ceo
|
|
203
|
+
if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "syntheses"):
|
|
204
|
+
filename = parts[-1]
|
|
205
|
+
if filename == "strategy-state.yaml":
|
|
206
|
+
return "synthesis", "ceo"
|
|
207
|
+
return "synthesis", "public"
|
|
208
|
+
|
|
209
|
+
# state/queues/*.yaml — leadership+
|
|
210
|
+
if (len(parts) >= 2 and parts[0] == "state" and parts[1] == "queues"):
|
|
211
|
+
return "queue", "leadership"
|
|
212
|
+
|
|
213
|
+
# Fallback
|
|
214
|
+
return "unknown", "public"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ── Document ID Generation ────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
def make_doc_id(source_path, line_num=None):
|
|
220
|
+
"""
|
|
221
|
+
Generate a deterministic document ID.
|
|
222
|
+
For JSONL: source_path:line_number
|
|
223
|
+
For YAML/other: hash of source_path
|
|
224
|
+
"""
|
|
225
|
+
if line_num is not None:
|
|
226
|
+
return f"{source_path}:{line_num}"
|
|
227
|
+
return hashlib.sha256(source_path.encode()).hexdigest()[:16]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ── Content Parsing ───────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
def parse_jsonl_file(filepath, rel_path, source_type, user_scope):
|
|
233
|
+
"""
|
|
234
|
+
Parse a JSONL file into individual documents.
|
|
235
|
+
Each line becomes one document.
|
|
236
|
+
"""
|
|
237
|
+
documents = []
|
|
238
|
+
try:
|
|
239
|
+
with open(filepath, "r") as f:
|
|
240
|
+
for line_num, line in enumerate(f, 1):
|
|
241
|
+
line = line.strip()
|
|
242
|
+
if not line:
|
|
243
|
+
continue
|
|
244
|
+
try:
|
|
245
|
+
record = json.loads(line)
|
|
246
|
+
except json.JSONDecodeError:
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
# Extract content from various field names
|
|
250
|
+
content = (record.get("content")
|
|
251
|
+
or record.get("text")
|
|
252
|
+
or record.get("content_preview")
|
|
253
|
+
or "")
|
|
254
|
+
|
|
255
|
+
if not content:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
# Build a rich text representation
|
|
259
|
+
sender = record.get("sender") or record.get("from") or ""
|
|
260
|
+
channel = record.get("channel") or ""
|
|
261
|
+
direction = record.get("direction") or ""
|
|
262
|
+
action = record.get("action_taken") or ""
|
|
263
|
+
context = record.get("context") or ""
|
|
264
|
+
|
|
265
|
+
# Build title from available fields
|
|
266
|
+
title_parts = []
|
|
267
|
+
if sender:
|
|
268
|
+
title_parts.append(f"From: {sender}")
|
|
269
|
+
if channel:
|
|
270
|
+
title_parts.append(f"Channel: {channel}")
|
|
271
|
+
if direction:
|
|
272
|
+
title_parts.append(f"({direction})")
|
|
273
|
+
title = " | ".join(title_parts) if title_parts else None
|
|
274
|
+
|
|
275
|
+
# Include action_taken and context in searchable content
|
|
276
|
+
full_content = content
|
|
277
|
+
if action:
|
|
278
|
+
full_content += f"\nAction: {action}"
|
|
279
|
+
if context:
|
|
280
|
+
full_content += f"\nContext: {context}"
|
|
281
|
+
|
|
282
|
+
# Extract timestamp
|
|
283
|
+
timestamp = (record.get("timestamp")
|
|
284
|
+
or record.get("processed_at")
|
|
285
|
+
or record.get("ts")
|
|
286
|
+
or "")
|
|
287
|
+
|
|
288
|
+
# Metadata
|
|
289
|
+
metadata = {
|
|
290
|
+
"sender": sender,
|
|
291
|
+
"channel": channel,
|
|
292
|
+
"direction": direction,
|
|
293
|
+
"classification": record.get("classification", ""),
|
|
294
|
+
"ref": record.get("ref", ""),
|
|
295
|
+
"message_ts": record.get("message_ts", ""),
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
doc_id = make_doc_id(rel_path, line_num)
|
|
299
|
+
documents.append({
|
|
300
|
+
"id": doc_id,
|
|
301
|
+
"source_type": source_type,
|
|
302
|
+
"source_path": rel_path,
|
|
303
|
+
"user_scope": user_scope,
|
|
304
|
+
"content": full_content,
|
|
305
|
+
"title": title,
|
|
306
|
+
"timestamp": timestamp,
|
|
307
|
+
"metadata_json": json.dumps(metadata),
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
except Exception as e:
|
|
311
|
+
print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
|
|
312
|
+
|
|
313
|
+
return documents
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def parse_yaml_file(filepath, rel_path, source_type, user_scope):
|
|
317
|
+
"""
|
|
318
|
+
Parse a YAML file into a single document.
|
|
319
|
+
Uses the full file content as the searchable text.
|
|
320
|
+
For simplicity and no-dependency constraint, we read the raw text.
|
|
321
|
+
"""
|
|
322
|
+
documents = []
|
|
323
|
+
try:
|
|
324
|
+
with open(filepath, "r") as f:
|
|
325
|
+
content = f.read()
|
|
326
|
+
|
|
327
|
+
if not content.strip():
|
|
328
|
+
return documents
|
|
329
|
+
|
|
330
|
+
# Extract a title from the first comment line or YAML key
|
|
331
|
+
title = None
|
|
332
|
+
for line in content.split("\n"):
|
|
333
|
+
line = line.strip()
|
|
334
|
+
if line.startswith("# ") and not line.startswith("# =="):
|
|
335
|
+
title = line[2:].strip()
|
|
336
|
+
break
|
|
337
|
+
elif line and not line.startswith("#") and ":" in line:
|
|
338
|
+
title = line.split(":")[0].strip()
|
|
339
|
+
break
|
|
340
|
+
|
|
341
|
+
# For large YAML files, split into sections by top-level keys
|
|
342
|
+
# But keep it simple: one document per file
|
|
343
|
+
doc_id = make_doc_id(rel_path)
|
|
344
|
+
documents.append({
|
|
345
|
+
"id": doc_id,
|
|
346
|
+
"source_type": source_type,
|
|
347
|
+
"source_path": rel_path,
|
|
348
|
+
"user_scope": user_scope,
|
|
349
|
+
"content": content[:50000], # cap at 50K chars
|
|
350
|
+
"title": title,
|
|
351
|
+
"timestamp": "",
|
|
352
|
+
"metadata_json": json.dumps({"filename": Path(filepath).name}),
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
except Exception as e:
|
|
356
|
+
print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
|
|
357
|
+
|
|
358
|
+
return documents
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def parse_markdown_file(filepath, rel_path, source_type, user_scope):
|
|
362
|
+
"""Parse a Markdown file into a single document."""
|
|
363
|
+
documents = []
|
|
364
|
+
try:
|
|
365
|
+
with open(filepath, "r") as f:
|
|
366
|
+
content = f.read()
|
|
367
|
+
|
|
368
|
+
if not content.strip():
|
|
369
|
+
return documents
|
|
370
|
+
|
|
371
|
+
# Extract title from first heading
|
|
372
|
+
title = None
|
|
373
|
+
for line in content.split("\n"):
|
|
374
|
+
line = line.strip()
|
|
375
|
+
if line.startswith("# "):
|
|
376
|
+
title = line[2:].strip()
|
|
377
|
+
break
|
|
378
|
+
|
|
379
|
+
doc_id = make_doc_id(rel_path)
|
|
380
|
+
documents.append({
|
|
381
|
+
"id": doc_id,
|
|
382
|
+
"source_type": source_type,
|
|
383
|
+
"source_path": rel_path,
|
|
384
|
+
"user_scope": user_scope,
|
|
385
|
+
"content": content[:50000],
|
|
386
|
+
"title": title,
|
|
387
|
+
"timestamp": "",
|
|
388
|
+
"metadata_json": json.dumps({"filename": Path(filepath).name}),
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
except Exception as e:
|
|
392
|
+
print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
|
|
393
|
+
|
|
394
|
+
return documents
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# ── File Discovery ────────────────────────────────────────────────────────────
|
|
398
|
+
|
|
399
|
+
def discover_files():
|
|
400
|
+
"""
|
|
401
|
+
Walk the configured source directories and return a list of
|
|
402
|
+
(absolute_path, relative_path) tuples for indexable files.
|
|
403
|
+
"""
|
|
404
|
+
source_dirs = [
|
|
405
|
+
"memory/interactions",
|
|
406
|
+
"knowledge/sources",
|
|
407
|
+
"knowledge/decisions",
|
|
408
|
+
"knowledge/syntheses",
|
|
409
|
+
"state/queues",
|
|
410
|
+
]
|
|
411
|
+
|
|
412
|
+
indexable_extensions = {".jsonl", ".yaml", ".yml", ".md"}
|
|
413
|
+
files = []
|
|
414
|
+
|
|
415
|
+
for src_dir in source_dirs:
|
|
416
|
+
abs_dir = BASE_DIR / src_dir
|
|
417
|
+
if not abs_dir.exists():
|
|
418
|
+
continue
|
|
419
|
+
|
|
420
|
+
for root, _dirs, filenames in os.walk(str(abs_dir)):
|
|
421
|
+
for fname in filenames:
|
|
422
|
+
ext = os.path.splitext(fname)[1].lower()
|
|
423
|
+
if ext not in indexable_extensions:
|
|
424
|
+
continue
|
|
425
|
+
|
|
426
|
+
abs_path = os.path.join(root, fname)
|
|
427
|
+
rel_path = os.path.relpath(abs_path, BASE_DIR)
|
|
428
|
+
files.append((abs_path, rel_path))
|
|
429
|
+
|
|
430
|
+
return files
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# ── Indexing Pipeline ─────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
def index_file(filepath, rel_path):
|
|
436
|
+
"""
|
|
437
|
+
Parse a file into documents based on its type and path.
|
|
438
|
+
Returns a list of document dicts.
|
|
439
|
+
"""
|
|
440
|
+
source_type, user_scope = classify_source(rel_path)
|
|
441
|
+
ext = os.path.splitext(filepath)[1].lower()
|
|
442
|
+
|
|
443
|
+
if ext == ".jsonl":
|
|
444
|
+
return parse_jsonl_file(filepath, rel_path, source_type, user_scope)
|
|
445
|
+
elif ext in (".yaml", ".yml"):
|
|
446
|
+
return parse_yaml_file(filepath, rel_path, source_type, user_scope)
|
|
447
|
+
elif ext == ".md":
|
|
448
|
+
return parse_markdown_file(filepath, rel_path, source_type, user_scope)
|
|
449
|
+
else:
|
|
450
|
+
return []
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def upsert_documents(conn, documents):
|
|
454
|
+
"""Insert or replace documents in the database."""
|
|
455
|
+
if not documents:
|
|
456
|
+
return 0
|
|
457
|
+
|
|
458
|
+
# We need to handle FTS sync carefully:
|
|
459
|
+
# 1. Delete existing docs by ID (triggers will remove from FTS)
|
|
460
|
+
# 2. Insert new docs (triggers will add to FTS)
|
|
461
|
+
count = 0
|
|
462
|
+
for doc in documents:
|
|
463
|
+
# Delete existing (triggers handle FTS cleanup)
|
|
464
|
+
conn.execute("DELETE FROM documents WHERE id = ?", (doc["id"],))
|
|
465
|
+
|
|
466
|
+
# Insert new
|
|
467
|
+
conn.execute(
|
|
468
|
+
"""INSERT INTO documents (id, source_type, source_path, user_scope,
|
|
469
|
+
content, title, timestamp, metadata_json)
|
|
470
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
471
|
+
(doc["id"], doc["source_type"], doc["source_path"], doc["user_scope"],
|
|
472
|
+
doc["content"], doc["title"], doc["timestamp"], doc["metadata_json"]),
|
|
473
|
+
)
|
|
474
|
+
count += 1
|
|
475
|
+
|
|
476
|
+
return count
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def remove_stale_documents(conn, source_path):
|
|
480
|
+
"""Remove all documents from a given source path (before re-indexing it)."""
|
|
481
|
+
conn.execute("DELETE FROM documents WHERE source_path = ?", (source_path,))
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def run_index(db_path, full=False):
|
|
485
|
+
"""
|
|
486
|
+
Run the indexing pipeline.
|
|
487
|
+
If full=True, drops and rebuilds the entire index.
|
|
488
|
+
Otherwise, only re-indexes changed files.
|
|
489
|
+
"""
|
|
490
|
+
if full:
|
|
491
|
+
print("Full re-index: dropping and rebuilding database...")
|
|
492
|
+
conn = drop_and_rebuild(db_path)
|
|
493
|
+
state = {"files": {}}
|
|
494
|
+
else:
|
|
495
|
+
conn = init_db(db_path)
|
|
496
|
+
state = load_index_state()
|
|
497
|
+
|
|
498
|
+
files = discover_files()
|
|
499
|
+
print(f"Discovered {len(files)} indexable files")
|
|
500
|
+
|
|
501
|
+
indexed_count = 0
|
|
502
|
+
doc_count = 0
|
|
503
|
+
skipped_count = 0
|
|
504
|
+
|
|
505
|
+
for abs_path, rel_path in files:
|
|
506
|
+
try:
|
|
507
|
+
mtime = os.path.getmtime(abs_path)
|
|
508
|
+
except OSError:
|
|
509
|
+
continue
|
|
510
|
+
|
|
511
|
+
# Check if file has changed since last index
|
|
512
|
+
last_mtime = state["files"].get(rel_path, 0)
|
|
513
|
+
if not full and mtime <= last_mtime:
|
|
514
|
+
skipped_count += 1
|
|
515
|
+
continue
|
|
516
|
+
|
|
517
|
+
# Remove old documents from this file
|
|
518
|
+
remove_stale_documents(conn, rel_path)
|
|
519
|
+
|
|
520
|
+
# Parse and index
|
|
521
|
+
documents = index_file(abs_path, rel_path)
|
|
522
|
+
if documents:
|
|
523
|
+
count = upsert_documents(conn, documents)
|
|
524
|
+
doc_count += count
|
|
525
|
+
|
|
526
|
+
# Update state
|
|
527
|
+
state["files"][rel_path] = mtime
|
|
528
|
+
indexed_count += 1
|
|
529
|
+
|
|
530
|
+
conn.commit()
|
|
531
|
+
conn.close()
|
|
532
|
+
|
|
533
|
+
# Save index state
|
|
534
|
+
save_index_state(state)
|
|
535
|
+
|
|
536
|
+
print(f"Indexed {indexed_count} files ({doc_count} documents)")
|
|
537
|
+
if skipped_count > 0:
|
|
538
|
+
print(f"Skipped {skipped_count} unchanged files")
|
|
539
|
+
|
|
540
|
+
return indexed_count, doc_count
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# ── Statistics ────────────────────────────────────────────────────────────────
|
|
544
|
+
|
|
545
|
+
def show_stats(db_path):
|
|
546
|
+
"""Show index statistics."""
|
|
547
|
+
if not db_path.exists():
|
|
548
|
+
print("No index database found. Run indexer first.")
|
|
549
|
+
return
|
|
550
|
+
|
|
551
|
+
conn = sqlite3.connect(str(db_path))
|
|
552
|
+
|
|
553
|
+
# Total documents
|
|
554
|
+
total = conn.execute("SELECT COUNT(*) FROM documents").fetchone()[0]
|
|
555
|
+
print(f"Total documents: {total}")
|
|
556
|
+
|
|
557
|
+
# By source type
|
|
558
|
+
print("\nBy source type:")
|
|
559
|
+
rows = conn.execute(
|
|
560
|
+
"SELECT source_type, COUNT(*) FROM documents GROUP BY source_type ORDER BY COUNT(*) DESC"
|
|
561
|
+
).fetchall()
|
|
562
|
+
for source_type, count in rows:
|
|
563
|
+
print(f" {source_type}: {count}")
|
|
564
|
+
|
|
565
|
+
# By user scope
|
|
566
|
+
print("\nBy user scope:")
|
|
567
|
+
rows = conn.execute(
|
|
568
|
+
"SELECT user_scope, COUNT(*) FROM documents GROUP BY user_scope ORDER BY COUNT(*) DESC"
|
|
569
|
+
).fetchall()
|
|
570
|
+
for scope, count in rows:
|
|
571
|
+
print(f" {scope}: {count}")
|
|
572
|
+
|
|
573
|
+
# By source path (top 10)
|
|
574
|
+
print("\nTop 10 source files:")
|
|
575
|
+
rows = conn.execute(
|
|
576
|
+
"SELECT source_path, COUNT(*) FROM documents GROUP BY source_path ORDER BY COUNT(*) DESC LIMIT 10"
|
|
577
|
+
).fetchall()
|
|
578
|
+
for path, count in rows:
|
|
579
|
+
print(f" {path}: {count}")
|
|
580
|
+
|
|
581
|
+
# Index state
|
|
582
|
+
state = load_index_state()
|
|
583
|
+
print(f"\nTracked files in index state: {len(state.get('files', {}))}")
|
|
584
|
+
|
|
585
|
+
conn.close()
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
# ── Main ───────────────────────────────────────────────────────────────────────
|
|
589
|
+
|
|
590
|
+
def main():
|
|
591
|
+
parser = argparse.ArgumentParser(
|
|
592
|
+
description="SQLite FTS5 indexer for per-user ring-fenced RAG (Phase 2). "
|
|
593
|
+
"Indexes the agent repo's content sources into a full-text search database.",
|
|
594
|
+
epilog="Examples:\n"
|
|
595
|
+
" %(prog)s # incremental index\n"
|
|
596
|
+
" %(prog)s --full # full re-index\n"
|
|
597
|
+
" %(prog)s --stats # show statistics\n"
|
|
598
|
+
" %(prog)s --db /tmp/test.db # custom DB path\n",
|
|
599
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
600
|
+
)
|
|
601
|
+
parser.add_argument(
|
|
602
|
+
"--full", action="store_true",
|
|
603
|
+
help="Full re-index: drop and rebuild the entire database"
|
|
604
|
+
)
|
|
605
|
+
parser.add_argument(
|
|
606
|
+
"--stats", action="store_true",
|
|
607
|
+
help="Show index statistics and exit"
|
|
608
|
+
)
|
|
609
|
+
parser.add_argument(
|
|
610
|
+
"--db", type=str, default=None,
|
|
611
|
+
help=f"Path to SQLite database (default: {DEFAULT_DB_PATH})"
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
args = parser.parse_args()
|
|
615
|
+
|
|
616
|
+
db_path = Path(args.db) if args.db else DEFAULT_DB_PATH
|
|
617
|
+
|
|
618
|
+
if args.stats:
|
|
619
|
+
show_stats(db_path)
|
|
620
|
+
return
|
|
621
|
+
|
|
622
|
+
start = time.time()
|
|
623
|
+
indexed, docs = run_index(db_path, full=args.full)
|
|
624
|
+
elapsed = time.time() - start
|
|
625
|
+
print(f"Completed in {elapsed:.2f}s")
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
if __name__ == "__main__":
|
|
629
|
+
main()
|