coding-os 0.3.2__py3-none-any.whl
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.
- adapters/claude/README.md +6 -0
- adapters/claude/_install_helpers/extract_stacks.py +43 -0
- adapters/claude/_install_helpers/update_mcp_json.py +75 -0
- adapters/claude/adapter.yaml +164 -0
- adapters/claude/hooks/README.md +40 -0
- adapters/claude/hooks/agent_memory_sync.py +131 -0
- adapters/claude/hooks/ensure-agent-memory-link.sh +36 -0
- adapters/claude/hooks/sync-agent-memory.sh +21 -0
- adapters/claude/install.sh +86 -0
- adapters/claude/sdk_dispatcher.py +871 -0
- adapters/claude/settings.local.template.json +31 -0
- adapters/claude/settings.template.json +808 -0
- adapters/claude/update_mcp_json.py +85 -0
- adapters/codex/adapter.yaml +254 -0
- adapters/codex/chat_provider.py +230 -0
- adapters/codex/commands/formula-f1.md +129 -0
- adapters/codex/commands/formula-f10.md +100 -0
- adapters/codex/commands/formula-f11.md +123 -0
- adapters/codex/commands/formula-f2.md +139 -0
- adapters/codex/commands/formula-f3.md +127 -0
- adapters/codex/commands/formula-f4.md +101 -0
- adapters/codex/commands/formula-f5.md +135 -0
- adapters/codex/commands/formula-f6.md +147 -0
- adapters/codex/commands/formula-f7.md +111 -0
- adapters/codex/commands/formula-f8.md +133 -0
- adapters/codex/commands/formula-f9.md +112 -0
- adapters/codex/enable_codex_hooks.py +94 -0
- adapters/codex/ensure_codex_mcp.py +124 -0
- adapters/codex/hooks/codex-merge-hook-output.py +72 -0
- adapters/codex/hooks/codex-normalize-edit.py +96 -0
- adapters/codex/hooks/codex-postedit-dispatch.sh +75 -0
- adapters/codex/hooks/codex-posttool-dispatch.sh +70 -0
- adapters/codex/hooks/codex-preedit-dispatch.sh +83 -0
- adapters/codex/hooks/codex-pretool-dispatch.sh +82 -0
- adapters/codex/hooks/codex-sessionend-dispatch.sh +20 -0
- adapters/codex/hooks/codex-sessionstart-dispatch.sh +68 -0
- adapters/codex/hooks/codex-stop-dispatch.sh +73 -0
- adapters/codex/hooks/codex-userpromptsubmit-dispatch.sh +74 -0
- adapters/codex/hooks.template.json +208 -0
- adapters/codex/install.sh +83 -0
- adapters/codex/sdk_dispatcher.py +449 -0
- board_os/__init__.py +39 -0
- board_os/_agent_runtime.py +256 -0
- board_os/config.py +421 -0
- board_os/git_coherence.py +107 -0
- board_os/hub_adapter_manifest.py +140 -0
- board_os/mcp_tools.py +3228 -0
- board_os/migration.py +166 -0
- board_os/parser.py +317 -0
- board_os/presence.py +156 -0
- board_os/sync.py +320 -0
- board_os/transition_gates.py +224 -0
- board_os/transition_gates_cli.py +272 -0
- board_os/transition_gates_validator.py +551 -0
- board_os/verify_suites.py +126 -0
- board_os/verify_suites_cli.py +328 -0
- board_os/workflow.py +967 -0
- cli/__init__.py +0 -0
- cli/_data_types.py +248 -0
- cli/_init_helpers.py +587 -0
- cli/_resources.py +100 -0
- cli/adapter_registry.py +239 -0
- cli/add_stack.py +315 -0
- cli/aggregator.py +438 -0
- cli/board_commands.py +1218 -0
- cli/brain_commands.py +255 -0
- cli/cognition.py +345 -0
- cli/config_composer.py +349 -0
- cli/core_version.py +41 -0
- cli/cron_commands.py +278 -0
- cli/db_reset.py +298 -0
- cli/doc_commands.py +111 -0
- cli/doctor.py +2953 -0
- cli/doctor_board.py +365 -0
- cli/doctor_extras.py +1121 -0
- cli/doctor_graph.py +608 -0
- cli/doctor_tokens.py +254 -0
- cli/graph_commands.py +1265 -0
- cli/hook_renderer.py +393 -0
- cli/hub_commands.py +580 -0
- cli/list_adapters.py +79 -0
- cli/list_stacks.py +105 -0
- cli/logs_commands.py +89 -0
- cli/main.py +3070 -0
- cli/materialize_file.py +65 -0
- cli/mcp_start.py +153 -0
- cli/module_commands.py +513 -0
- cli/pr_commands.py +2024 -0
- cli/preset_commands.py +126 -0
- cli/preset_registry.py +171 -0
- cli/project_overrides.py +119 -0
- cli/registry.py +365 -0
- cli/remove_stack.py +492 -0
- cli/renderer.py +620 -0
- cli/setup.py +464 -0
- cli/skill_commands.py +689 -0
- cli/skill_registry.py +235 -0
- cli/skills_list.py +332 -0
- cli/stack_lint.py +351 -0
- cli/stack_registry.py +688 -0
- cli/subsystems.py +335 -0
- cli/sync_all.py +310 -0
- cli/tail_command.py +410 -0
- cli/update.py +608 -0
- cli/verify_since_edit.py +439 -0
- coding_os-0.3.2.dist-info/METADATA +508 -0
- coding_os-0.3.2.dist-info/RECORD +1304 -0
- coding_os-0.3.2.dist-info/WHEEL +5 -0
- coding_os-0.3.2.dist-info/entry_points.txt +5 -0
- coding_os-0.3.2.dist-info/licenses/LICENSE +201 -0
- coding_os-0.3.2.dist-info/top_level.txt +10 -0
- core/__init__.py +0 -0
- core/board_os/__init__.py +39 -0
- core/board_os/_agent_runtime.py +256 -0
- core/board_os/config.py +421 -0
- core/board_os/git_coherence.py +107 -0
- core/board_os/hub_adapter_manifest.py +140 -0
- core/board_os/mcp_tools.py +3228 -0
- core/board_os/migration.py +166 -0
- core/board_os/parser.py +317 -0
- core/board_os/presence.py +156 -0
- core/board_os/sync.py +320 -0
- core/board_os/transition-gates.yaml +176 -0
- core/board_os/transition_gates.py +224 -0
- core/board_os/transition_gates_cli.py +272 -0
- core/board_os/transition_gates_validator.py +551 -0
- core/board_os/verify-suites.yaml +113 -0
- core/board_os/verify_suites.py +126 -0
- core/board_os/verify_suites_cli.py +328 -0
- core/board_os/workflow.py +967 -0
- core/commands/board.md +27 -0
- core/commands/classify.md +23 -0
- core/commands/compose.md +23 -0
- core/commands/daily.md +31 -0
- core/commands/diagnose.md +7 -0
- core/commands/memory-search.md +23 -0
- core/commands/new-project.md +33 -0
- core/commands/retro.md +38 -0
- core/commands/review.md +14 -0
- core/commands/task.md +17 -0
- core/commands/verify.md +34 -0
- core/docs/thinking_os-final-edition.md +1449 -0
- core/doctor-config.yaml +74 -0
- core/graph_os/__init__.py +29 -0
- core/graph_os/backend.py +233 -0
- core/graph_os/backends/__init__.py +13 -0
- core/graph_os/backends/sqlite_backend.py +1053 -0
- core/graph_os/bench/__init__.py +17 -0
- core/graph_os/bench/fixtures.py +56 -0
- core/graph_os/bench/harness.py +95 -0
- core/graph_os/bench/persian_precision.py +142 -0
- core/graph_os/bench/scale_500k.py +120 -0
- core/graph_os/bench/token_cost.py +170 -0
- core/graph_os/bench/viewer_fps.py +110 -0
- core/graph_os/communities.py +410 -0
- core/graph_os/enterprise.py +218 -0
- core/graph_os/entry_points.py +226 -0
- core/graph_os/extractors/__init__.py +25 -0
- core/graph_os/extractors/code_generic.py +914 -0
- core/graph_os/extractors/code_go.py +1422 -0
- core/graph_os/extractors/code_json.py +340 -0
- core/graph_os/extractors/code_php.py +979 -0
- core/graph_os/extractors/code_python.py +1454 -0
- core/graph_os/extractors/code_shell.py +538 -0
- core/graph_os/extractors/code_toml.py +302 -0
- core/graph_os/extractors/code_ts.py +1665 -0
- core/graph_os/extractors/code_yaml.py +394 -0
- core/graph_os/extractors/contracts.py +1592 -0
- core/graph_os/extractors/md_links.py +890 -0
- core/graph_os/extractors/task_deps.py +345 -0
- core/graph_os/groups/__init__.py +22 -0
- core/graph_os/groups/cross_repo.py +156 -0
- core/graph_os/groups/manifest.py +141 -0
- core/graph_os/ingest/__init__.py +19 -0
- core/graph_os/ingest/base.py +306 -0
- core/graph_os/ingest/github.py +112 -0
- core/graph_os/ingest/zip.py +95 -0
- core/graph_os/toolchain.py +393 -0
- core/graph_os/tools/__init__.py +9 -0
- core/graph_os/tools/graph.py +5573 -0
- core/graph_os/tools/reindex_dispatch.py +730 -0
- core/graph_os/tree_sitter_overlay.py +235 -0
- core/graph_os/types.py +252 -0
- core/graph_os/vec_index.py +277 -0
- core/graph_os/viewer/__init__.py +12 -0
- core/graph_os/viewer/exporter.py +93 -0
- core/graph_os/viewer/template.py +189 -0
- core/hooks/_helpers/_paths.py +40 -0
- core/hooks/_helpers/advance_role.py +72 -0
- core/hooks/_helpers/auto_compose.py +228 -0
- core/hooks/_helpers/auto_validate_lessons.py +55 -0
- core/hooks/_helpers/branch_guard_check.py +796 -0
- core/hooks/_helpers/check_commit_message.py +108 -0
- core/hooks/_helpers/check_dangerous_rm.py +80 -0
- core/hooks/_helpers/check_git_bypass.py +154 -0
- core/hooks/_helpers/check_git_destructive.py +77 -0
- core/hooks/_helpers/check_settings_write.py +97 -0
- core/hooks/_helpers/consume_override.py +51 -0
- core/hooks/_helpers/context_budget.py +77 -0
- core/hooks/_helpers/cos_say_json.py +103 -0
- core/hooks/_helpers/destructive_edit_check.py +163 -0
- core/hooks/_helpers/detect_status_transition.py +82 -0
- core/hooks/_helpers/digest_regen.py +56 -0
- core/hooks/_helpers/doc_sync_check.py +498 -0
- core/hooks/_helpers/drain_embedding_outbox.py +52 -0
- core/hooks/_helpers/extract_additional_context.py +51 -0
- core/hooks/_helpers/extract_commit_msg_arg.py +74 -0
- core/hooks/_helpers/git_command_parse.py +424 -0
- core/hooks/_helpers/git_settings_fields.py +47 -0
- core/hooks/_helpers/graph_context_match.py +37 -0
- core/hooks/_helpers/graph_marker_check.py +70 -0
- core/hooks/_helpers/jit_recall.py +56 -0
- core/hooks/_helpers/json_field.py +41 -0
- core/hooks/_helpers/narrative_signal.py +59 -0
- core/hooks/_helpers/observation_count.py +31 -0
- core/hooks/_helpers/pre_commit_batch.py +177 -0
- core/hooks/_helpers/pre_commit_fake_input.py +42 -0
- core/hooks/_helpers/presence_gc.py +102 -0
- core/hooks/_helpers/presence_write.py +167 -0
- core/hooks/_helpers/recover_indirect.py +35 -0
- core/hooks/_helpers/routing_evolution.py +104 -0
- core/hooks/_helpers/session_recap.py +72 -0
- core/hooks/_helpers/skill_primer.py +229 -0
- core/hooks/_helpers/task_sync.py +59 -0
- core/hooks/_helpers/tool_failure_capture.py +147 -0
- core/hooks/_helpers/trajectory_autosnap.py +278 -0
- core/hooks/_helpers/trajectory_startup.py +62 -0
- core/hooks/_helpers/turn_summary.py +82 -0
- core/hooks/_helpers/validate_task_frontmatter.py +98 -0
- core/hooks/_helpers/wip_limit_check.py +103 -0
- core/hooks/_helpers/wip_lines.py +53 -0
- core/hooks/_helpers/work_log_append.py +89 -0
- core/hooks/_helpers/wrap_dispatch_output.py +82 -0
- core/hooks/advance-role.sh +48 -0
- core/hooks/agent-presence.sh +179 -0
- core/hooks/auto-brain-decay.sh +184 -0
- core/hooks/auto-compose-roles.sh +83 -0
- core/hooks/auto-graph-reconcile-shell.sh +119 -0
- core/hooks/auto-regen-doc-index.sh +120 -0
- core/hooks/auto-reindex-docs.sh +130 -0
- core/hooks/auto-task-sync.sh +56 -0
- core/hooks/auto-trace-rotate.sh +88 -0
- core/hooks/block-bad-patterns.sh +212 -0
- core/hooks/block-dangerous-commands.sh +182 -0
- core/hooks/block-hardcoded-literals.sh +90 -0
- core/hooks/block-migration-conflict.sh +114 -0
- core/hooks/block-protected-files.sh +129 -0
- core/hooks/block-secrets.sh +185 -0
- core/hooks/block-shared-tree-edit.sh +75 -0
- core/hooks/block-uv-heredoc.sh +78 -0
- core/hooks/branch-guard.sh +122 -0
- core/hooks/capture-observation.sh +76 -0
- core/hooks/capture-tool-failure.sh +24 -0
- core/hooks/capture-work-log.sh +89 -0
- core/hooks/check-agents-md-refs.sh +75 -0
- core/hooks/check-agents-md-size.sh +49 -0
- core/hooks/check-capture-worked.sh +148 -0
- core/hooks/check-doc-size.sh +61 -0
- core/hooks/check-mcp-extras.sh +92 -0
- core/hooks/check-state.sh +87 -0
- core/hooks/classify-task-mode.sh +103 -0
- core/hooks/cos-env.sh +1301 -0
- core/hooks/drain-embedding-outbox.sh +24 -0
- core/hooks/enforce-anti-ambiguity.sh +74 -0
- core/hooks/enforce-commit-message.sh +73 -0
- core/hooks/enforce-doc-anchor.sh +224 -0
- core/hooks/enforce-doc-sync.sh +206 -0
- core/hooks/enforce-graph-context.sh +89 -0
- core/hooks/enforce-graph-first-read.sh +94 -0
- core/hooks/enforce-memory-check.sh +128 -0
- core/hooks/enforce-rename-plan.sh +78 -0
- core/hooks/enforce-scaffold-boundary.sh +68 -0
- core/hooks/enforce-skill.sh +125 -0
- core/hooks/enforce-task-body.sh +52 -0
- core/hooks/enforce-task-start.sh +81 -0
- core/hooks/enforce-task-transition.sh +75 -0
- core/hooks/enforce-template.sh +143 -0
- core/hooks/enforce-verify.sh +112 -0
- core/hooks/enforce-wip-limit.sh +43 -0
- core/hooks/enforce-zoom.sh +69 -0
- core/hooks/ensure-hub-up.sh +67 -0
- core/hooks/inject-mcp-caller-session.sh +70 -0
- core/hooks/jit-recall.sh +65 -0
- core/hooks/link-commit-to-task.sh +143 -0
- core/hooks/lint-task.sh +40 -0
- core/hooks/nudge-docs-first.sh +71 -0
- core/hooks/nudge-git-mode.sh +29 -0
- core/hooks/nudge-graph-os.sh +118 -0
- core/hooks/nudge-learn-narrative.sh +36 -0
- core/hooks/nudge-model-routing.sh +32 -0
- core/hooks/nudge-reentry.sh +101 -0
- core/hooks/nudge-reuse-first.sh +68 -0
- core/hooks/nudge-task-discovery.sh +81 -0
- core/hooks/nudge-thinking-os.sh +109 -0
- core/hooks/pr-reap.sh +23 -0
- core/hooks/reclaim-sweep.sh +58 -0
- core/hooks/record-verify-auto.sh +77 -0
- core/hooks/record-verify.sh +74 -0
- core/hooks/regen-reminder.sh +104 -0
- core/hooks/registry.yaml +1262 -0
- core/hooks/remind-daily.sh +27 -0
- core/hooks/remind-dogfood.sh +70 -0
- core/hooks/remind-learn-validate.sh +94 -0
- core/hooks/rules-primer.sh +50 -0
- core/hooks/search-enforce-inventory.sh +108 -0
- core/hooks/search-verify-remaining.sh +132 -0
- core/hooks/session-context.sh +729 -0
- core/hooks/session-end.sh +145 -0
- core/hooks/session-skill-primer.sh +43 -0
- core/hooks/snapshot-transcript.sh +56 -0
- core/hooks/sync-task-current.sh +85 -0
- core/hooks/test-first-reminder.sh +120 -0
- core/hooks/test-governor.sh +173 -0
- core/hooks/thinking_os-gate.sh +52 -0
- core/hooks/track-backtrack.sh +35 -0
- core/hooks/track-discovery.sh +121 -0
- core/hooks/track-skill.sh +53 -0
- core/hooks/validate-task-frontmatter.sh +49 -0
- core/hooks/verify-rename-callers.sh +119 -0
- core/hooks/warn-abandoned-task.sh +99 -0
- core/hooks/warn-destructive-edit.sh +64 -0
- core/hooks/warn-diff-size.sh +43 -0
- core/hooks/warn-graph-empty.sh +81 -0
- core/hooks/warn-mcp-down.sh +190 -0
- core/hooks/write-state.sh +55 -0
- core/logging_os/__init__.py +33 -0
- core/logging_os/api.py +127 -0
- core/logging_os/bridge.py +80 -0
- core/logging_os/config.py +172 -0
- core/logging_os/fingerprint.py +25 -0
- core/logging_os/redact.py +53 -0
- core/logging_os/render.py +83 -0
- core/logging_os/sinks.py +164 -0
- core/rules/anti-overengineering.md +44 -0
- core/rules/api-contract-discipline.md +41 -0
- core/rules/dimension-registry.md +155 -0
- core/rules/git-workflow.md +57 -0
- core/rules/memory.md +46 -0
- core/rules/model-routing.md +22 -0
- core/rules/skill-enforcement.md +75 -0
- core/rules/test-discipline.md +38 -0
- core/rules/thinking_os.md +48 -0
- core/rules/transparency-banner.md +37 -0
- core/runtime_paths.yaml +36 -0
- core/scaffold_manifest.json +14430 -0
- core/scheduled/__init__.py +0 -0
- core/scheduled/_activity.py +126 -0
- core/scheduled/_state.py +113 -0
- core/scheduled/config.py +86 -0
- core/scheduled/dep_reconcile.py +135 -0
- core/scheduled/error_sweep.py +137 -0
- core/scheduled/nightly.py +930 -0
- core/scheduled/responsive_extract.py +65 -0
- core/schemas/adapter.schema.json +269 -0
- core/schemas/preset.schema.json +50 -0
- core/schemas/skill.schema.json +81 -0
- core/schemas/stack.schema.json +404 -0
- core/scripts/_lib.sh +9 -0
- core/scripts/docs-lint.sh +228 -0
- core/scripts/docs-nav-fix.sh +133 -0
- core/scripts/docs-staleness-check.sh +154 -0
- core/scripts/install-adapter.sh +266 -0
- core/scripts/link-stack-skills.sh +52 -0
- core/scripts/log-latest.sh +106 -0
- core/scripts/log-search.sh +89 -0
- core/scripts/log-write.sh +134 -0
- core/scripts/ref-resolve.sh +71 -0
- core/skills/a11y/SKILL.md +305 -0
- core/skills/a11y/assets/a11y-checklist.md +137 -0
- core/skills/a11y/references/aria-and-focus.md +247 -0
- core/skills/a11y/references/rn-accessibility.md +343 -0
- core/skills/a11y/references/screen-reader-testing.md +190 -0
- core/skills/agent-memory/SKILL.md +191 -0
- core/skills/agent-memory/assets/memory-checklist.md +21 -0
- core/skills/agent-memory/references/memory-recipes.md +57 -0
- core/skills/api-design/SKILL.md +232 -0
- core/skills/api-design/assets/api-design-checklist.md +110 -0
- core/skills/api-design/references/error-envelope.md +381 -0
- core/skills/api-design/references/idempotency-pagination.md +312 -0
- core/skills/api-design/references/rest-contracts.md +426 -0
- core/skills/auth-patterns/SKILL.md +352 -0
- core/skills/auth-patterns/assets/auth-checklist.md +118 -0
- core/skills/auth-patterns/references/jwt-and-service-tokens.md +343 -0
- core/skills/auth-patterns/references/passkeys-2fa.md +289 -0
- core/skills/auth-patterns/references/sessions-vs-jwt.md +230 -0
- core/skills/auth-patterns/scripts/cookie-flag-check.py +146 -0
- core/skills/backend-fundamentals/SKILL.md +238 -0
- core/skills/backend-fundamentals/assets/backend-checklist.md +27 -0
- core/skills/backend-fundamentals/references/backend-patterns.md +56 -0
- core/skills/backend-fundamentals/scripts/check_layering.py +83 -0
- core/skills/clean-code/SKILL.md +642 -0
- core/skills/clean-code/scripts/audit-fail-closed.py +167 -0
- core/skills/codebase-explorer/SKILL.md +89 -0
- core/skills/codebase-explorer/assets/reading-checklist.md +24 -0
- core/skills/codebase-explorer/references/reading-strategies.md +52 -0
- core/skills/codebase-explorer/scripts/outline.py +99 -0
- core/skills/db-design/SKILL.md +327 -0
- core/skills/db-design/assets/migration-template.sql +49 -0
- core/skills/db-design/references/migration-discipline.md +290 -0
- core/skills/db-design/references/postgres-patterns.md +340 -0
- core/skills/db-design/scripts/migration-safety.sh +150 -0
- core/skills/deployment-cicd/SKILL.md +260 -0
- core/skills/deployment-cicd/assets/deploy-checklist.md +26 -0
- core/skills/deployment-cicd/references/pipeline-and-release.md +54 -0
- core/skills/deployment-cicd/scripts/lint_workflow.py +79 -0
- core/skills/docker/SKILL.md +114 -0
- core/skills/docker/assets/dockerfile-checklist.md +31 -0
- core/skills/docker/references/compose-patterns.md +66 -0
- core/skills/docker/references/dockerfile-optimization.md +64 -0
- core/skills/docker/scripts/lint_dockerfile.sh +48 -0
- core/skills/docker/versions.json +16 -0
- core/skills/end-to-end-testing/SKILL.md +101 -0
- core/skills/end-to-end-testing/assets/e2e-checklist.md +23 -0
- core/skills/end-to-end-testing/references/maestro.md +63 -0
- core/skills/end-to-end-testing/references/playwright.md +68 -0
- core/skills/end-to-end-testing/scripts/lint_e2e.py +88 -0
- core/skills/end-to-end-testing/versions.json +16 -0
- core/skills/frontend-design/SKILL.md +76 -0
- core/skills/frontend-design/assets/design-checklist.md +29 -0
- core/skills/frontend-design/references/design-principles.md +65 -0
- core/skills/frontend-design/scripts/check_contrast.py +89 -0
- core/skills/frontend-fundamentals/SKILL.md +213 -0
- core/skills/frontend-fundamentals/assets/frontend-checklist.md +25 -0
- core/skills/frontend-fundamentals/references/rendering-and-state.md +66 -0
- core/skills/frontend-fundamentals/scripts/check_frontend.py +86 -0
- core/skills/graph-explorer/SKILL.md +215 -0
- core/skills/graph-explorer/scripts/explain-impact.sh +64 -0
- core/skills/graphql/SKILL.md +187 -0
- core/skills/grpc-microservices/SKILL.md +174 -0
- core/skills/hexagonal-architecture/SKILL.md +199 -0
- core/skills/hexagonal-architecture/assets/folder-scaffold.md +233 -0
- core/skills/hexagonal-architecture/references/anti-patterns.md +129 -0
- core/skills/hexagonal-architecture/references/go-fiber-layout.md +429 -0
- core/skills/hexagonal-architecture/references/python-fastapi-layout.md +453 -0
- core/skills/hexagonal-architecture/references/react-native-layout.md +428 -0
- core/skills/i18n/SKILL.md +126 -0
- core/skills/incident-response/SKILL.md +225 -0
- core/skills/incident-response/assets/incident-checklist.md +29 -0
- core/skills/incident-response/references/severity-and-runbook.md +53 -0
- core/skills/incident-response/scripts/classify_severity.py +86 -0
- core/skills/linux-sysadmin/SKILL.md +115 -0
- core/skills/linux-sysadmin/assets/hardening-checklist.md +29 -0
- core/skills/linux-sysadmin/references/ssh-hardening.md +62 -0
- core/skills/linux-sysadmin/references/systemd-and-services.md +77 -0
- core/skills/linux-sysadmin/scripts/triage.sh +50 -0
- core/skills/linux-sysadmin/versions.json +17 -0
- core/skills/llm-patterns/SKILL.md +410 -0
- core/skills/llm-patterns/assets/llm-feature-checklist.md +26 -0
- core/skills/llm-patterns/references/rag-and-evals.md +59 -0
- core/skills/llm-patterns/scripts/estimate_tokens.py +75 -0
- core/skills/messaging-queues/SKILL.md +142 -0
- core/skills/mobile-fundamentals/SKILL.md +406 -0
- core/skills/mobile-fundamentals/assets/mobile-launch-checklist.md +130 -0
- core/skills/mobile-fundamentals/references/navigation-and-deep-links.md +337 -0
- core/skills/mobile-fundamentals/references/offline-sync.md +339 -0
- core/skills/node-backend/SKILL.md +114 -0
- core/skills/node-backend/assets/node-checklist.md +25 -0
- core/skills/node-backend/references/async-and-errors.md +67 -0
- core/skills/node-backend/references/event-loop.md +65 -0
- core/skills/node-backend/scripts/check_package.py +78 -0
- core/skills/node-backend/versions.json +17 -0
- core/skills/observability/SKILL.md +289 -0
- core/skills/observability/assets/observability-checklist.md +27 -0
- core/skills/observability/references/instrumentation.md +57 -0
- core/skills/observability/scripts/lint_logging.py +77 -0
- core/skills/payments/SKILL.md +102 -0
- core/skills/performance/SKILL.md +305 -0
- core/skills/performance/assets/perf-checklist.md +143 -0
- core/skills/performance/references/mobile-performance.md +249 -0
- core/skills/performance/references/web-vitals.md +209 -0
- core/skills/php/SKILL.md +116 -0
- core/skills/php/assets/php-checklist.md +26 -0
- core/skills/php/references/modern-php.md +64 -0
- core/skills/php/references/security.md +72 -0
- core/skills/php/scripts/scan_php_smells.py +99 -0
- core/skills/php/versions.json +9 -0
- core/skills/pr-mode-driver/SKILL.md +65 -0
- core/skills/realtime-websockets/SKILL.md +152 -0
- core/skills/redis/SKILL.md +105 -0
- core/skills/redis/assets/redis-checklist.md +27 -0
- core/skills/redis/references/operations.md +66 -0
- core/skills/redis/references/patterns.md +62 -0
- core/skills/redis/scripts/analyze_info.py +101 -0
- core/skills/redis/versions.json +9 -0
- core/skills/search/SKILL.md +91 -0
- core/skills/search/references/grep.md +76 -0
- core/skills/search/scripts/verify-count.sh +73 -0
- core/skills/search-infra/SKILL.md +109 -0
- core/skills/security-mobile/SKILL.md +394 -0
- core/skills/security-mobile/assets/mobile-security-checklist.md +117 -0
- core/skills/security-mobile/references/masvs-l1-checklist.md +127 -0
- core/skills/security-web/SKILL.md +217 -0
- core/skills/security-web/assets/security-web-checklist.md +167 -0
- core/skills/security-web/references/owasp-top-10.md +551 -0
- core/skills/security-web/references/supply-chain.md +179 -0
- core/skills/security-web/scripts/csp-check.sh +153 -0
- core/skills/shell-scripting/SKILL.md +128 -0
- core/skills/shell-scripting/assets/script-checklist.md +33 -0
- core/skills/shell-scripting/references/argument-parsing.md +84 -0
- core/skills/shell-scripting/references/bash-robustness.md +78 -0
- core/skills/shell-scripting/scripts/lint_script.sh +53 -0
- core/skills/shell-scripting/scripts/new_script.py +131 -0
- core/skills/shell-scripting/versions.json +23 -0
- core/skills/sql-authoring/SKILL.md +104 -0
- core/skills/sql-authoring/assets/query-review-checklist.md +26 -0
- core/skills/sql-authoring/references/query-patterns.md +89 -0
- core/skills/sql-authoring/references/reading-explain.md +52 -0
- core/skills/sql-authoring/scripts/analyze_plan.py +100 -0
- core/skills/sql-authoring/versions.json +17 -0
- core/skills/state-management/SKILL.md +428 -0
- core/skills/state-management/references/tanstack-query-recipes.md +301 -0
- core/skills/state-management/references/zustand-recipes.md +364 -0
- core/skills/supabase/SKILL.md +108 -0
- core/skills/supabase/assets/supabase-checklist.md +26 -0
- core/skills/supabase/references/realtime-and-storage.md +55 -0
- core/skills/supabase/references/rls-and-auth.md +65 -0
- core/skills/supabase/scripts/check_rls.py +88 -0
- core/skills/supabase/versions.json +9 -0
- core/skills/task-driver/SKILL.md +272 -0
- core/skills/task-driver/scripts/task-lint.sh +163 -0
- core/skills/technical-writing/SKILL.md +85 -0
- core/skills/technical-writing/assets/doc-checklist.md +29 -0
- core/skills/technical-writing/references/doc-anatomy.md +53 -0
- core/skills/technical-writing/references/writing-craft.md +59 -0
- core/skills/technical-writing/scripts/new_doc.py +81 -0
- core/skills/terraform-k8s/SKILL.md +133 -0
- core/skills/testing-strategy/SKILL.md +266 -0
- core/skills/testing-strategy/assets/test-review-checklist.md +25 -0
- core/skills/testing-strategy/references/test-types.md +52 -0
- core/skills/testing-strategy/scripts/coverage_gate.py +79 -0
- core/skills/thinking_os/SKILL.md +288 -0
- core/skills/thinking_os/scripts/classify.sh +122 -0
- core/skills/typescript/SKILL.md +110 -0
- core/skills/typescript/assets/typescript-checklist.md +24 -0
- core/skills/typescript/references/strictness.md +53 -0
- core/skills/typescript/references/type-system.md +86 -0
- core/skills/typescript/scripts/check_tsconfig.py +92 -0
- core/skills/typescript/versions.json +9 -0
- core/subsystems.yaml +202 -0
- core/thinking_os/__init__.py +1 -0
- core/thinking_os/_agent_markers.py +32 -0
- core/thinking_os/agents/README.md +71 -0
- core/thinking_os/agents/analyst.md +139 -0
- core/thinking_os/agents/architect.md +127 -0
- core/thinking_os/agents/debugger.md +111 -0
- core/thinking_os/agents/deployer.md +112 -0
- core/thinking_os/agents/distiller.md +28 -0
- core/thinking_os/agents/documenter.md +101 -0
- core/thinking_os/agents/implementer.md +135 -0
- core/thinking_os/agents/internal/session_observer.md +39 -0
- core/thinking_os/agents/observer.md +100 -0
- core/thinking_os/agents/onboarder.md +81 -0
- core/thinking_os/agents/refactorer.md +123 -0
- core/thinking_os/agents/repairer.md +47 -0
- core/thinking_os/agents/researcher.md +129 -0
- core/thinking_os/agents/reviewer.md +147 -0
- core/thinking_os/agents/security_auditor.md +133 -0
- core/thinking_os/background.py +405 -0
- core/thinking_os/bootstrap_outcomes.py +200 -0
- core/thinking_os/budget.py +302 -0
- core/thinking_os/capture.py +495 -0
- core/thinking_os/cognition.py +516 -0
- core/thinking_os/cognition_schemas.py +517 -0
- core/thinking_os/compress.py +192 -0
- core/thinking_os/concepts.py +233 -0
- core/thinking_os/dashboard.py +159 -0
- core/thinking_os/database.py +2883 -0
- core/thinking_os/decay.py +393 -0
- core/thinking_os/digest.py +295 -0
- core/thinking_os/dispatcher.py +192 -0
- core/thinking_os/dispatcher_helpers.py +48 -0
- core/thinking_os/dispatchers/__init__.py +3 -0
- core/thinking_os/dispatchers/default.py +47 -0
- core/thinking_os/distill.py +192 -0
- core/thinking_os/doc_indexer.py +905 -0
- core/thinking_os/embeddings.py +943 -0
- core/thinking_os/formula_composer.py +556 -0
- core/thinking_os/gate_marker.py +75 -0
- core/thinking_os/graph.py +296 -0
- core/thinking_os/graph_indexer.py +360 -0
- core/thinking_os/health_check.py +517 -0
- core/thinking_os/impact.py +119 -0
- core/thinking_os/memory_gc.py +356 -0
- core/thinking_os/migrator_embeddings.py +318 -0
- core/thinking_os/precision.py +194 -0
- core/thinking_os/presets/registry.yaml +127 -0
- core/thinking_os/record_outcome.py +399 -0
- core/thinking_os/repair.py +105 -0
- core/thinking_os/retrieval_quality.py +239 -0
- core/thinking_os/roles/analyst.yaml +83 -0
- core/thinking_os/roles/architect.yaml +88 -0
- core/thinking_os/roles/debugger.yaml +71 -0
- core/thinking_os/roles/deployer.yaml +71 -0
- core/thinking_os/roles/documenter.yaml +72 -0
- core/thinking_os/roles/implementer.yaml +83 -0
- core/thinking_os/roles/observer.yaml +70 -0
- core/thinking_os/roles/refactorer.yaml +71 -0
- core/thinking_os/roles/researcher.yaml +72 -0
- core/thinking_os/roles/reviewer.yaml +79 -0
- core/thinking_os/roles/security_auditor.yaml +83 -0
- core/thinking_os/roles_state.py +168 -0
- core/thinking_os/sanitizer.py +320 -0
- core/thinking_os/server.py +3160 -0
- core/thinking_os/session_enrich.py +272 -0
- core/thinking_os/session_observe_worker.py +111 -0
- core/thinking_os/session_startup.py +74 -0
- core/thinking_os/session_summary.py +245 -0
- core/thinking_os/situations/registry.yaml +99 -0
- core/thinking_os/task_analyzer.py +462 -0
- core/thinking_os/task_parser.py +342 -0
- core/thinking_os/task_sync.py +73 -0
- core/thinking_os/tools/__init__.py +6 -0
- core/thinking_os/tools/_shared.py +947 -0
- core/thinking_os/tools/cognition.py +1867 -0
- core/thinking_os/tools/docs.py +770 -0
- core/thinking_os/tools/learning.py +2078 -0
- core/thinking_os/tools/logs.py +79 -0
- core/thinking_os/tools/memory.py +840 -0
- core/thinking_os/tools/metrics.py +200 -0
- core/thinking_os/tools/retrieve.py +415 -0
- core/thinking_os/tools/routing.py +658 -0
- core/thinking_os/tools/tasks.py +449 -0
- core/thinking_os/tools/trajectory.py +181 -0
- core/thinking_os/tracing.py +235 -0
- core/web/__init__.py +5 -0
- core/web/_cache.py +118 -0
- core/web/_deps.py +56 -0
- core/web/_envelope.py +85 -0
- core/web/_project_context.py +140 -0
- core/web/chat_providers.py +108 -0
- core/web/init_jobs.py +216 -0
- core/web/routes/__init__.py +25 -0
- core/web/routes/_bounded_read.py +76 -0
- core/web/routes/board.py +1089 -0
- core/web/routes/cognition.py +1838 -0
- core/web/routes/config.py +635 -0
- core/web/routes/graph.py +513 -0
- core/web/routes/health.py +180 -0
- core/web/routes/hooks.py +288 -0
- core/web/routes/hub.py +1219 -0
- core/web/routes/logs.py +374 -0
- core/web/routes/metrics.py +43 -0
- core/web/routes/observability.py +400 -0
- core/web/routes/patterns.py +227 -0
- core/web/routes/presence.py +609 -0
- core/web/routes/roles.py +446 -0
- core/web/routes/scheduled.py +261 -0
- core/web/routes/search.py +238 -0
- core/web/routes/sessions.py +220 -0
- core/web/routes/settings.py +363 -0
- core/web/routes/stream.py +547 -0
- core/web/security.py +157 -0
- core/web/server.py +283 -0
- graph_os/__init__.py +29 -0
- graph_os/backend.py +233 -0
- graph_os/backends/__init__.py +13 -0
- graph_os/backends/sqlite_backend.py +1053 -0
- graph_os/communities.py +410 -0
- graph_os/enterprise.py +218 -0
- graph_os/entry_points.py +226 -0
- graph_os/extractors/__init__.py +25 -0
- graph_os/extractors/code_generic.py +914 -0
- graph_os/extractors/code_go.py +1422 -0
- graph_os/extractors/code_json.py +340 -0
- graph_os/extractors/code_php.py +979 -0
- graph_os/extractors/code_python.py +1454 -0
- graph_os/extractors/code_shell.py +538 -0
- graph_os/extractors/code_toml.py +302 -0
- graph_os/extractors/code_ts.py +1665 -0
- graph_os/extractors/code_yaml.py +394 -0
- graph_os/extractors/contracts.py +1592 -0
- graph_os/extractors/md_links.py +890 -0
- graph_os/extractors/task_deps.py +345 -0
- graph_os/groups/__init__.py +22 -0
- graph_os/groups/cross_repo.py +156 -0
- graph_os/groups/manifest.py +141 -0
- graph_os/ingest/__init__.py +19 -0
- graph_os/ingest/base.py +306 -0
- graph_os/ingest/github.py +112 -0
- graph_os/ingest/zip.py +95 -0
- graph_os/toolchain.py +393 -0
- graph_os/tools/__init__.py +9 -0
- graph_os/tools/graph.py +5573 -0
- graph_os/tools/reindex_dispatch.py +730 -0
- graph_os/tree_sitter_overlay.py +235 -0
- graph_os/types.py +252 -0
- graph_os/vec_index.py +277 -0
- graph_os/viewer/__init__.py +12 -0
- graph_os/viewer/exporter.py +93 -0
- graph_os/viewer/template.py +189 -0
- scheduled/__init__.py +0 -0
- scheduled/_activity.py +126 -0
- scheduled/_state.py +113 -0
- scheduled/config.py +86 -0
- scheduled/dep_reconcile.py +135 -0
- scheduled/error_sweep.py +137 -0
- scheduled/nightly.py +930 -0
- scheduled/responsive_extract.py +65 -0
- scripts/__init__.py +4 -0
- scripts/_commit_msg_body.sh +31 -0
- scripts/_post_commit_body.sh +49 -0
- scripts/_pre_commit_body.sh +121 -0
- scripts/_prepare_commit_msg_body.sh +53 -0
- scripts/audit_mcp_tools.py +693 -0
- scripts/bench_sdk_dispatcher.py +177 -0
- scripts/capture_golden.py +169 -0
- scripts/check_graph_phantoms.py +75 -0
- scripts/dev/audit_doc_links.py +359 -0
- scripts/dev/audit_scaffold_module_tags.py +107 -0
- scripts/dev/backfill_doc_headers.py +328 -0
- scripts/dev/backfill_nav_lines.py +119 -0
- scripts/dev/fix_nav_placement.py +106 -0
- scripts/dev/inspect_sdk_options.py +45 -0
- scripts/dev/migrate_check_ids.py +170 -0
- scripts/dev/strip_purpose_blocks.py +154 -0
- scripts/dump_openapi.py +66 -0
- scripts/e2e_dispatch_tool.py +195 -0
- scripts/generate_manifest.py +166 -0
- scripts/golden_sections.py +20 -0
- scripts/graph_demo.py +161 -0
- scripts/install-git-hooks.sh +47 -0
- scripts/migrate_embeddings_minilm_to_bge_m3.py +84 -0
- scripts/operational_eval.py +445 -0
- scripts/probe_agent_session_resolver.py +59 -0
- scripts/prune_deleted_path.py +127 -0
- scripts/refactor_agent_dual_mode.py +171 -0
- scripts/refresh_skill_versions.py +302 -0
- scripts/regen_doc_index.py +209 -0
- scripts/regen_doctor_schema.py +62 -0
- scripts/regen_rules.py +94 -0
- scripts/rename_formulas_to_semantic.py +241 -0
- scripts/smoke_db_connections.py +183 -0
- scripts/smoke_doc_header.py +72 -0
- scripts/smoke_graph_e2e.py +374 -0
- scripts/smoke_sdk_dispatch.py +84 -0
- scripts/smoke_uid_resolver.py +164 -0
- scripts/verify_dispatchers.py +244 -0
- scripts/verify_phase_c_e2e.py +436 -0
- templates/__init__.py +6 -0
- templates/_base/Makefile.base +353 -0
- templates/_base/base.yaml +59 -0
- templates/_base/coding-os.yaml.template +39 -0
- templates/_base/dimension-registry.template.md +68 -0
- templates/_base/domain-config.template.json +46 -0
- templates/_base/fragments/anatomy-map.md.tmpl +11 -0
- templates/_base/fragments/context-discipline.md.tmpl +3 -0
- templates/_base/fragments/core-loop.md.tmpl +52 -0
- templates/_base/fragments/engineering-routing.md.tmpl +3 -0
- templates/_base/fragments/header.md.tmpl +6 -0
- templates/_base/fragments/identity.md.tmpl +3 -0
- templates/_base/fragments/principles.md.tmpl +3 -0
- templates/_base/fragments/retrieval-routing.md.tmpl +23 -0
- templates/_base/fragments/session-handoff.md.tmpl +3 -0
- templates/_base/fragments/skills.md.tmpl +3 -0
- templates/_base/fragments/ssot-map.md.tmpl +3 -0
- templates/_base/fragments/stop-conditions.md.tmpl +3 -0
- templates/_base/fragments/subagent-dispatch.md.tmpl +3 -0
- templates/_base/fragments/task-authoring.md.tmpl +69 -0
- templates/_base/fragments/task-logging.md.tmpl +8 -0
- templates/_base/fragments/tool-routing.md.tmpl +9 -0
- templates/_base/fragments/verification-matrix.md.tmpl +12 -0
- templates/_base/lang/dart/analysis_options.yaml +7 -0
- templates/_base/lang/php/phpcs.xml.dist +10 -0
- templates/_base/lang/python/pyproject.toml +19 -0
- templates/_base/lang/rust/clippy.toml +5 -0
- templates/_base/lang/rust/rustfmt.toml +3 -0
- templates/_base/lang/typescript/eslint.config.js +26 -0
- templates/_base/lang/typescript/tsconfig.json +15 -0
- templates/_base/lang/typescript/vitest.config.ts +10 -0
- templates/_base/scaffold/changes.log +1 -0
- templates/_base/scaffold/docs/00-index.md +55 -0
- templates/_base/scaffold/docs/_meta/feature-dependency-tree.md +30 -0
- templates/_base/scaffold/docs/_meta/foundation-map.md +56 -0
- templates/_base/scaffold/docs/_meta/questions.md +8 -0
- templates/_base/scaffold/docs/_meta/roadmap.md +33 -0
- templates/_base/scaffold/docs/api-contracts/00-index.md +58 -0
- templates/_base/scaffold/docs/api-contracts/error-format.md +58 -0
- templates/_base/scaffold/docs/architecture/00-index.md +42 -0
- templates/_base/scaffold/docs/architecture/adr/00-index.md +39 -0
- templates/_base/scaffold/docs/engineering/00-index.md +9 -0
- templates/_base/scaffold/docs/governance/00-index.md +55 -0
- templates/_base/scaffold/docs/governance/_templates/doc-cheat-sheet.md +202 -0
- templates/_base/scaffold/docs/governance/_templates/playbook-template.md +81 -0
- templates/_base/scaffold/docs/governance/_templates/post-mortem-template.md +85 -0
- templates/_base/scaffold/docs/governance/_templates/runbook-template.md +88 -0
- templates/_base/scaffold/docs/governance/_templates/security-review-template.md +111 -0
- templates/_base/scaffold/docs/governance/_templates/task-detail.md +61 -0
- templates/_base/scaffold/docs/governance/agent-workflow.md +101 -0
- templates/_base/scaffold/docs/governance/anatomy-contract.md +150 -0
- templates/_base/scaffold/docs/governance/critical-rules.md +224 -0
- templates/_base/scaffold/docs/governance/decision-records.md +58 -0
- templates/_base/scaffold/docs/governance/docs-first-protocol.md +157 -0
- templates/_base/scaffold/docs/governance/docs-system.md +151 -0
- templates/_base/scaffold/docs/governance/gdpr-compliance.md +66 -0
- templates/_base/scaffold/docs/governance/mcp-tool-inventory.md +112 -0
- templates/_base/scaffold/docs/governance/risk-register.md +26 -0
- templates/_base/scaffold/docs/governance/scaffold-boundary-contract.md +161 -0
- templates/_base/scaffold/docs/governance/task-lifecycle.md +125 -0
- templates/_base/scaffold/docs/governance/wrapper-derivation.md +50 -0
- templates/_base/scaffold/docs/insights/00-index.md +17 -0
- templates/_base/scaffold/docs/ops/00-index.md +59 -0
- templates/_base/scaffold/docs/ops/runbooks/00-index.md +9 -0
- templates/_base/scaffold/docs/playbooks/00-index.md +12 -0
- templates/_base/scaffold/docs/playbooks/research-validation.md +29 -0
- templates/_base/scaffold/docs/playbooks/security-review.md +41 -0
- templates/_base/scaffold/docs/prd/00-index.md +43 -0
- templates/_base/scaffold/docs/prd/01-snapshot-vision.md +56 -0
- templates/_base/scaffold/docs/workflow/workflow-guide.md +138 -0
- templates/_base/scaffold/src/shared/README.md +23 -0
- templates/_base/skill-enforcement.template.md +14 -0
- templates/_base/task-detail.template.md +61 -0
- templates/_presets/ai-saas.yaml +9 -0
- templates/_presets/django-next.yaml +8 -0
- templates/_presets/dotnet-react.yaml +8 -0
- templates/_presets/flutter-baas.yaml +8 -0
- templates/_presets/go-react.yaml +8 -0
- templates/_presets/hexagonal-product.yaml +16 -0
- templates/_presets/jamstack.yaml +8 -0
- templates/_presets/laravel-vue.yaml +8 -0
- templates/_presets/mean.yaml +8 -0
- templates/_presets/mern.yaml +9 -0
- templates/_presets/nest-angular.yaml +8 -0
- templates/_presets/nextjs-fastapi.yaml +10 -0
- templates/_presets/nuxt-fullstack.yaml +9 -0
- templates/_presets/pern.yaml +9 -0
- templates/_presets/rails-react.yaml +8 -0
- templates/_presets/rn-api.yaml +8 -0
- templates/_presets/rust-svelte.yaml +8 -0
- templates/_presets/spring-react.yaml +8 -0
- templates/_presets/t3-style.yaml +9 -0
- templates/_presets/tall.yaml +8 -0
- templates/_presets/wordpress-cms.yaml +8 -0
- templates/angular/rules/frontend.md +19 -0
- templates/angular/scaffold/docs/engineering/accessibility.md +46 -0
- templates/angular/scaffold/docs/engineering/angular-rules.md +35 -0
- templates/angular/scaffold/docs/playbooks/angular-app.md +42 -0
- templates/angular/scaffold/src/frontend/angular.json +52 -0
- templates/angular/scaffold/src/frontend/package.json +28 -0
- templates/angular/scaffold/src/frontend/src/app/app.component.ts +19 -0
- templates/angular/scaffold/src/frontend/src/app/app.config.ts +22 -0
- templates/angular/scaffold/src/frontend/src/app/app.routes.ts +8 -0
- templates/angular/scaffold/src/frontend/src/app/core/global-error-handler.ts +12 -0
- templates/angular/scaffold/src/frontend/src/app/health/health.component.ts +14 -0
- templates/angular/scaffold/src/frontend/src/app/health/health.service.spec.ts +16 -0
- templates/angular/scaffold/src/frontend/src/app/health/health.service.ts +10 -0
- templates/angular/scaffold/src/frontend/src/index.html +11 -0
- templates/angular/scaffold/src/frontend/src/main.ts +9 -0
- templates/angular/scaffold/src/frontend/src/styles.css +14 -0
- templates/angular/scaffold/src/frontend/tsconfig.app.json +8 -0
- templates/angular/scaffold/src/frontend/tsconfig.json +27 -0
- templates/angular/scaffold/src/frontend/tsconfig.spec.json +8 -0
- templates/angular/scaffold-boundary.yaml +27 -0
- templates/angular/skills/angular/SKILL.md +79 -0
- templates/angular/skills/angular/references/anatomy.md +69 -0
- templates/angular/stack.yaml +75 -0
- templates/aspnet-core/rules/backend.md +20 -0
- templates/aspnet-core/scaffold/docs/engineering/aspnet-core-rules.md +36 -0
- templates/aspnet-core/scaffold/docs/playbooks/aspnet-core-service.md +40 -0
- templates/aspnet-core/scaffold/src/backend/Backend.csproj +11 -0
- templates/aspnet-core/scaffold/src/backend/Backend.sln +27 -0
- templates/aspnet-core/scaffold/src/backend/Common/ExceptionHandlingMiddleware.cs +35 -0
- templates/aspnet-core/scaffold/src/backend/Features/Health/HealthEndpoints.cs +10 -0
- templates/aspnet-core/scaffold/src/backend/Features/Health/HealthService.cs +9 -0
- templates/aspnet-core/scaffold/src/backend/Program.cs +22 -0
- templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/Backend.Tests.csproj +22 -0
- templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/HealthServiceTests.cs +15 -0
- templates/aspnet-core/scaffold-boundary.yaml +24 -0
- templates/aspnet-core/skills/aspnet-core/SKILL.md +80 -0
- templates/aspnet-core/skills/aspnet-core/references/anatomy.md +65 -0
- templates/aspnet-core/stack.yaml +68 -0
- templates/astro/rules/frontend.md +20 -0
- templates/astro/scaffold/docs/engineering/astro-rules.md +40 -0
- templates/astro/scaffold/docs/playbooks/astro-app.md +57 -0
- templates/astro/scaffold/docs/playbooks/content-seo.md +37 -0
- templates/astro/scaffold/src/frontend/astro.config.mjs +10 -0
- templates/astro/scaffold/src/frontend/package.json +23 -0
- templates/astro/scaffold/src/frontend/src/components/Greeting.astro +13 -0
- templates/astro/scaffold/src/frontend/src/content/posts/hello.md +10 -0
- templates/astro/scaffold/src/frontend/src/content.config.ts +19 -0
- templates/astro/scaffold/src/frontend/src/lib/problem.test.ts +39 -0
- templates/astro/scaffold/src/frontend/src/lib/problem.ts +30 -0
- templates/astro/scaffold/src/frontend/src/pages/api/health.ts +13 -0
- templates/astro/scaffold/src/frontend/src/pages/index.astro +21 -0
- templates/astro/scaffold/src/frontend/tsconfig.json +9 -0
- templates/astro/scaffold/src/frontend/vitest.config.ts +10 -0
- templates/astro/scaffold-boundary.yaml +28 -0
- templates/astro/skills/astro/SKILL.md +71 -0
- templates/astro/skills/astro/references/anatomy.md +66 -0
- templates/astro/stack.yaml +75 -0
- templates/csharp-plain/scaffold/src/backend/Backend.csproj +12 -0
- templates/csharp-plain/scaffold/src/backend/Program.cs +1 -0
- templates/csharp-plain/scaffold-boundary.yaml +23 -0
- templates/csharp-plain/stack.yaml +50 -0
- templates/django/rules/backend.md +18 -0
- templates/django/scaffold/docs/engineering/anti-ambiguity.md +74 -0
- templates/django/scaffold/docs/engineering/backend-rules.md +133 -0
- templates/django/scaffold/docs/engineering/glossary.md +51 -0
- templates/django/scaffold/docs/engineering/logging-standards.md +107 -0
- templates/django/scaffold/docs/engineering/naming-conventions.md +68 -0
- templates/django/scaffold/docs/engineering/secrets-rotation-runbook.md +142 -0
- templates/django/scaffold/docs/playbooks/backend-api.md +119 -0
- templates/django/scaffold/src/backend/config/__init__.py +0 -0
- templates/django/scaffold/src/backend/config/settings.py +31 -0
- templates/django/scaffold/src/backend/config/urls.py +11 -0
- templates/django/scaffold/src/backend/config/wsgi.py +6 -0
- templates/django/scaffold/src/backend/manage.py +14 -0
- templates/django/scaffold/src/backend/pyproject.toml +37 -0
- templates/django/scaffold/src/backend/tests/test_health.py +4 -0
- templates/django/scaffold-boundary.yaml +25 -0
- templates/django/skills/python-django/SKILL.md +450 -0
- templates/django/skills/python-django/references/anatomy.md +117 -0
- templates/django/skills/python-django/scripts/new_endpoint.py +89 -0
- templates/django/stack.yaml +73 -0
- templates/fastapi/rules/backend.md +18 -0
- templates/fastapi/scaffold/docs/engineering/fastapi-rules.md +37 -0
- templates/fastapi/scaffold/docs/playbooks/fastapi-service.md +30 -0
- templates/fastapi/scaffold/src/backend/app/__init__.py +0 -0
- templates/fastapi/scaffold/src/backend/app/main.py +8 -0
- templates/fastapi/scaffold/src/backend/pyproject.toml +39 -0
- templates/fastapi/scaffold/src/backend/tests/test_health.py +11 -0
- templates/fastapi/scaffold-boundary.yaml +25 -0
- templates/fastapi/skills/python-fastapi/SKILL.md +75 -0
- templates/fastapi/skills/python-fastapi/references/anatomy.md +117 -0
- templates/fastapi/skills/python-fastapi/scripts/new_endpoint.py +101 -0
- templates/fastapi/stack.yaml +57 -0
- templates/flutter/rules/mobile.md +26 -0
- templates/flutter/scaffold/docs/engineering/flutter-rules.md +35 -0
- templates/flutter/scaffold/docs/playbooks/flutter-app.md +45 -0
- templates/flutter/scaffold/src/mobile/lib/core/error_mapper.dart +17 -0
- templates/flutter/scaffold/src/mobile/lib/core/router.dart +13 -0
- templates/flutter/scaffold/src/mobile/lib/main.dart +22 -0
- templates/flutter/scaffold/src/mobile/lib/screens/health_screen.dart +32 -0
- templates/flutter/scaffold/src/mobile/lib/services/health_service.dart +11 -0
- templates/flutter/scaffold/src/mobile/lib/state/health_provider.dart +13 -0
- templates/flutter/scaffold/src/mobile/pubspec.yaml +22 -0
- templates/flutter/scaffold/src/mobile/test/health_provider_test.dart +90 -0
- templates/flutter/scaffold-boundary.yaml +28 -0
- templates/flutter/skills/flutter/SKILL.md +76 -0
- templates/flutter/skills/flutter/references/anatomy.md +64 -0
- templates/flutter/stack.yaml +70 -0
- templates/go/rules/backend.md +19 -0
- templates/go/scaffold/docs/engineering/go-rules.md +45 -0
- templates/go/scaffold/docs/playbooks/go-service.md +30 -0
- templates/go/scaffold/src/backend/cmd/api/main.go +22 -0
- templates/go/scaffold/src/backend/cmd/api/main_test.go +20 -0
- templates/go/scaffold/src/backend/go.mod +3 -0
- templates/go/scaffold-boundary.yaml +25 -0
- templates/go/skills/go-patterns/SKILL.md +68 -0
- templates/go/skills/go-patterns/assets/go-checklist.md +29 -0
- templates/go/skills/go-patterns/references/anatomy.md +115 -0
- templates/go/skills/go-patterns/references/go-2026-idioms.md +92 -0
- templates/go/skills/go-patterns/scripts/new_endpoint.py +117 -0
- templates/go/skills/go-patterns/versions.json +16 -0
- templates/go/stack.yaml +54 -0
- templates/go-fiber/rules/backend.md +20 -0
- templates/go-fiber/scaffold/docs/engineering/fiber-rules.md +97 -0
- templates/go-fiber/scaffold/docs/playbooks/fiber-service.md +149 -0
- templates/go-fiber/scaffold/src/backend/cmd/api/main.go +21 -0
- templates/go-fiber/scaffold/src/backend/cmd/api/main_test.go +17 -0
- templates/go-fiber/scaffold/src/backend/go.mod +23 -0
- templates/go-fiber/scaffold/src/backend/go.sum +49 -0
- templates/go-fiber/scaffold-boundary.yaml +26 -0
- templates/go-fiber/skills/go-fiber/SKILL.md +203 -0
- templates/go-fiber/skills/go-fiber/assets/fiber-checklist.md +26 -0
- templates/go-fiber/skills/go-fiber/references/anatomy.md +116 -0
- templates/go-fiber/skills/go-fiber/references/fiber-v3-patterns.md +89 -0
- templates/go-fiber/skills/go-fiber/scripts/new_endpoint.py +107 -0
- templates/go-fiber/skills/go-fiber/versions.json +16 -0
- templates/go-fiber/stack.yaml +59 -0
- templates/go-plain/scaffold/src/backend/go.mod +3 -0
- templates/go-plain/scaffold/src/backend/main.go +7 -0
- templates/go-plain/scaffold-boundary.yaml +22 -0
- templates/go-plain/stack.yaml +51 -0
- templates/java-plain/scaffold/src/backend/mvnw +302 -0
- templates/java-plain/scaffold/src/backend/pom.xml +41 -0
- templates/java-plain/scaffold/src/backend/src/main/java/com/example/app/Main.java +10 -0
- templates/java-plain/scaffold-boundary.yaml +23 -0
- templates/java-plain/stack.yaml +51 -0
- templates/laravel/rules/backend.md +20 -0
- templates/laravel/scaffold/docs/engineering/laravel-rules.md +28 -0
- templates/laravel/scaffold/docs/playbooks/laravel-service.md +28 -0
- templates/laravel/scaffold/src/backend/app/Exceptions/Handler.php +27 -0
- templates/laravel/scaffold/src/backend/app/Http/Controllers/HealthController.php +15 -0
- templates/laravel/scaffold/src/backend/app/Support/HealthStatus.php +14 -0
- templates/laravel/scaffold/src/backend/composer.json +24 -0
- templates/laravel/scaffold/src/backend/phpunit.xml +10 -0
- templates/laravel/scaffold/src/backend/public/index.php +8 -0
- templates/laravel/scaffold/src/backend/routes/api.php +7 -0
- templates/laravel/scaffold/src/backend/tests/Unit/HealthStatusTest.php +16 -0
- templates/laravel/scaffold-boundary.yaml +23 -0
- templates/laravel/skills/laravel/SKILL.md +56 -0
- templates/laravel/skills/laravel/references/anatomy.md +65 -0
- templates/laravel/stack.yaml +66 -0
- templates/meta/rules/graph-first.md +27 -0
- templates/meta/rules/hook-author.md +19 -0
- templates/meta/rules/mcp-tool-author.md +18 -0
- templates/meta/rules/meta-engineering.md +17 -0
- templates/meta/scaffold-boundary.yaml +55 -0
- templates/meta/skills/claude-sdk-integration/SKILL.md +163 -0
- templates/meta/skills/claude-sdk-integration/assets/sdk-checklist.md +27 -0
- templates/meta/skills/claude-sdk-integration/scripts/check_model_ids.py +97 -0
- templates/meta/skills/graph-os-authoring/SKILL.md +278 -0
- templates/meta/skills/graph-os-authoring/assets/graph-os-checklist.md +25 -0
- templates/meta/skills/graph-os-authoring/scripts/new_extractor.py +76 -0
- templates/meta/skills/hook-authoring/SKILL.md +292 -0
- templates/meta/skills/hook-authoring/assets/hook-checklist.md +30 -0
- templates/meta/skills/hook-authoring/scripts/new_hook.sh +75 -0
- templates/meta/skills/mcp-tool-authoring/SKILL.md +301 -0
- templates/meta/skills/mcp-tool-authoring/assets/mcp-tool-checklist.md +29 -0
- templates/meta/skills/mcp-tool-authoring/scripts/new_tool.py +74 -0
- templates/meta/skills/meta-engineering/SKILL.md +151 -0
- templates/meta/skills/meta-engineering/assets/meta-edit-checklist.md +28 -0
- templates/meta/skills/meta-engineering/scripts/which_layer.py +61 -0
- templates/meta/skills/python-meta-server/SKILL.md +162 -0
- templates/meta/skills/python-meta-server/assets/meta-server-checklist.md +28 -0
- templates/meta/skills/python-meta-server/scripts/check_envelope.py +91 -0
- templates/meta/skills/react-vite-hub/SKILL.md +140 -0
- templates/meta/skills/react-vite-hub/assets/hub-ui-checklist.md +23 -0
- templates/meta/skills/react-vite-hub/scripts/check_vite_env.py +73 -0
- templates/meta/stack.yaml +119 -0
- templates/nestjs/rules/backend.md +20 -0
- templates/nestjs/scaffold/docs/engineering/nestjs-rules.md +33 -0
- templates/nestjs/scaffold/docs/playbooks/nestjs-service.md +39 -0
- templates/nestjs/scaffold/src/backend/nest-cli.json +5 -0
- templates/nestjs/scaffold/src/backend/package.json +28 -0
- templates/nestjs/scaffold/src/backend/src/app.module.ts +9 -0
- templates/nestjs/scaffold/src/backend/src/common/all-exceptions.filter.ts +59 -0
- templates/nestjs/scaffold/src/backend/src/health/health.controller.ts +14 -0
- templates/nestjs/scaffold/src/backend/src/health/health.module.ts +10 -0
- templates/nestjs/scaffold/src/backend/src/health/health.service.spec.ts +21 -0
- templates/nestjs/scaffold/src/backend/src/health/health.service.ts +9 -0
- templates/nestjs/scaffold/src/backend/src/main.ts +26 -0
- templates/nestjs/scaffold/src/backend/tsconfig.json +16 -0
- templates/nestjs/scaffold/src/backend/vitest.config.ts +9 -0
- templates/nestjs/scaffold-boundary.yaml +25 -0
- templates/nestjs/skills/nestjs/SKILL.md +67 -0
- templates/nestjs/skills/nestjs/references/anatomy.md +65 -0
- templates/nestjs/stack.yaml +68 -0
- templates/nextjs/rules/frontend.md +18 -0
- templates/nextjs/scaffold/docs/design/00-index.md +23 -0
- templates/nextjs/scaffold/docs/design/colors-tokens.md +141 -0
- templates/nextjs/scaffold/docs/design/components-patterns.md +159 -0
- templates/nextjs/scaffold/docs/design/motion-accessibility.md +137 -0
- templates/nextjs/scaffold/docs/design/typography-spacing.md +107 -0
- templates/nextjs/scaffold/docs/engineering/accessibility-web.md +56 -0
- templates/nextjs/scaffold/docs/engineering/copywriting-standard.md +102 -0
- templates/nextjs/scaffold/docs/engineering/formatting-rules.md +89 -0
- templates/nextjs/scaffold/docs/engineering/frontend-rendering-rules.md +80 -0
- templates/nextjs/scaffold/docs/engineering/frontend-rules.md +183 -0
- templates/nextjs/scaffold/docs/engineering/i18n-policy.md +99 -0
- templates/nextjs/scaffold/docs/pages-content-spec/00-index.md +78 -0
- templates/nextjs/scaffold/docs/playbooks/content-seo.md +55 -0
- templates/nextjs/scaffold/docs/playbooks/docs-governance.md +51 -0
- templates/nextjs/scaffold/docs/playbooks/frontend-ui.md +63 -0
- templates/nextjs/scaffold/src/frontend/app/layout.tsx +14 -0
- templates/nextjs/scaffold/src/frontend/app/page.tsx +3 -0
- templates/nextjs/scaffold/src/frontend/eslint.config.js +17 -0
- templates/nextjs/scaffold/src/frontend/lib/greeting.test.ts +9 -0
- templates/nextjs/scaffold/src/frontend/lib/greeting.ts +3 -0
- templates/nextjs/scaffold/src/frontend/package.json +28 -0
- templates/nextjs/scaffold/src/frontend/tsconfig.json +18 -0
- templates/nextjs/scaffold/src/frontend/vitest.config.ts +10 -0
- templates/nextjs/scaffold-boundary.yaml +30 -0
- templates/nextjs/skills/nextjs-react/SKILL.md +485 -0
- templates/nextjs/skills/nextjs-react/references/anatomy.md +116 -0
- templates/nextjs/skills/nextjs-react/scripts/new_component.py +72 -0
- templates/nextjs/stack.yaml +81 -0
- templates/node-express/rules/backend.md +20 -0
- templates/node-express/scaffold/docs/engineering/express-rules.md +30 -0
- templates/node-express/scaffold/docs/playbooks/express-service.md +35 -0
- templates/node-express/scaffold/src/backend/package.json +24 -0
- templates/node-express/scaffold/src/backend/src/index.ts +17 -0
- templates/node-express/scaffold/src/backend/src/middleware/error-handler.ts +12 -0
- templates/node-express/scaffold/src/backend/src/routes/health.test.ts +33 -0
- templates/node-express/scaffold/src/backend/src/routes/health.ts +7 -0
- templates/node-express/scaffold/src/backend/tsconfig.json +15 -0
- templates/node-express/scaffold/src/backend/types/express-bootstrap.d.ts +21 -0
- templates/node-express/scaffold-boundary.yaml +25 -0
- templates/node-express/skills/node-express/SKILL.md +70 -0
- templates/node-express/skills/node-express/references/anatomy.md +63 -0
- templates/node-express/stack.yaml +63 -0
- templates/python/scaffold/docs/engineering/python-rules.md +27 -0
- templates/python/scaffold/docs/playbooks/python-library.md +35 -0
- templates/python/stack.yaml +60 -0
- templates/rails/rules/backend.md +10 -0
- templates/rails/scaffold/docs/engineering/rails-rules.md +33 -0
- templates/rails/scaffold/docs/playbooks/rails-service.md +42 -0
- templates/rails/scaffold/src/backend/Gemfile +12 -0
- templates/rails/scaffold/src/backend/app/controllers/application_controller.rb +26 -0
- templates/rails/scaffold/src/backend/app/controllers/health_controller.rb +6 -0
- templates/rails/scaffold/src/backend/app/models/health.rb +6 -0
- templates/rails/scaffold/src/backend/config/application.rb +12 -0
- templates/rails/scaffold/src/backend/config/boot.rb +3 -0
- templates/rails/scaffold/src/backend/config/routes.rb +4 -0
- templates/rails/scaffold/src/backend/config.ru +5 -0
- templates/rails/scaffold/src/backend/spec/rails_helper.rb +18 -0
- templates/rails/scaffold/src/backend/spec/requests/health_spec.rb +24 -0
- templates/rails/scaffold-boundary.yaml +25 -0
- templates/rails/skills/rails/SKILL.md +62 -0
- templates/rails/skills/rails/references/anatomy.md +71 -0
- templates/rails/stack.yaml +72 -0
- templates/react-native/rules/mobile.md +26 -0
- templates/react-native/scaffold/docs/engineering/accessibility-mobile.md +95 -0
- templates/react-native/scaffold/docs/engineering/mobile-rules.md +56 -0
- templates/react-native/scaffold/docs/engineering/offline-first.md +61 -0
- templates/react-native/scaffold/docs/playbooks/mobile-app.md +49 -0
- templates/react-native/scaffold/src/mobile/App.tsx +9 -0
- templates/react-native/scaffold/src/mobile/eslint.config.js +17 -0
- templates/react-native/scaffold/src/mobile/package.json +23 -0
- templates/react-native/scaffold/src/mobile/src/greeting.test.ts +9 -0
- templates/react-native/scaffold/src/mobile/src/greeting.ts +3 -0
- templates/react-native/scaffold/src/mobile/tsconfig.json +17 -0
- templates/react-native/scaffold/src/mobile/vitest.config.ts +10 -0
- templates/react-native/scaffold-boundary.yaml +30 -0
- templates/react-native/skills/react-native-mobile/SKILL.md +119 -0
- templates/react-native/skills/react-native-mobile/assets/rn-mobile-checklist.md +28 -0
- templates/react-native/skills/react-native-mobile/references/anatomy.md +140 -0
- templates/react-native/skills/react-native-mobile/references/rn-2026-practices.md +54 -0
- templates/react-native/skills/react-native-mobile/scripts/new_screen.py +73 -0
- templates/react-native/skills/react-native-mobile/versions.json +16 -0
- templates/react-native/skills/react-native-patterns/SKILL.md +512 -0
- templates/react-native/skills/react-native-patterns/assets/rn-review-checklist.md +26 -0
- templates/react-native/skills/react-native-patterns/references/anatomy.md +62 -0
- templates/react-native/skills/react-native-patterns/references/list-performance.md +70 -0
- templates/react-native/skills/react-native-patterns/scripts/scan_rn_perf.py +77 -0
- templates/react-native/stack.yaml +70 -0
- templates/ruby-plain/scaffold/src/backend/Gemfile +8 -0
- templates/ruby-plain/scaffold/src/backend/main.rb +3 -0
- templates/ruby-plain/scaffold-boundary.yaml +23 -0
- templates/ruby-plain/stack.yaml +50 -0
- templates/rust-axum/rules/backend.md +20 -0
- templates/rust-axum/scaffold/docs/engineering/rust-axum-rules.md +35 -0
- templates/rust-axum/scaffold/docs/playbooks/rust-axum-service.md +43 -0
- templates/rust-axum/scaffold/src/backend/Cargo.toml +20 -0
- templates/rust-axum/scaffold/src/backend/src/app.rs +12 -0
- templates/rust-axum/scaffold/src/backend/src/error.rs +48 -0
- templates/rust-axum/scaffold/src/backend/src/main.rs +24 -0
- templates/rust-axum/scaffold/src/backend/src/routes/health.rs +37 -0
- templates/rust-axum/scaffold/src/backend/src/routes/mod.rs +2 -0
- templates/rust-axum/scaffold-boundary.yaml +25 -0
- templates/rust-axum/skills/rust/SKILL.md +73 -0
- templates/rust-axum/skills/rust/references/anatomy.md +63 -0
- templates/rust-axum/stack.yaml +65 -0
- templates/rust-plain/scaffold/src/backend/Cargo.toml +6 -0
- templates/rust-plain/scaffold/src/backend/src/main.rs +3 -0
- templates/rust-plain/scaffold-boundary.yaml +23 -0
- templates/rust-plain/stack.yaml +51 -0
- templates/spring-boot/rules/backend.md +20 -0
- templates/spring-boot/scaffold/docs/engineering/spring-boot-rules.md +35 -0
- templates/spring-boot/scaffold/docs/playbooks/spring-boot-service.md +45 -0
- templates/spring-boot/scaffold/src/backend/mvnw +302 -0
- templates/spring-boot/scaffold/src/backend/pom.xml +69 -0
- templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/Application.java +16 -0
- templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/common/GlobalExceptionHandler.java +31 -0
- templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthController.java +22 -0
- templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthService.java +12 -0
- templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthStatus.java +4 -0
- templates/spring-boot/scaffold/src/backend/src/test/java/com/example/app/health/HealthServiceTest.java +15 -0
- templates/spring-boot/scaffold-boundary.yaml +26 -0
- templates/spring-boot/skills/spring-boot/SKILL.md +84 -0
- templates/spring-boot/skills/spring-boot/references/anatomy.md +63 -0
- templates/spring-boot/stack.yaml +65 -0
- templates/svelte-sveltekit/rules/frontend.md +20 -0
- templates/svelte-sveltekit/scaffold/docs/engineering/svelte-sveltekit-rules.md +37 -0
- templates/svelte-sveltekit/scaffold/docs/playbooks/svelte-sveltekit-app.md +36 -0
- templates/svelte-sveltekit/scaffold/src/frontend/package.json +22 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/app.html +12 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/hooks.server.ts +14 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/lib/components/Greeting.svelte +6 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.test.ts +26 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.ts +4 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+layout.svelte +24 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.svelte +9 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.ts +7 -0
- templates/svelte-sveltekit/scaffold/src/frontend/src/routes/health/+server.ts +7 -0
- templates/svelte-sveltekit/scaffold/src/frontend/svelte.config.js +10 -0
- templates/svelte-sveltekit/scaffold/src/frontend/tsconfig.json +7 -0
- templates/svelte-sveltekit/scaffold/src/frontend/vite.config.ts +7 -0
- templates/svelte-sveltekit/scaffold/src/frontend/vitest.config.ts +11 -0
- templates/svelte-sveltekit/scaffold-boundary.yaml +29 -0
- templates/svelte-sveltekit/skills/svelte/SKILL.md +90 -0
- templates/svelte-sveltekit/skills/svelte/references/anatomy.md +62 -0
- templates/svelte-sveltekit/stack.yaml +70 -0
- templates/typescript-plain/scaffold/src/index.ts +3 -0
- templates/typescript-plain/scaffold/tsconfig.json +13 -0
- templates/typescript-plain/scaffold-boundary.yaml +22 -0
- templates/typescript-plain/stack.yaml +44 -0
- templates/vue-nuxt/rules/frontend.md +19 -0
- templates/vue-nuxt/scaffold/docs/engineering/nuxt-rules.md +30 -0
- templates/vue-nuxt/scaffold/docs/playbooks/nuxt-app.md +29 -0
- templates/vue-nuxt/scaffold/src/frontend/app.vue +3 -0
- templates/vue-nuxt/scaffold/src/frontend/nuxt.config.ts +11 -0
- templates/vue-nuxt/scaffold/src/frontend/package.json +20 -0
- templates/vue-nuxt/scaffold/src/frontend/pages/index.test.ts +19 -0
- templates/vue-nuxt/scaffold/src/frontend/pages/index.vue +11 -0
- templates/vue-nuxt/scaffold/src/frontend/vitest.config.ts +12 -0
- templates/vue-nuxt/scaffold-boundary.yaml +26 -0
- templates/vue-nuxt/skills/vue-nuxt/SKILL.md +57 -0
- templates/vue-nuxt/skills/vue-nuxt/references/anatomy.md +60 -0
- templates/vue-nuxt/stack.yaml +61 -0
- templates/wordpress/rules/backend.md +19 -0
- templates/wordpress/scaffold/docs/engineering/wordpress-rules.md +28 -0
- templates/wordpress/scaffold/docs/playbooks/wordpress-service.md +29 -0
- templates/wordpress/scaffold/src/backend/composer.json +17 -0
- templates/wordpress/scaffold/src/backend/phpcs.xml.dist +11 -0
- templates/wordpress/scaffold/src/backend/phpunit.xml +10 -0
- templates/wordpress/scaffold/src/backend/plugin/inc/health.php +8 -0
- templates/wordpress/scaffold/src/backend/plugin/plugin.php +28 -0
- templates/wordpress/scaffold/src/backend/tests/HealthStatusTest.php +15 -0
- templates/wordpress/scaffold/src/backend/theme/functions.php +18 -0
- templates/wordpress/scaffold/src/backend/theme/style.css +11 -0
- templates/wordpress/scaffold-boundary.yaml +23 -0
- templates/wordpress/skills/wordpress/SKILL.md +110 -0
- templates/wordpress/skills/wordpress/assets/wp-checklist.md +28 -0
- templates/wordpress/skills/wordpress/references/wp-development.md +75 -0
- templates/wordpress/skills/wordpress/references/wp-security.md +68 -0
- templates/wordpress/skills/wordpress/scripts/scan_wp_smells.py +91 -0
- templates/wordpress/skills/wordpress/versions.json +16 -0
- templates/wordpress/stack.yaml +60 -0
- thinking_os/__init__.py +1 -0
- thinking_os/_agent_markers.py +32 -0
- thinking_os/background.py +405 -0
- thinking_os/bootstrap_outcomes.py +200 -0
- thinking_os/budget.py +302 -0
- thinking_os/capture.py +495 -0
- thinking_os/cognition.py +516 -0
- thinking_os/cognition_schemas.py +517 -0
- thinking_os/compress.py +192 -0
- thinking_os/concepts.py +233 -0
- thinking_os/dashboard.py +159 -0
- thinking_os/database.py +2883 -0
- thinking_os/decay.py +393 -0
- thinking_os/digest.py +295 -0
- thinking_os/dispatcher.py +192 -0
- thinking_os/dispatcher_helpers.py +48 -0
- thinking_os/dispatchers/__init__.py +3 -0
- thinking_os/dispatchers/default.py +47 -0
- thinking_os/distill.py +192 -0
- thinking_os/doc_indexer.py +905 -0
- thinking_os/embeddings.py +943 -0
- thinking_os/formula_composer.py +556 -0
- thinking_os/gate_marker.py +75 -0
- thinking_os/graph.py +296 -0
- thinking_os/graph_indexer.py +360 -0
- thinking_os/health_check.py +517 -0
- thinking_os/impact.py +119 -0
- thinking_os/memory_gc.py +356 -0
- thinking_os/migrator_embeddings.py +318 -0
- thinking_os/precision.py +194 -0
- thinking_os/record_outcome.py +399 -0
- thinking_os/repair.py +105 -0
- thinking_os/retrieval_quality.py +239 -0
- thinking_os/roles_state.py +168 -0
- thinking_os/sanitizer.py +320 -0
- thinking_os/server.py +3160 -0
- thinking_os/session_enrich.py +272 -0
- thinking_os/session_observe_worker.py +111 -0
- thinking_os/session_startup.py +74 -0
- thinking_os/session_summary.py +245 -0
- thinking_os/task_analyzer.py +462 -0
- thinking_os/task_parser.py +342 -0
- thinking_os/task_sync.py +73 -0
- thinking_os/tools/__init__.py +6 -0
- thinking_os/tools/_shared.py +947 -0
- thinking_os/tools/cognition.py +1867 -0
- thinking_os/tools/docs.py +770 -0
- thinking_os/tools/learning.py +2078 -0
- thinking_os/tools/logs.py +79 -0
- thinking_os/tools/memory.py +840 -0
- thinking_os/tools/metrics.py +200 -0
- thinking_os/tools/retrieve.py +415 -0
- thinking_os/tools/routing.py +658 -0
- thinking_os/tools/tasks.py +449 -0
- thinking_os/tools/trajectory.py +181 -0
- thinking_os/tracing.py +235 -0
- web/__init__.py +5 -0
- web/_cache.py +118 -0
- web/_deps.py +56 -0
- web/_envelope.py +85 -0
- web/_project_context.py +140 -0
- web/chat_providers.py +108 -0
- web/init_jobs.py +216 -0
- web/routes/__init__.py +25 -0
- web/routes/_bounded_read.py +76 -0
- web/routes/board.py +1089 -0
- web/routes/cognition.py +1838 -0
- web/routes/config.py +635 -0
- web/routes/graph.py +513 -0
- web/routes/health.py +180 -0
- web/routes/hooks.py +288 -0
- web/routes/hub.py +1219 -0
- web/routes/logs.py +374 -0
- web/routes/metrics.py +43 -0
- web/routes/observability.py +400 -0
- web/routes/patterns.py +227 -0
- web/routes/presence.py +609 -0
- web/routes/roles.py +446 -0
- web/routes/scheduled.py +261 -0
- web/routes/search.py +238 -0
- web/routes/sessions.py +220 -0
- web/routes/settings.py +363 -0
- web/routes/stream.py +547 -0
- web/security.py +157 -0
- web/server.py +283 -0
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Coding OS — Vector embeddings module for RAG retrieval.
|
|
3
|
+
|
|
4
|
+
Provides semantic search via sentence-transformers + numpy cosine similarity
|
|
5
|
+
backed by a single SQLite `embeddings` table. Designed for graceful degradation:
|
|
6
|
+
if `sentence-transformers` or `numpy` is not installed, every public function
|
|
7
|
+
returns a safe falsy value and callers fall back to FTS5/LIKE.
|
|
8
|
+
|
|
9
|
+
Storage:
|
|
10
|
+
embeddings table (created by db.py migration v5):
|
|
11
|
+
id, source_table, source_id, text_hash, embedding (BLOB), model_name, created_at
|
|
12
|
+
UNIQUE(source_table, source_id)
|
|
13
|
+
|
|
14
|
+
Vector format:
|
|
15
|
+
numpy float32 array, 384 dimensions for `all-MiniLM-L6-v2`.
|
|
16
|
+
Stored as raw bytes (1536 bytes per vector).
|
|
17
|
+
|
|
18
|
+
Public API:
|
|
19
|
+
is_available() -> bool
|
|
20
|
+
embed_text(text) -> bytes | None
|
|
21
|
+
embed_texts(texts) -> list[bytes | None]
|
|
22
|
+
cosine_similarity(query, candidates) -> list[float]
|
|
23
|
+
upsert_embedding(conn, source_table, source_id, text) -> dict
|
|
24
|
+
search_similar(conn, query, source_tables, limit, threshold) -> list[dict]
|
|
25
|
+
has_embeddings_data(conn) -> bool
|
|
26
|
+
reindex_all(conn) -> dict
|
|
27
|
+
|
|
28
|
+
CLI entry point:
|
|
29
|
+
python -m embeddings --reindex
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import functools
|
|
35
|
+
import hashlib
|
|
36
|
+
import heapq
|
|
37
|
+
import logging
|
|
38
|
+
import os
|
|
39
|
+
import sqlite3
|
|
40
|
+
import sys
|
|
41
|
+
import time
|
|
42
|
+
from typing import Any
|
|
43
|
+
|
|
44
|
+
# Streaming batch sizes — bound peak memory regardless of table size (TASK-224).
|
|
45
|
+
# search streams candidate vectors; reindex streams + batch-embeds source rows.
|
|
46
|
+
_SEARCH_BATCH = 4096
|
|
47
|
+
_REINDEX_BATCH = 64
|
|
48
|
+
|
|
49
|
+
logger = logging.getLogger("coding_os.embeddings")
|
|
50
|
+
|
|
51
|
+
# Enterprise: never make unauthenticated HuggingFace Hub requests at runtime.
|
|
52
|
+
# Default to offline (use the locally-cached model only). A first-time vendoring
|
|
53
|
+
# download is explicit opt-in via COS_ALLOW_MODEL_DOWNLOAD=1, so the agent
|
|
54
|
+
# runtime never phones home without consent. setdefault respects an operator's
|
|
55
|
+
# own HF_HUB_OFFLINE choice.
|
|
56
|
+
if os.environ.get("COS_ALLOW_MODEL_DOWNLOAD") != "1":
|
|
57
|
+
os.environ.setdefault("HF_HUB_OFFLINE", "1")
|
|
58
|
+
os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
|
|
59
|
+
|
|
60
|
+
# Default model for FRESH projects — BAAI/bge-m3: 1024-dim, multilingual,
|
|
61
|
+
# ~4.3GB first-time download (vendored explicitly via COS_ALLOW_MODEL_DOWNLOAD=1).
|
|
62
|
+
# Per-project opt-back to MiniLM via COS_EMBEDDING_MODEL. The model a running
|
|
63
|
+
# process actually encodes with is active_model_name(), NOT this constant.
|
|
64
|
+
DEFAULT_MODEL_NAME = "BAAI/bge-m3"
|
|
65
|
+
EMBEDDING_DIM = 384 # Legacy MiniLM dim — last-resort fallback; per-model dims live in MODEL_DIMS
|
|
66
|
+
EMBEDDING_BYTES = EMBEDDING_DIM * 4 # float32 → 4 bytes per dimension
|
|
67
|
+
|
|
68
|
+
# Dual-model support during the MiniLM → BGE-M3 migration.
|
|
69
|
+
# Each entry: output dim per model. Callers can opt into BGE-M3 via the
|
|
70
|
+
# COS_EMBEDDING_MODEL env var or an explicit model_name kwarg. The DB
|
|
71
|
+
# remembers the model_name + embedding_dim per row so mixed populations
|
|
72
|
+
# are queryable through dim-aware cosine_similarity.
|
|
73
|
+
MODEL_DIMS: dict[str, int] = {
|
|
74
|
+
"all-MiniLM-L6-v2": 384,
|
|
75
|
+
"BAAI/bge-m3": 1024,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Source tables that support embedded retrieval. New tables can be added
|
|
79
|
+
# without code changes — just call upsert_embedding with the new table name.
|
|
80
|
+
DEFAULT_SOURCE_TABLES = (
|
|
81
|
+
"observations",
|
|
82
|
+
"learned_patterns",
|
|
83
|
+
"outcome_history",
|
|
84
|
+
"document_chunks",
|
|
85
|
+
"tasks",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# graph_node kinds worth embedding for semantic code search. Identifiers,
|
|
89
|
+
# imports, and external stubs are excluded — they are similarity noise (G21)
|
|
90
|
+
# and carry no meaningful label+signature+docstring text. Stored kinds are
|
|
91
|
+
# the canonical short forms (migration v16); see graph_os.types.NodeKind.
|
|
92
|
+
GRAPH_EMBED_KINDS: tuple[str, ...] = (
|
|
93
|
+
"function",
|
|
94
|
+
"method",
|
|
95
|
+
"class",
|
|
96
|
+
"route",
|
|
97
|
+
"mcp_tool",
|
|
98
|
+
"doc_heading",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _active_model_marker_path():
|
|
103
|
+
from pathlib import Path
|
|
104
|
+
|
|
105
|
+
state = os.environ.get("COS_STATE_DIR") or str(
|
|
106
|
+
Path(os.environ.get("COS_PROJECT_ROOT", os.getcwd())) / ".coding-os"
|
|
107
|
+
)
|
|
108
|
+
return Path(state) / ".embedding-model"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def active_model_name() -> str:
|
|
112
|
+
"""Return the model the current process encodes with.
|
|
113
|
+
|
|
114
|
+
SSOT order (M5): COS_EMBEDDING_MODEL env > persisted cutover marker
|
|
115
|
+
(.coding-os/.embedding-model) > DEFAULT_MODEL_NAME. The marker lets the
|
|
116
|
+
cutover flip every process to the new model at once after the corpus is
|
|
117
|
+
fully re-embedded, instead of each process guessing from its own env.
|
|
118
|
+
"""
|
|
119
|
+
env = os.environ.get("COS_EMBEDDING_MODEL", "").strip()
|
|
120
|
+
if env:
|
|
121
|
+
return env
|
|
122
|
+
try:
|
|
123
|
+
marker = _active_model_marker_path()
|
|
124
|
+
if marker.exists():
|
|
125
|
+
name = marker.read_text(encoding="utf-8").strip()
|
|
126
|
+
if name in MODEL_DIMS:
|
|
127
|
+
return name
|
|
128
|
+
except OSError as exc:
|
|
129
|
+
logger.debug("active-model marker read skipped: %s", exc)
|
|
130
|
+
return DEFAULT_MODEL_NAME
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def set_active_model(name: str) -> None:
|
|
134
|
+
"""Persist the active-model cutover marker (atomic tmp+replace)."""
|
|
135
|
+
if name not in MODEL_DIMS:
|
|
136
|
+
raise ValueError(f"unknown model {name!r}; known: {sorted(MODEL_DIMS)}")
|
|
137
|
+
marker = _active_model_marker_path()
|
|
138
|
+
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
139
|
+
tmp = marker.with_suffix(".tmp")
|
|
140
|
+
tmp.write_text(name, encoding="utf-8")
|
|
141
|
+
tmp.replace(marker)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# Calibrated similarity floors per model (measured 2026-06: BGE-M3 separates
|
|
145
|
+
# related code-symbol cosine ~0.84 from unrelated ~0.55; MiniLM barely
|
|
146
|
+
# separates 0.39 vs 0.35). The persisted-vector similar path caps its floor at
|
|
147
|
+
# this value so a legacy confidence_min default cannot suppress the fast path.
|
|
148
|
+
_PERSISTED_FLOORS: dict[str, float] = {
|
|
149
|
+
"all-MiniLM-L6-v2": 0.25,
|
|
150
|
+
"BAAI/bge-m3": 0.60,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def persisted_similarity_floor(model_name: str | None = None) -> float:
|
|
155
|
+
"""Calibrated cosine floor for the persisted-embedding similar path."""
|
|
156
|
+
return _PERSISTED_FLOORS.get(model_name or active_model_name(), 0.25)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# Calibrated cosine floors for DOC-CHUNK semantic search (cos_doc_search).
|
|
160
|
+
# Distinct from _PERSISTED_FLOORS: query-vs-doc-chunk cosines sit lower than
|
|
161
|
+
# code-symbol-vs-symbol ones, so the node floor (0.60) would discard genuine
|
|
162
|
+
# hits. Measured 2026-06 on the dogfood corpus: BGE-M3 related ~0.54-0.68 vs
|
|
163
|
+
# noise ~0.31-0.49 → 0.50 is the clean split. MiniLM keeps its legacy 0.05
|
|
164
|
+
# (it barely separates, so an aggressive floor just costs recall).
|
|
165
|
+
_DOC_FLOORS: dict[str, float] = {
|
|
166
|
+
"all-MiniLM-L6-v2": 0.05,
|
|
167
|
+
"BAAI/bge-m3": 0.50,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def doc_similarity_floor(model_name: str | None = None) -> float:
|
|
172
|
+
"""Calibrated cosine floor for cos_doc_search semantic ranking."""
|
|
173
|
+
return _DOC_FLOORS.get(model_name or active_model_name(), 0.05)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Calibrated cosine floor for AGENT-MEMORY semantic search (cos_search over
|
|
177
|
+
# observations + learned_patterns) and short task records. Measured 2026-07 on
|
|
178
|
+
# the dogfood corpus: genuine synonym matches on short memory text land ~0.50-0.62
|
|
179
|
+
# (a marketplace task at 0.51, a Celery obs at 0.50) while unrelated rows sit
|
|
180
|
+
# <=0.40 — so 0.45 clears real signal with margin below the lowest genuine hit
|
|
181
|
+
# and above the noise cluster. This floor is LOWER than doc chunks (0.50) and
|
|
182
|
+
# code symbols (0.60): short natural-language memory rows separate at lower
|
|
183
|
+
# cosines than either, so borrowing the code-symbol 0.55 (the prior value) just
|
|
184
|
+
# filtered genuine recall. MiniLM keeps its legacy 0.05 (it barely separates, so
|
|
185
|
+
# a hard floor only costs recall).
|
|
186
|
+
_MEMORY_FLOORS: dict[str, float] = {
|
|
187
|
+
"all-MiniLM-L6-v2": 0.05,
|
|
188
|
+
"BAAI/bge-m3": 0.45,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def memory_similarity_floor(model_name: str | None = None) -> float:
|
|
193
|
+
"""Calibrated cosine floor for agent-memory + task semantic search."""
|
|
194
|
+
return _MEMORY_FLOORS.get(model_name or active_model_name(), 0.05)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def migration_status(conn: sqlite3.Connection, target_model: str | None = None) -> dict:
|
|
198
|
+
"""Report re-embedding progress toward target_model (cutover-gate input)."""
|
|
199
|
+
target = target_model or active_model_name()
|
|
200
|
+
try:
|
|
201
|
+
total = conn.execute("SELECT COUNT(*) FROM embeddings").fetchone()[0]
|
|
202
|
+
remaining = conn.execute(
|
|
203
|
+
"SELECT COUNT(*) FROM embeddings WHERE model_name IS NULL OR model_name != ?",
|
|
204
|
+
(target,),
|
|
205
|
+
).fetchone()[0]
|
|
206
|
+
except sqlite3.OperationalError:
|
|
207
|
+
return {"target": target, "total": 0, "remaining": 0, "complete": False}
|
|
208
|
+
return {
|
|
209
|
+
"target": target,
|
|
210
|
+
"total": int(total),
|
|
211
|
+
"remaining": int(remaining),
|
|
212
|
+
"complete": total > 0 and remaining == 0,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def model_dim(model_name: str) -> int | None:
|
|
217
|
+
"""Return the expected vector dim for a known model, else None."""
|
|
218
|
+
return MODEL_DIMS.get(model_name)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def bytes_to_dim(payload: bytes | None) -> int | None:
|
|
222
|
+
"""Return dim inferred from a raw float32 blob (len / 4)."""
|
|
223
|
+
if not payload:
|
|
224
|
+
return None
|
|
225
|
+
if len(payload) % 4 != 0:
|
|
226
|
+
return None
|
|
227
|
+
return len(payload) // 4
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ---------------------------------------------------------------------------
|
|
231
|
+
# Availability detection — graceful degradation entry point
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@functools.lru_cache(maxsize=1)
|
|
236
|
+
def is_available() -> bool:
|
|
237
|
+
"""Return True iff sentence-transformers and numpy are importable.
|
|
238
|
+
|
|
239
|
+
Result is cached for the process lifetime — checking is cheap after the
|
|
240
|
+
first call.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
True if both `sentence_transformers` and `numpy` import successfully.
|
|
244
|
+
False otherwise — callers must handle this and fall back.
|
|
245
|
+
"""
|
|
246
|
+
try:
|
|
247
|
+
import numpy # noqa: F401
|
|
248
|
+
import sentence_transformers # noqa: F401
|
|
249
|
+
|
|
250
|
+
return True
|
|
251
|
+
except ImportError:
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@functools.lru_cache(maxsize=4)
|
|
256
|
+
def _get_model_by_name(name: str) -> Any:
|
|
257
|
+
"""Load and cache an embedding model by name."""
|
|
258
|
+
if not is_available():
|
|
259
|
+
return None
|
|
260
|
+
override = _MODEL_OVERRIDES.get(name)
|
|
261
|
+
if override is not None:
|
|
262
|
+
return override
|
|
263
|
+
try:
|
|
264
|
+
from sentence_transformers import SentenceTransformer
|
|
265
|
+
|
|
266
|
+
logger.info("Loading embedding model: %s", name)
|
|
267
|
+
return SentenceTransformer(name)
|
|
268
|
+
except Exception as exc:
|
|
269
|
+
if os.environ.get("COS_ALLOW_MODEL_DOWNLOAD") != "1":
|
|
270
|
+
logger.warning(
|
|
271
|
+
"Embedding model %s not in local cache; runtime downloads are "
|
|
272
|
+
"disabled — set COS_ALLOW_MODEL_DOWNLOAD=1 once to vendor it. "
|
|
273
|
+
"Falling back to lexical search. (%s)",
|
|
274
|
+
name,
|
|
275
|
+
exc,
|
|
276
|
+
)
|
|
277
|
+
else:
|
|
278
|
+
logger.warning("Failed to load embedding model %s: %s", name, exc)
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# Test hook: a mapping of model_name → pre-built encoder (duck-typed to
|
|
283
|
+
# expose .encode(...) with the sentence-transformers signature). Lets the
|
|
284
|
+
# test suite exercise dual-model behaviour without downloading BGE-M3.
|
|
285
|
+
_MODEL_OVERRIDES: dict[str, Any] = {}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _override_model(name: str, encoder: Any) -> None:
|
|
289
|
+
"""Test-only: install a fake encoder for the given model name."""
|
|
290
|
+
if encoder is None:
|
|
291
|
+
_MODEL_OVERRIDES.pop(name, None)
|
|
292
|
+
else:
|
|
293
|
+
_MODEL_OVERRIDES[name] = encoder
|
|
294
|
+
_get_model_by_name.cache_clear()
|
|
295
|
+
_get_model.cache_clear()
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@functools.lru_cache(maxsize=1)
|
|
299
|
+
def _get_model() -> Any:
|
|
300
|
+
"""Legacy shim — returns the active-model encoder.
|
|
301
|
+
|
|
302
|
+
Kept as an lru_cache'd function so existing tests that call
|
|
303
|
+
`_get_model.cache_clear()` continue to work after the
|
|
304
|
+
refactor. Defers to the multi-model loader.
|
|
305
|
+
"""
|
|
306
|
+
return _get_model_by_name(active_model_name())
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
# Embedding generation
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def embed_text(text: str, model_name: str | None = None) -> bytes | None:
|
|
315
|
+
"""Embed a single text string with the active (or explicit) model."""
|
|
316
|
+
if not text or not text.strip():
|
|
317
|
+
return None
|
|
318
|
+
# When the caller doesn't pick a model, route through the legacy
|
|
319
|
+
# _get_model() so existing tests that patch it keep working.
|
|
320
|
+
if model_name is None:
|
|
321
|
+
model = _get_model()
|
|
322
|
+
name = active_model_name()
|
|
323
|
+
else:
|
|
324
|
+
name = model_name
|
|
325
|
+
model = _get_model_by_name(name)
|
|
326
|
+
if model is None:
|
|
327
|
+
return None
|
|
328
|
+
try:
|
|
329
|
+
import numpy as np
|
|
330
|
+
|
|
331
|
+
vector = model.encode(text, convert_to_numpy=True, normalize_embeddings=True)
|
|
332
|
+
return np.asarray(vector, dtype=np.float32).tobytes()
|
|
333
|
+
except Exception as exc:
|
|
334
|
+
logger.warning("embed_text(%s) failed: %s", name, exc)
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def embed_texts(texts: list[str], model_name: str | None = None) -> list[bytes | None]:
|
|
339
|
+
"""Batch-embed with the active (or explicit) model."""
|
|
340
|
+
if not texts:
|
|
341
|
+
return []
|
|
342
|
+
if model_name is None:
|
|
343
|
+
model = _get_model()
|
|
344
|
+
name = active_model_name()
|
|
345
|
+
else:
|
|
346
|
+
name = model_name
|
|
347
|
+
model = _get_model_by_name(name)
|
|
348
|
+
if model is None:
|
|
349
|
+
return [None] * len(texts)
|
|
350
|
+
try:
|
|
351
|
+
import numpy as np
|
|
352
|
+
|
|
353
|
+
indices = [i for i, t in enumerate(texts) if t and t.strip()]
|
|
354
|
+
valid_texts = [texts[i] for i in indices]
|
|
355
|
+
if not valid_texts:
|
|
356
|
+
return [None] * len(texts)
|
|
357
|
+
vectors = model.encode(
|
|
358
|
+
valid_texts,
|
|
359
|
+
convert_to_numpy=True,
|
|
360
|
+
normalize_embeddings=True,
|
|
361
|
+
batch_size=32,
|
|
362
|
+
)
|
|
363
|
+
results: list[bytes | None] = [None] * len(texts)
|
|
364
|
+
for idx, vec in zip(indices, vectors):
|
|
365
|
+
results[idx] = np.asarray(vec, dtype=np.float32).tobytes()
|
|
366
|
+
return results
|
|
367
|
+
except Exception as exc:
|
|
368
|
+
logger.warning("embed_texts(%s) failed: %s", name, exc)
|
|
369
|
+
return [None] * len(texts)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# ---------------------------------------------------------------------------
|
|
373
|
+
# Similarity computation (numpy cosine on normalized vectors → just dot product)
|
|
374
|
+
# ---------------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def cosine_similarity(query_vec: bytes, candidate_vecs: list[bytes]) -> list[float]:
|
|
378
|
+
"""Compute cosine similarity — dim-aware."""
|
|
379
|
+
if not query_vec or not candidate_vecs:
|
|
380
|
+
return []
|
|
381
|
+
return cosine_similarity_with_meta(query_vec, candidate_vecs)["scores"]
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def cosine_similarity_with_meta(
|
|
385
|
+
query_vec: bytes,
|
|
386
|
+
candidate_vecs: list[bytes],
|
|
387
|
+
) -> dict:
|
|
388
|
+
"""Dim-aware cosine with diagnostic metadata."""
|
|
389
|
+
default = {
|
|
390
|
+
"scores": [0.0] * len(candidate_vecs),
|
|
391
|
+
"query_dim": bytes_to_dim(query_vec),
|
|
392
|
+
"dim_mismatch_skipped": 0,
|
|
393
|
+
"malformed_skipped": 0,
|
|
394
|
+
"total": len(candidate_vecs),
|
|
395
|
+
"matched": 0,
|
|
396
|
+
}
|
|
397
|
+
if not query_vec or not candidate_vecs:
|
|
398
|
+
return default
|
|
399
|
+
if not is_available():
|
|
400
|
+
return default
|
|
401
|
+
try:
|
|
402
|
+
import numpy as np
|
|
403
|
+
|
|
404
|
+
query_dim = bytes_to_dim(query_vec)
|
|
405
|
+
if query_dim is None:
|
|
406
|
+
default["malformed_skipped"] = 1
|
|
407
|
+
return default
|
|
408
|
+
query = np.frombuffer(query_vec, dtype=np.float32)
|
|
409
|
+
|
|
410
|
+
scores = [0.0] * len(candidate_vecs)
|
|
411
|
+
valid_rows: list[Any] = []
|
|
412
|
+
valid_idx: list[int] = []
|
|
413
|
+
dim_mismatch = 0
|
|
414
|
+
malformed = 0
|
|
415
|
+
|
|
416
|
+
for i, c in enumerate(candidate_vecs):
|
|
417
|
+
if not c:
|
|
418
|
+
malformed += 1
|
|
419
|
+
continue
|
|
420
|
+
c_dim = bytes_to_dim(c)
|
|
421
|
+
if c_dim is None:
|
|
422
|
+
malformed += 1
|
|
423
|
+
continue
|
|
424
|
+
if c_dim != query_dim:
|
|
425
|
+
dim_mismatch += 1
|
|
426
|
+
continue
|
|
427
|
+
valid_rows.append(np.frombuffer(c, dtype=np.float32))
|
|
428
|
+
valid_idx.append(i)
|
|
429
|
+
|
|
430
|
+
if valid_rows:
|
|
431
|
+
matrix = np.vstack(valid_rows)
|
|
432
|
+
raw = matrix @ query
|
|
433
|
+
for j, idx in enumerate(valid_idx):
|
|
434
|
+
scores[idx] = float(raw[j])
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
"scores": scores,
|
|
438
|
+
"query_dim": query_dim,
|
|
439
|
+
"dim_mismatch_skipped": dim_mismatch,
|
|
440
|
+
"malformed_skipped": malformed,
|
|
441
|
+
"total": len(candidate_vecs),
|
|
442
|
+
"matched": len(valid_rows),
|
|
443
|
+
}
|
|
444
|
+
except Exception as exc:
|
|
445
|
+
logger.warning("cosine_similarity_with_meta failed: %s", exc)
|
|
446
|
+
return default
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# ---------------------------------------------------------------------------
|
|
450
|
+
# Text hashing — staleness detection
|
|
451
|
+
# ---------------------------------------------------------------------------
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _compute_text_hash(text: str) -> str:
|
|
455
|
+
"""Return the first 16 hex chars of SHA256(text).
|
|
456
|
+
|
|
457
|
+
Matches the pattern used by capture._compute_content_hash so the codebase
|
|
458
|
+
has one consistent hashing convention.
|
|
459
|
+
"""
|
|
460
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
# ---------------------------------------------------------------------------
|
|
464
|
+
# DB helpers
|
|
465
|
+
# ---------------------------------------------------------------------------
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def has_embeddings_data(conn: sqlite3.Connection) -> bool:
|
|
469
|
+
"""Return True if the embeddings table exists and contains at least one row."""
|
|
470
|
+
try:
|
|
471
|
+
row = conn.execute(
|
|
472
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'"
|
|
473
|
+
).fetchone()
|
|
474
|
+
if row is None:
|
|
475
|
+
return False
|
|
476
|
+
count = conn.execute("SELECT COUNT(*) FROM embeddings").fetchone()
|
|
477
|
+
return bool(count and count[0] > 0)
|
|
478
|
+
except sqlite3.OperationalError:
|
|
479
|
+
return False
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def upsert_embedding(
|
|
483
|
+
conn: sqlite3.Connection,
|
|
484
|
+
source_table: str,
|
|
485
|
+
source_id: int,
|
|
486
|
+
text: str,
|
|
487
|
+
*,
|
|
488
|
+
model_name: str | None = None,
|
|
489
|
+
) -> dict:
|
|
490
|
+
"""Insert or refresh an embedding row with the active model."""
|
|
491
|
+
if not is_available():
|
|
492
|
+
return {"status": "skipped", "reason": "unavailable"}
|
|
493
|
+
if not text or not text.strip():
|
|
494
|
+
return {"status": "skipped", "reason": "empty_text"}
|
|
495
|
+
|
|
496
|
+
name = model_name or active_model_name()
|
|
497
|
+
text_hash = _compute_text_hash(text)
|
|
498
|
+
try:
|
|
499
|
+
existing = conn.execute(
|
|
500
|
+
"SELECT id, text_hash, model_name FROM embeddings "
|
|
501
|
+
"WHERE source_table = ? AND source_id = ?",
|
|
502
|
+
(source_table, source_id),
|
|
503
|
+
).fetchone()
|
|
504
|
+
except sqlite3.OperationalError as exc:
|
|
505
|
+
return {"status": "skipped", "reason": f"table_missing: {exc}"}
|
|
506
|
+
|
|
507
|
+
if existing and existing[1] == text_hash and existing[2] == name:
|
|
508
|
+
return {"status": "unchanged", "id": existing[0]}
|
|
509
|
+
|
|
510
|
+
vector = embed_text(text, model_name=name)
|
|
511
|
+
if vector is None:
|
|
512
|
+
return {"status": "skipped", "reason": "embed_failed"}
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
status, row_id, dim = _persist_embedding(
|
|
516
|
+
conn,
|
|
517
|
+
source_table,
|
|
518
|
+
source_id,
|
|
519
|
+
text_hash,
|
|
520
|
+
vector,
|
|
521
|
+
name,
|
|
522
|
+
existing[0] if existing else None,
|
|
523
|
+
_has_embedding_dim_column(conn),
|
|
524
|
+
)
|
|
525
|
+
conn.commit()
|
|
526
|
+
return {"status": status, "id": row_id, "dim": dim, "model_name": name}
|
|
527
|
+
except sqlite3.OperationalError as exc:
|
|
528
|
+
return {"status": "error", "reason": str(exc)}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def _persist_embedding(
|
|
532
|
+
conn: sqlite3.Connection,
|
|
533
|
+
source_table: str,
|
|
534
|
+
source_id: int,
|
|
535
|
+
text_hash: str,
|
|
536
|
+
vector: bytes,
|
|
537
|
+
name: str,
|
|
538
|
+
existing_id: int | None,
|
|
539
|
+
has_dim_col: bool,
|
|
540
|
+
) -> tuple[str, int, int]:
|
|
541
|
+
"""Write one embedding row (INSERT or UPDATE). Caller commits.
|
|
542
|
+
|
|
543
|
+
Shared by upsert_embedding (single) and reindex_all (batched). Returns
|
|
544
|
+
(status, row_id, dim). `has_dim_col` tolerates pre-v12 DBs missing the
|
|
545
|
+
embedding_dim column.
|
|
546
|
+
"""
|
|
547
|
+
dim = bytes_to_dim(vector) or model_dim(name) or EMBEDDING_DIM
|
|
548
|
+
if existing_id is not None:
|
|
549
|
+
if has_dim_col:
|
|
550
|
+
conn.execute(
|
|
551
|
+
"UPDATE embeddings SET text_hash = ?, embedding = ?, model_name = ?, "
|
|
552
|
+
"embedding_dim = ?, created_at = CURRENT_TIMESTAMP WHERE id = ?",
|
|
553
|
+
(text_hash, vector, name, dim, existing_id),
|
|
554
|
+
)
|
|
555
|
+
else:
|
|
556
|
+
conn.execute(
|
|
557
|
+
"UPDATE embeddings SET text_hash = ?, embedding = ?, model_name = ?, "
|
|
558
|
+
"created_at = CURRENT_TIMESTAMP WHERE id = ?",
|
|
559
|
+
(text_hash, vector, name, existing_id),
|
|
560
|
+
)
|
|
561
|
+
return "updated", existing_id, dim
|
|
562
|
+
|
|
563
|
+
if has_dim_col:
|
|
564
|
+
cursor = conn.execute(
|
|
565
|
+
"INSERT INTO embeddings (source_table, source_id, text_hash, "
|
|
566
|
+
"embedding, model_name, embedding_dim) VALUES (?, ?, ?, ?, ?, ?)",
|
|
567
|
+
(source_table, source_id, text_hash, vector, name, dim),
|
|
568
|
+
)
|
|
569
|
+
else:
|
|
570
|
+
cursor = conn.execute(
|
|
571
|
+
"INSERT INTO embeddings (source_table, source_id, text_hash, "
|
|
572
|
+
"embedding, model_name) VALUES (?, ?, ?, ?, ?)",
|
|
573
|
+
(source_table, source_id, text_hash, vector, name),
|
|
574
|
+
)
|
|
575
|
+
return "inserted", int(cursor.lastrowid), dim
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _has_embedding_dim_column(conn: sqlite3.Connection) -> bool:
|
|
579
|
+
"""Tolerate pre-v12 DBs that lack the embedding_dim column."""
|
|
580
|
+
try:
|
|
581
|
+
rows = conn.execute("PRAGMA table_info(embeddings)").fetchall()
|
|
582
|
+
except sqlite3.OperationalError:
|
|
583
|
+
return False
|
|
584
|
+
return any(r[1] == "embedding_dim" for r in rows)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
# ---------------------------------------------------------------------------
|
|
588
|
+
# Search
|
|
589
|
+
# ---------------------------------------------------------------------------
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def search_similar(
|
|
593
|
+
conn: sqlite3.Connection,
|
|
594
|
+
query: str,
|
|
595
|
+
source_tables: list[str] | None = None,
|
|
596
|
+
limit: int = 5,
|
|
597
|
+
threshold: float = 0.1,
|
|
598
|
+
) -> list[dict]:
|
|
599
|
+
"""Find rows whose embeddings are most similar to the query text.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
conn: Open SQLite connection.
|
|
603
|
+
query: Natural language query.
|
|
604
|
+
source_tables: Optional filter — only return matches from these
|
|
605
|
+
source_table values. Defaults to None (all tables).
|
|
606
|
+
limit: Max results (capped at 100).
|
|
607
|
+
threshold: Minimum cosine similarity to return (default 0.1 — tuned
|
|
608
|
+
for all-MiniLM-L6-v2 on short text where typical "related" scores
|
|
609
|
+
land in the 0.1-0.3 range and "unrelated" stays below 0.1).
|
|
610
|
+
|
|
611
|
+
Returns:
|
|
612
|
+
List of dicts: [{source_table, source_id, score}], sorted desc by score.
|
|
613
|
+
Empty list if embeddings unavailable, table missing, or query empty.
|
|
614
|
+
"""
|
|
615
|
+
if not query or not query.strip():
|
|
616
|
+
return []
|
|
617
|
+
if not is_available():
|
|
618
|
+
return []
|
|
619
|
+
if not has_embeddings_data(conn):
|
|
620
|
+
return []
|
|
621
|
+
|
|
622
|
+
# Dual-model bridge (M4): a DB mid-migration holds vectors from >1 model
|
|
623
|
+
# at different dims. Encode the query with EVERY model present in the
|
|
624
|
+
# target population, then score each candidate against the matching-dim
|
|
625
|
+
# query vector. Dim-aware cosine returns 0 for mismatched dims, so an
|
|
626
|
+
# element-wise max over the per-model score vectors collapses to "the
|
|
627
|
+
# score from the candidate's own model" — no row is ever silently
|
|
628
|
+
# invisible regardless of migration progress.
|
|
629
|
+
model_sql = "SELECT DISTINCT model_name FROM embeddings"
|
|
630
|
+
model_params: list[Any] = []
|
|
631
|
+
if source_tables:
|
|
632
|
+
ph = ",".join("?" * len(source_tables))
|
|
633
|
+
model_sql += f" WHERE source_table IN ({ph})"
|
|
634
|
+
model_params.extend(source_tables)
|
|
635
|
+
try:
|
|
636
|
+
present_models = [r[0] for r in conn.execute(model_sql, model_params).fetchall() if r[0]]
|
|
637
|
+
except sqlite3.OperationalError:
|
|
638
|
+
present_models = []
|
|
639
|
+
if not present_models:
|
|
640
|
+
present_models = [active_model_name()]
|
|
641
|
+
query_vecs = [v for v in (embed_text(query, model_name=m) for m in present_models) if v]
|
|
642
|
+
if not query_vecs:
|
|
643
|
+
return []
|
|
644
|
+
|
|
645
|
+
# Cap limit to prevent runaway queries
|
|
646
|
+
limit = max(1, min(int(limit), 100))
|
|
647
|
+
|
|
648
|
+
sql = "SELECT source_table, source_id, embedding FROM embeddings"
|
|
649
|
+
params: list[Any] = []
|
|
650
|
+
if source_tables:
|
|
651
|
+
placeholders = ",".join("?" * len(source_tables))
|
|
652
|
+
sql += f" WHERE source_table IN ({placeholders})"
|
|
653
|
+
params.extend(source_tables)
|
|
654
|
+
|
|
655
|
+
try:
|
|
656
|
+
cursor = conn.execute(sql, params)
|
|
657
|
+
except sqlite3.OperationalError as exc:
|
|
658
|
+
logger.warning("search_similar query failed: %s", exc)
|
|
659
|
+
return []
|
|
660
|
+
|
|
661
|
+
# Stream candidates in batches and keep only a top-`limit` min-heap, so peak
|
|
662
|
+
# memory is one batch of vectors + `limit` results — never the whole table
|
|
663
|
+
# (1M embeddings fetched at once would be ~1.5 GB of RAM). TASK-224.
|
|
664
|
+
heap: list[tuple[float, int, dict]] = []
|
|
665
|
+
seq = 0
|
|
666
|
+
while True:
|
|
667
|
+
batch = cursor.fetchmany(_SEARCH_BATCH)
|
|
668
|
+
if not batch:
|
|
669
|
+
break
|
|
670
|
+
cand_vecs = [r[2] for r in batch]
|
|
671
|
+
if len(query_vecs) == 1:
|
|
672
|
+
scores = cosine_similarity(query_vecs[0], cand_vecs)
|
|
673
|
+
else:
|
|
674
|
+
per_model = [cosine_similarity(qv, cand_vecs) for qv in query_vecs]
|
|
675
|
+
scores = [max(col) for col in zip(*per_model)]
|
|
676
|
+
for i, score in enumerate(scores):
|
|
677
|
+
if score < threshold:
|
|
678
|
+
continue
|
|
679
|
+
entry = (
|
|
680
|
+
score,
|
|
681
|
+
seq,
|
|
682
|
+
{"source_table": batch[i][0], "source_id": batch[i][1], "score": score},
|
|
683
|
+
)
|
|
684
|
+
if len(heap) < limit:
|
|
685
|
+
heapq.heappush(heap, entry)
|
|
686
|
+
elif score > heap[0][0]:
|
|
687
|
+
heapq.heapreplace(heap, entry)
|
|
688
|
+
seq += 1
|
|
689
|
+
|
|
690
|
+
results = [item for _, _, item in heap]
|
|
691
|
+
results.sort(key=lambda d: d["score"], reverse=True)
|
|
692
|
+
return results
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
# ---------------------------------------------------------------------------
|
|
696
|
+
# Outbox — durable deferral of hot-path-skipped embeddings (Wave 4)
|
|
697
|
+
# ---------------------------------------------------------------------------
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def enqueue_outbox(conn: sqlite3.Connection, source_table: str, source_id: int) -> bool:
|
|
701
|
+
"""Record that (source_table, source_id) needs an embedding, off the hot path.
|
|
702
|
+
|
|
703
|
+
Idempotent via UNIQUE(source_table, source_id) — re-enqueueing is a no-op.
|
|
704
|
+
Cheap (one INSERT, no model load) so the capture hot path stays fast.
|
|
705
|
+
"""
|
|
706
|
+
try:
|
|
707
|
+
conn.execute(
|
|
708
|
+
"INSERT OR IGNORE INTO embedding_outbox (source_table, source_id, enqueued_at) "
|
|
709
|
+
"VALUES (?, ?, ?)",
|
|
710
|
+
(source_table, int(source_id), int(time.time())),
|
|
711
|
+
)
|
|
712
|
+
conn.commit()
|
|
713
|
+
return True
|
|
714
|
+
except sqlite3.OperationalError as exc:
|
|
715
|
+
logger.debug("outbox enqueue skipped (%s): %s", source_table, exc)
|
|
716
|
+
return False
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
def drain_outbox(conn: sqlite3.Connection, *, limit: int = 128, max_attempts: int = 3) -> dict:
|
|
720
|
+
"""Embed up to `limit` pending outbox rows; remove on success, retry-bounded.
|
|
721
|
+
|
|
722
|
+
Runs off the interactive path (Stop hook / cron). A row whose source text
|
|
723
|
+
is gone is dropped; a transient embed failure increments attempts and keeps
|
|
724
|
+
last_error, and is abandoned after max_attempts so the queue can't wedge.
|
|
725
|
+
"""
|
|
726
|
+
# Self-heal: drop outbox rows already satisfied by an embedding (any path may
|
|
727
|
+
# have embedded them). Model-free, so it runs even when the encoder is down —
|
|
728
|
+
# otherwise these rows leak in the queue forever.
|
|
729
|
+
try:
|
|
730
|
+
conn.execute(
|
|
731
|
+
"DELETE FROM embedding_outbox WHERE EXISTS ("
|
|
732
|
+
"SELECT 1 FROM embeddings e WHERE e.source_table = embedding_outbox.source_table "
|
|
733
|
+
"AND e.source_id = embedding_outbox.source_id)"
|
|
734
|
+
)
|
|
735
|
+
conn.commit()
|
|
736
|
+
except sqlite3.OperationalError as exc:
|
|
737
|
+
logger.debug("outbox reconcile skipped (no table?): %s", exc)
|
|
738
|
+
if not is_available():
|
|
739
|
+
return {"status": "unavailable", "drained": 0, "failed": 0, "remaining": 0}
|
|
740
|
+
try:
|
|
741
|
+
rows = conn.execute(
|
|
742
|
+
"SELECT id, source_table, source_id FROM embedding_outbox "
|
|
743
|
+
"WHERE attempts < ? ORDER BY attempts, enqueued_at LIMIT ?",
|
|
744
|
+
(int(max_attempts), max(1, int(limit))),
|
|
745
|
+
).fetchall()
|
|
746
|
+
except sqlite3.OperationalError as exc:
|
|
747
|
+
logger.debug("outbox drain skipped (no table?): %s", exc)
|
|
748
|
+
return {"status": "no_table", "drained": 0, "failed": 0, "remaining": 0}
|
|
749
|
+
if not rows:
|
|
750
|
+
return {"status": "ok", "drained": 0, "failed": 0, "remaining": 0}
|
|
751
|
+
|
|
752
|
+
# Reuse the migrator's per-table text reconstruction (single source of truth
|
|
753
|
+
# for "how to rebuild the text behind an embedding"). Lazy import avoids the
|
|
754
|
+
# embeddings<->migrator circular import at module load.
|
|
755
|
+
from migrator_embeddings import _text_for_row # type: ignore
|
|
756
|
+
|
|
757
|
+
drained = failed = 0
|
|
758
|
+
for oid, source_table, source_id in rows:
|
|
759
|
+
text = _text_for_row(conn, {"source_table": source_table, "source_id": source_id})
|
|
760
|
+
if not text:
|
|
761
|
+
conn.execute("DELETE FROM embedding_outbox WHERE id = ?", (oid,))
|
|
762
|
+
continue
|
|
763
|
+
res = upsert_embedding(conn, source_table, int(source_id), text)
|
|
764
|
+
if res.get("status") in ("inserted", "updated", "unchanged"):
|
|
765
|
+
conn.execute("DELETE FROM embedding_outbox WHERE id = ?", (oid,))
|
|
766
|
+
drained += 1
|
|
767
|
+
else:
|
|
768
|
+
conn.execute(
|
|
769
|
+
"UPDATE embedding_outbox SET attempts = attempts + 1, last_error = ? WHERE id = ?",
|
|
770
|
+
(str(res.get("reason"))[:200], oid),
|
|
771
|
+
)
|
|
772
|
+
failed += 1
|
|
773
|
+
conn.commit()
|
|
774
|
+
remaining = conn.execute(
|
|
775
|
+
"SELECT COUNT(*) FROM embedding_outbox WHERE attempts < ?", (int(max_attempts),)
|
|
776
|
+
).fetchone()[0]
|
|
777
|
+
return {"status": "ok", "drained": drained, "failed": failed, "remaining": int(remaining)}
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
# ---------------------------------------------------------------------------
|
|
781
|
+
# Reindex (bootstrap / model upgrade)
|
|
782
|
+
# ---------------------------------------------------------------------------
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
def reindex_all(conn: sqlite3.Connection) -> dict:
|
|
786
|
+
"""Re-embed every row in every supported source table.
|
|
787
|
+
|
|
788
|
+
Walks observations, learned_patterns, outcome_history, document_chunks,
|
|
789
|
+
and tasks (when present), and calls upsert_embedding for each. Skips
|
|
790
|
+
rows that already have an unchanged embedding (text_hash match).
|
|
791
|
+
|
|
792
|
+
Used by `make cos-reindex` after a model upgrade or to bootstrap an
|
|
793
|
+
existing DB.
|
|
794
|
+
|
|
795
|
+
Returns:
|
|
796
|
+
{table_name: {processed, inserted, updated, unchanged, skipped}}.
|
|
797
|
+
"""
|
|
798
|
+
if not is_available():
|
|
799
|
+
return {"status": "skipped", "reason": "unavailable"}
|
|
800
|
+
|
|
801
|
+
report: dict[str, dict[str, int]] = {}
|
|
802
|
+
|
|
803
|
+
handlers = [
|
|
804
|
+
(
|
|
805
|
+
"observations",
|
|
806
|
+
"SELECT id, title, narrative, concepts FROM observations",
|
|
807
|
+
lambda r: " ".join(filter(None, [r["title"], r["narrative"], r["concepts"]])),
|
|
808
|
+
),
|
|
809
|
+
(
|
|
810
|
+
"learned_patterns",
|
|
811
|
+
"SELECT id, pattern, concepts FROM learned_patterns",
|
|
812
|
+
lambda r: " ".join(filter(None, [r["pattern"], r["concepts"]])),
|
|
813
|
+
),
|
|
814
|
+
(
|
|
815
|
+
"outcome_history",
|
|
816
|
+
"SELECT id, narrative_key_insight, narrative_what_failed, narrative_what_worked FROM outcome_history",
|
|
817
|
+
lambda r: " ".join(
|
|
818
|
+
filter(
|
|
819
|
+
None,
|
|
820
|
+
[
|
|
821
|
+
r["narrative_key_insight"],
|
|
822
|
+
r["narrative_what_failed"],
|
|
823
|
+
r["narrative_what_worked"],
|
|
824
|
+
],
|
|
825
|
+
)
|
|
826
|
+
),
|
|
827
|
+
),
|
|
828
|
+
(
|
|
829
|
+
"document_chunks",
|
|
830
|
+
"SELECT id, heading_path, content FROM document_chunks",
|
|
831
|
+
lambda r: " ".join(filter(None, [r["heading_path"], r["content"]])),
|
|
832
|
+
),
|
|
833
|
+
(
|
|
834
|
+
"graph_nodes",
|
|
835
|
+
"SELECT id, label, signature, doc_blob FROM graph_nodes WHERE kind IN ("
|
|
836
|
+
+ ",".join(f"'{k}'" for k in GRAPH_EMBED_KINDS)
|
|
837
|
+
+ ")",
|
|
838
|
+
lambda r: " ".join(filter(None, [r["label"], r["signature"], r["doc_blob"]])),
|
|
839
|
+
),
|
|
840
|
+
]
|
|
841
|
+
|
|
842
|
+
name = active_model_name()
|
|
843
|
+
has_dim_col = _has_embedding_dim_column(conn)
|
|
844
|
+
|
|
845
|
+
for table, query, text_builder in handlers:
|
|
846
|
+
stats = {"processed": 0, "inserted": 0, "updated": 0, "unchanged": 0, "skipped": 0}
|
|
847
|
+
try:
|
|
848
|
+
cursor = conn.execute(query)
|
|
849
|
+
except sqlite3.OperationalError:
|
|
850
|
+
# Table doesn't exist yet (e.g. document_chunks before v5)
|
|
851
|
+
report[table] = stats
|
|
852
|
+
continue
|
|
853
|
+
# Stream source rows in batches; embed only changed/new rows in one
|
|
854
|
+
# embed_texts() call per batch instead of one model.encode per row.
|
|
855
|
+
# Peak memory = one batch, not the whole table. TASK-224.
|
|
856
|
+
while True:
|
|
857
|
+
src_rows = cursor.fetchmany(_REINDEX_BATCH)
|
|
858
|
+
if not src_rows:
|
|
859
|
+
break
|
|
860
|
+
pending: list[tuple[int, str, int | None]] = [] # (source_id, text_hash, existing_id)
|
|
861
|
+
texts: list[str] = []
|
|
862
|
+
for row in src_rows:
|
|
863
|
+
stats["processed"] += 1
|
|
864
|
+
text = text_builder(row)
|
|
865
|
+
if not text or not text.strip():
|
|
866
|
+
stats["skipped"] += 1
|
|
867
|
+
continue
|
|
868
|
+
text_hash = _compute_text_hash(text)
|
|
869
|
+
existing = conn.execute(
|
|
870
|
+
"SELECT id, text_hash, model_name FROM embeddings "
|
|
871
|
+
"WHERE source_table = ? AND source_id = ?",
|
|
872
|
+
(table, row["id"]),
|
|
873
|
+
).fetchone()
|
|
874
|
+
if existing and existing[1] == text_hash and existing[2] == name:
|
|
875
|
+
stats["unchanged"] += 1
|
|
876
|
+
continue
|
|
877
|
+
pending.append((row["id"], text_hash, existing[0] if existing else None))
|
|
878
|
+
texts.append(text)
|
|
879
|
+
if not texts:
|
|
880
|
+
continue
|
|
881
|
+
vectors = embed_texts(texts, model_name=name)
|
|
882
|
+
for (source_id, text_hash, existing_id), vector in zip(pending, vectors):
|
|
883
|
+
if vector is None:
|
|
884
|
+
stats["skipped"] += 1
|
|
885
|
+
continue
|
|
886
|
+
try:
|
|
887
|
+
status, _, _ = _persist_embedding(
|
|
888
|
+
conn, table, source_id, text_hash, vector, name, existing_id, has_dim_col
|
|
889
|
+
)
|
|
890
|
+
except sqlite3.OperationalError:
|
|
891
|
+
stats["skipped"] += 1
|
|
892
|
+
continue
|
|
893
|
+
stats[status] += 1
|
|
894
|
+
conn.commit()
|
|
895
|
+
report[table] = stats
|
|
896
|
+
|
|
897
|
+
return report
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
# ---------------------------------------------------------------------------
|
|
901
|
+
# CLI entry point — `python -m embeddings --reindex`
|
|
902
|
+
# ---------------------------------------------------------------------------
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def _main() -> None:
|
|
906
|
+
import argparse
|
|
907
|
+
import json
|
|
908
|
+
from pathlib import Path
|
|
909
|
+
|
|
910
|
+
parser = argparse.ArgumentParser(description="Embeddings CLI for coding-os RAG")
|
|
911
|
+
parser.add_argument(
|
|
912
|
+
"--reindex", action="store_true", help="Re-embed all rows in supported tables"
|
|
913
|
+
)
|
|
914
|
+
parser.add_argument(
|
|
915
|
+
"--db", type=str, default=None, help="Override DB path (defaults to COS_DB_PATH)"
|
|
916
|
+
)
|
|
917
|
+
args = parser.parse_args()
|
|
918
|
+
|
|
919
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
920
|
+
from database import init_db
|
|
921
|
+
|
|
922
|
+
if not is_available():
|
|
923
|
+
print(
|
|
924
|
+
"ERROR: sentence-transformers not installed. Run: uv sync --extra rag", file=sys.stderr
|
|
925
|
+
)
|
|
926
|
+
sys.exit(1)
|
|
927
|
+
|
|
928
|
+
conn = init_db(args.db)
|
|
929
|
+
if args.reindex:
|
|
930
|
+
result = reindex_all(conn)
|
|
931
|
+
print(json.dumps(result, indent=2))
|
|
932
|
+
else:
|
|
933
|
+
from database import get_db_stats
|
|
934
|
+
|
|
935
|
+
stats = get_db_stats(conn)
|
|
936
|
+
print(
|
|
937
|
+
json.dumps({"status": "ok", "embeddings_available": True, "db_stats": stats}, indent=2)
|
|
938
|
+
)
|
|
939
|
+
conn.close()
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
if __name__ == "__main__":
|
|
943
|
+
_main()
|