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,1053 @@
|
|
|
1
|
+
"""graph_os — SQLite fallback backend.
|
|
2
|
+
|
|
3
|
+
DEPENDS: sqlite3 stdlib; core/thinking_os/database.py for init_db when
|
|
4
|
+
path-based; core/graph_os/types.py for the value types.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import sqlite3
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
from collections.abc import Iterable, Sequence
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from ..types import EvidenceSignal, GraphEdge, GraphNode, normalize_kind
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("graph_os.backends.sqlite")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _import_db_module() -> Any:
|
|
25
|
+
"""Return the thinking_os database module.
|
|
26
|
+
|
|
27
|
+
Prefers the installed package path (`thinking_os.database`); falls back
|
|
28
|
+
to the bare ``database`` name for environments where
|
|
29
|
+
core/thinking_os/ is on sys.path directly (e.g. direct script
|
|
30
|
+
invocation without editable install).
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
from thinking_os import database as _db # type: ignore
|
|
34
|
+
|
|
35
|
+
return _db
|
|
36
|
+
except ImportError:
|
|
37
|
+
import database as _db # type: ignore
|
|
38
|
+
|
|
39
|
+
return _db
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SqliteBackend:
|
|
43
|
+
"""SQLite-backed graph store (thinking_os DB, migration v12).
|
|
44
|
+
|
|
45
|
+
DEPENDS: migration v12 (graph_nodes, graph_edges_v12,
|
|
46
|
+
graph_evidence_v12 tables + optional FTS5 virtual
|
|
47
|
+
table).
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
backend_id: str = "sqlite"
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
*,
|
|
55
|
+
conn: sqlite3.Connection | None = None,
|
|
56
|
+
db_path: str | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
self._owns_conn = False
|
|
59
|
+
# B1: serialise writes across threads so sqlite3.Connection (which
|
|
60
|
+
# is only cursor-safe, not write-safe under concurrent INSERT) does
|
|
61
|
+
# not raise ProgrammingError. Reads fall under the same lock to
|
|
62
|
+
# cover BEGIN/COMMIT boundaries in ``upsert_edge``.
|
|
63
|
+
self._write_lock = threading.RLock()
|
|
64
|
+
# P6: per-thread read connections. WAL allows multiple concurrent
|
|
65
|
+
# readers; a single shared sqlite3.Connection serialises them
|
|
66
|
+
# behind the GIL + connection mutex. Opening one connection per
|
|
67
|
+
# thread (lazy) unblocks true parallel reads in Hub UI + parallel
|
|
68
|
+
# MCP dispatch. Track all opened conns so close() can drain them.
|
|
69
|
+
self._db_path: str | None = None
|
|
70
|
+
self._read_conn_pool = threading.local()
|
|
71
|
+
self._all_read_conns: list[sqlite3.Connection] = []
|
|
72
|
+
self._read_conn_lock = threading.Lock()
|
|
73
|
+
if conn is not None:
|
|
74
|
+
self._conn = conn
|
|
75
|
+
else:
|
|
76
|
+
try:
|
|
77
|
+
from thinking_os.database import resolve_db_path # type: ignore
|
|
78
|
+
|
|
79
|
+
resolved = db_path or str(resolve_db_path())
|
|
80
|
+
except ImportError:
|
|
81
|
+
# Standalone — fall back to env var + repo-relative default.
|
|
82
|
+
resolved = db_path or os.environ.get("COS_DB_PATH", ".coding-os/coding-os.db")
|
|
83
|
+
Path(resolved).parent.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
db = _import_db_module()
|
|
85
|
+
# B1: own-connection path opens with check_same_thread=False so
|
|
86
|
+
# multiple MCP threads can share this backend instance. WAL +
|
|
87
|
+
# busy_timeout give us concurrent readers + a writer without
|
|
88
|
+
# SQLITE_BUSY spam.
|
|
89
|
+
self._db_path = resolved
|
|
90
|
+
self._conn = sqlite3.connect(resolved, timeout=10, check_same_thread=False)
|
|
91
|
+
self._conn.row_factory = sqlite3.Row
|
|
92
|
+
self._conn.execute("PRAGMA journal_mode = WAL")
|
|
93
|
+
self._conn.execute("PRAGMA synchronous = NORMAL")
|
|
94
|
+
self._conn.execute("PRAGMA foreign_keys = ON")
|
|
95
|
+
# 30s ceiling tolerates ten concurrent dispatcher workers
|
|
96
|
+
# reindexing different files at the same time without the
|
|
97
|
+
# busy_timeout firing.
|
|
98
|
+
self._conn.execute("PRAGMA busy_timeout = 30000")
|
|
99
|
+
# G16: apply the full pragma SSOT (cache_size, mmap_size,
|
|
100
|
+
# temp_store, wal_autocheckpoint) so standalone-conn bench
|
|
101
|
+
# harnesses (bench/scale_500k.py, bench/viewer_fps.py) +
|
|
102
|
+
# reindex_dispatch.py see the same p99 SLA the pooled path
|
|
103
|
+
# does. Fail-open if helper unavailable (older DB module).
|
|
104
|
+
_apply_pragmas = getattr(db, "_apply_pragmas", None)
|
|
105
|
+
if callable(_apply_pragmas):
|
|
106
|
+
try:
|
|
107
|
+
_apply_pragmas(self._conn)
|
|
108
|
+
except Exception as exc: # pragma: no cover — diagnostic
|
|
109
|
+
logger.debug("standalone PRAGMA application skipped: %s", exc)
|
|
110
|
+
db.run_migrations(self._conn)
|
|
111
|
+
self._owns_conn = True
|
|
112
|
+
self._conn.execute("PRAGMA foreign_keys = ON")
|
|
113
|
+
self._verify_schema()
|
|
114
|
+
|
|
115
|
+
def _verify_schema(self) -> None:
|
|
116
|
+
db = _import_db_module()
|
|
117
|
+
missing: list[str] = []
|
|
118
|
+
if not db.has_graph_nodes_table(self._conn):
|
|
119
|
+
missing.append("graph_nodes")
|
|
120
|
+
if not db.has_graph_edges_table(self._conn):
|
|
121
|
+
missing.append("graph_edges_v12")
|
|
122
|
+
if not db.has_graph_evidence_table(self._conn):
|
|
123
|
+
missing.append("graph_evidence_v12")
|
|
124
|
+
if missing:
|
|
125
|
+
raise RuntimeError(
|
|
126
|
+
"SqliteBackend: required tables missing "
|
|
127
|
+
f"({', '.join(missing)}). Run migrations (init_db) first."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# -- Lifecycle ---------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
def close(self) -> None:
|
|
133
|
+
if self._owns_conn:
|
|
134
|
+
with self._write_lock:
|
|
135
|
+
try:
|
|
136
|
+
self._conn.close()
|
|
137
|
+
except sqlite3.Error as exc:
|
|
138
|
+
logger.debug("sqlite close suppressed: %s", exc)
|
|
139
|
+
# P6: drain per-thread read connections too.
|
|
140
|
+
with self._read_conn_lock:
|
|
141
|
+
for conn in self._all_read_conns:
|
|
142
|
+
try:
|
|
143
|
+
conn.close()
|
|
144
|
+
except sqlite3.Error as exc:
|
|
145
|
+
logger.debug("sqlite read-conn close suppressed: %s", exc)
|
|
146
|
+
self._all_read_conns.clear()
|
|
147
|
+
|
|
148
|
+
def _get_read_conn(self) -> sqlite3.Connection:
|
|
149
|
+
"""P6: return this thread's read-only sqlite3.Connection.
|
|
150
|
+
|
|
151
|
+
Lazy-open per thread under WAL so concurrent get_node /
|
|
152
|
+
count_edges / list_edges calls don't serialise behind the
|
|
153
|
+
primary connection's GIL+mutex. Falls back to the shared
|
|
154
|
+
connection when ``_db_path`` is unset (caller-provided conn
|
|
155
|
+
via constructor) — tests + caller-pooled scenarios keep
|
|
156
|
+
working unchanged.
|
|
157
|
+
"""
|
|
158
|
+
if self._db_path is None:
|
|
159
|
+
return self._conn
|
|
160
|
+
conn = getattr(self._read_conn_pool, "conn", None)
|
|
161
|
+
if conn is not None:
|
|
162
|
+
return conn
|
|
163
|
+
conn = sqlite3.connect(self._db_path, timeout=10, check_same_thread=False)
|
|
164
|
+
conn.row_factory = sqlite3.Row
|
|
165
|
+
# Read connections need WAL so they see committed writes
|
|
166
|
+
# immediately. Foreign keys + query_only enforce read-only
|
|
167
|
+
# semantics (defence-in-depth). Cache stays modest (~2MB
|
|
168
|
+
# default) so 16 threads don't multiply the 64MB primary cache.
|
|
169
|
+
try:
|
|
170
|
+
conn.execute("PRAGMA journal_mode = WAL")
|
|
171
|
+
conn.execute("PRAGMA query_only = ON")
|
|
172
|
+
conn.execute("PRAGMA foreign_keys = ON")
|
|
173
|
+
conn.execute("PRAGMA busy_timeout = 30000")
|
|
174
|
+
except sqlite3.Error as exc:
|
|
175
|
+
logger.debug("read-conn pragma skipped: %s", exc)
|
|
176
|
+
self._read_conn_pool.conn = conn
|
|
177
|
+
with self._read_conn_lock:
|
|
178
|
+
self._all_read_conns.append(conn)
|
|
179
|
+
return conn
|
|
180
|
+
|
|
181
|
+
# -- Write path --------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
def upsert_node(self, node: GraphNode) -> int:
|
|
184
|
+
"""Insert a node or update it in place; return the primary key.
|
|
185
|
+
|
|
186
|
+
DEPENDS: migration v12 schema.
|
|
187
|
+
"""
|
|
188
|
+
now = int(time.time())
|
|
189
|
+
metadata_json = json.dumps(dict(node.metadata), sort_keys=True)
|
|
190
|
+
# Canonicalise kind at the storage boundary (S3 NodeKind). Falls
|
|
191
|
+
# back to the raw string if normalize_kind doesn't recognise the
|
|
192
|
+
# form so a stray label can't kill an entire reindex run.
|
|
193
|
+
try:
|
|
194
|
+
kind_value = normalize_kind(node.kind).value
|
|
195
|
+
except ValueError:
|
|
196
|
+
kind_value = node.kind
|
|
197
|
+
with self._write_lock:
|
|
198
|
+
row = self._conn.execute(
|
|
199
|
+
"SELECT id, doc_blob, signature, metadata_json, file_path, lang, "
|
|
200
|
+
"content_hash, start_line, end_line FROM graph_nodes WHERE uid = ?",
|
|
201
|
+
(node.uid,),
|
|
202
|
+
).fetchone()
|
|
203
|
+
if row is None:
|
|
204
|
+
cursor = self._conn.execute(
|
|
205
|
+
"""
|
|
206
|
+
INSERT INTO graph_nodes
|
|
207
|
+
(kind, label, uid, file_path, start_line, end_line,
|
|
208
|
+
signature, lang, doc_blob, ast_hash, content_hash,
|
|
209
|
+
metadata_json, created_at, updated_at)
|
|
210
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
211
|
+
""",
|
|
212
|
+
(
|
|
213
|
+
kind_value,
|
|
214
|
+
node.label,
|
|
215
|
+
node.uid,
|
|
216
|
+
node.file_path,
|
|
217
|
+
node.start_line,
|
|
218
|
+
node.end_line,
|
|
219
|
+
node.signature,
|
|
220
|
+
node.lang,
|
|
221
|
+
node.doc_blob,
|
|
222
|
+
node.ast_hash,
|
|
223
|
+
node.content_hash,
|
|
224
|
+
metadata_json,
|
|
225
|
+
now,
|
|
226
|
+
now,
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
self._conn.commit()
|
|
230
|
+
return int(cursor.lastrowid)
|
|
231
|
+
|
|
232
|
+
node_id = int(row[0])
|
|
233
|
+
existing_doc_blob = row[1]
|
|
234
|
+
existing_signature = row[2]
|
|
235
|
+
existing_meta_json = row[3]
|
|
236
|
+
existing_file_path = row[4]
|
|
237
|
+
existing_lang = row[5]
|
|
238
|
+
existing_content_hash = row[6]
|
|
239
|
+
existing_start_line = row[7]
|
|
240
|
+
existing_end_line = row[8]
|
|
241
|
+
incoming_is_stub = bool(node.metadata.get("stub")) if node.metadata else False
|
|
242
|
+
existing_meta = json.loads(existing_meta_json or "{}")
|
|
243
|
+
existing_is_stub = bool(existing_meta.get("stub"))
|
|
244
|
+
|
|
245
|
+
doc_blob_to_write = node.doc_blob
|
|
246
|
+
sig_to_write = node.signature
|
|
247
|
+
meta_to_write = metadata_json
|
|
248
|
+
if incoming_is_stub and not existing_is_stub:
|
|
249
|
+
doc_blob_to_write = existing_doc_blob
|
|
250
|
+
sig_to_write = existing_signature
|
|
251
|
+
meta_to_write = existing_meta_json or metadata_json
|
|
252
|
+
else:
|
|
253
|
+
if existing_doc_blob and not node.doc_blob:
|
|
254
|
+
doc_blob_to_write = existing_doc_blob
|
|
255
|
+
if existing_signature and not node.signature:
|
|
256
|
+
sig_to_write = existing_signature
|
|
257
|
+
|
|
258
|
+
# Preserve existing non-null path/lang/hash; a path-less stub upsert must not clobber them.
|
|
259
|
+
file_path_to_write = (
|
|
260
|
+
node.file_path if node.file_path is not None else existing_file_path
|
|
261
|
+
)
|
|
262
|
+
lang_to_write = node.lang if node.lang is not None else existing_lang
|
|
263
|
+
content_hash_to_write = (
|
|
264
|
+
node.content_hash if node.content_hash is not None else existing_content_hash
|
|
265
|
+
)
|
|
266
|
+
start_line_to_write = (
|
|
267
|
+
node.start_line if node.start_line is not None else existing_start_line
|
|
268
|
+
)
|
|
269
|
+
end_line_to_write = node.end_line if node.end_line is not None else existing_end_line
|
|
270
|
+
|
|
271
|
+
self._conn.execute(
|
|
272
|
+
"""
|
|
273
|
+
UPDATE graph_nodes SET
|
|
274
|
+
kind=?, label=?, file_path=?, start_line=?, end_line=?,
|
|
275
|
+
signature=?, lang=?, doc_blob=?, ast_hash=?, content_hash=?,
|
|
276
|
+
metadata_json=?, updated_at=?
|
|
277
|
+
WHERE id=?
|
|
278
|
+
""",
|
|
279
|
+
(
|
|
280
|
+
kind_value,
|
|
281
|
+
node.label,
|
|
282
|
+
file_path_to_write,
|
|
283
|
+
start_line_to_write,
|
|
284
|
+
end_line_to_write,
|
|
285
|
+
sig_to_write,
|
|
286
|
+
lang_to_write,
|
|
287
|
+
doc_blob_to_write,
|
|
288
|
+
node.ast_hash,
|
|
289
|
+
content_hash_to_write,
|
|
290
|
+
meta_to_write,
|
|
291
|
+
now,
|
|
292
|
+
node_id,
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
self._conn.commit()
|
|
296
|
+
return node_id
|
|
297
|
+
|
|
298
|
+
def upsert_edge(self, edge: GraphEdge) -> int:
|
|
299
|
+
"""Insert or update an edge; replace evidence atomically.
|
|
300
|
+
|
|
301
|
+
DEPENDS: upsert_node must have been called for both endpoints.
|
|
302
|
+
|
|
303
|
+
F12 / Audit #3: extractors occasionally emit self-loops
|
|
304
|
+
(source_uid == target_uid) when an AST visitor mis-resolves
|
|
305
|
+
recursion or nested attribute access. They poison call-graph
|
|
306
|
+
analytics. Drop them at the backend write boundary so a single
|
|
307
|
+
fix covers every extractor.
|
|
308
|
+
"""
|
|
309
|
+
if edge.source_uid == edge.target_uid:
|
|
310
|
+
logger.debug(
|
|
311
|
+
"self-loop dropped at upsert_edge: uid=%s type=%s", edge.source_uid, edge.edge_type
|
|
312
|
+
)
|
|
313
|
+
return -1
|
|
314
|
+
now = int(time.time())
|
|
315
|
+
with self._write_lock:
|
|
316
|
+
source_id = self._node_id_for_uid(edge.source_uid)
|
|
317
|
+
target_id = self._node_id_for_uid(edge.target_uid)
|
|
318
|
+
cursor = self._conn.cursor()
|
|
319
|
+
try:
|
|
320
|
+
cursor.execute("BEGIN")
|
|
321
|
+
if edge.edge_type == "contains":
|
|
322
|
+
# Structural folder-spine edge — every extractor that
|
|
323
|
+
# touches a file re-emits the folder→file spine. Dedup on
|
|
324
|
+
# (source,target,type) IGNORING extractor; otherwise a file
|
|
325
|
+
# processed by N extractors yields N identical rows that
|
|
326
|
+
# inflate degree centrality (COUNT(e.id), not DISTINCT). W6.10.
|
|
327
|
+
row = cursor.execute(
|
|
328
|
+
"""
|
|
329
|
+
SELECT id FROM graph_edges_v12
|
|
330
|
+
WHERE source_id=? AND target_id=? AND edge_type=?
|
|
331
|
+
""",
|
|
332
|
+
(source_id, target_id, edge.edge_type),
|
|
333
|
+
).fetchone()
|
|
334
|
+
else:
|
|
335
|
+
row = cursor.execute(
|
|
336
|
+
"""
|
|
337
|
+
SELECT id FROM graph_edges_v12
|
|
338
|
+
WHERE source_id=? AND target_id=? AND edge_type=? AND extractor=?
|
|
339
|
+
""",
|
|
340
|
+
(source_id, target_id, edge.edge_type, edge.extractor),
|
|
341
|
+
).fetchone()
|
|
342
|
+
if row is None:
|
|
343
|
+
cursor.execute(
|
|
344
|
+
"""
|
|
345
|
+
INSERT INTO graph_edges_v12
|
|
346
|
+
(source_id, target_id, edge_type, confidence,
|
|
347
|
+
extractor, source_span, created_at, updated_at)
|
|
348
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
349
|
+
""",
|
|
350
|
+
(
|
|
351
|
+
source_id,
|
|
352
|
+
target_id,
|
|
353
|
+
edge.edge_type,
|
|
354
|
+
float(edge.confidence),
|
|
355
|
+
edge.extractor,
|
|
356
|
+
edge.source_span,
|
|
357
|
+
now,
|
|
358
|
+
now,
|
|
359
|
+
),
|
|
360
|
+
)
|
|
361
|
+
edge_id = int(cursor.lastrowid)
|
|
362
|
+
else:
|
|
363
|
+
edge_id = int(row[0])
|
|
364
|
+
cursor.execute(
|
|
365
|
+
"""
|
|
366
|
+
UPDATE graph_edges_v12 SET
|
|
367
|
+
confidence=?, source_span=?, updated_at=?
|
|
368
|
+
WHERE id=?
|
|
369
|
+
""",
|
|
370
|
+
(
|
|
371
|
+
float(edge.confidence),
|
|
372
|
+
edge.source_span,
|
|
373
|
+
now,
|
|
374
|
+
edge_id,
|
|
375
|
+
),
|
|
376
|
+
)
|
|
377
|
+
cursor.execute("DELETE FROM graph_evidence_v12 WHERE edge_id=?", (edge_id,))
|
|
378
|
+
|
|
379
|
+
for signal in edge.evidence:
|
|
380
|
+
cursor.execute(
|
|
381
|
+
"""
|
|
382
|
+
INSERT INTO graph_evidence_v12
|
|
383
|
+
(edge_id, signal_name, weight, note, created_at)
|
|
384
|
+
VALUES (?, ?, ?, ?, ?)
|
|
385
|
+
""",
|
|
386
|
+
(
|
|
387
|
+
edge_id,
|
|
388
|
+
signal.signal_name,
|
|
389
|
+
float(signal.weight),
|
|
390
|
+
signal.note,
|
|
391
|
+
now,
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
self._conn.commit()
|
|
395
|
+
return edge_id
|
|
396
|
+
except Exception:
|
|
397
|
+
self._conn.rollback()
|
|
398
|
+
raise
|
|
399
|
+
|
|
400
|
+
def bulk_upsert(
|
|
401
|
+
self, nodes: Iterable[GraphNode], edges: Iterable[GraphEdge]
|
|
402
|
+
) -> tuple[int, int]:
|
|
403
|
+
"""Insert many nodes then many edges; return counts written.
|
|
404
|
+
|
|
405
|
+
Two-pass so edges never reference an unknown uid.
|
|
406
|
+
"""
|
|
407
|
+
node_count = 0
|
|
408
|
+
for node in nodes:
|
|
409
|
+
self.upsert_node(node)
|
|
410
|
+
node_count += 1
|
|
411
|
+
edge_count = 0
|
|
412
|
+
for edge in edges:
|
|
413
|
+
if self.upsert_edge(edge) >= 0:
|
|
414
|
+
edge_count += 1
|
|
415
|
+
return node_count, edge_count
|
|
416
|
+
|
|
417
|
+
def delete_node(self, uid: str) -> bool:
|
|
418
|
+
"""Remove a node; FK CASCADE removes edges + evidence."""
|
|
419
|
+
with self._write_lock:
|
|
420
|
+
cursor = self._conn.execute("DELETE FROM graph_nodes WHERE uid=?", (uid,))
|
|
421
|
+
self._conn.commit()
|
|
422
|
+
return cursor.rowcount > 0
|
|
423
|
+
|
|
424
|
+
def delete_nodes_for_file(
|
|
425
|
+
self, file_path: str, *, extractors: Sequence[str] | None = None
|
|
426
|
+
) -> int:
|
|
427
|
+
"""Prune nodes belonging to a single source file before reindex.
|
|
428
|
+
|
|
429
|
+
HARD delete by design — the graph mirrors HEAD-of-tree, so a symbol
|
|
430
|
+
that left the file is gone, not historical (git is the record). This
|
|
431
|
+
runs on every per-file reindex (prune-before-reindex), so the graph
|
|
432
|
+
keeps no deletion ledger of its own — that would be pure churn.
|
|
433
|
+
Node prune here is routine and logged at debug. See graph-os-authoring.
|
|
434
|
+
"""
|
|
435
|
+
with self._write_lock:
|
|
436
|
+
if not extractors:
|
|
437
|
+
cursor = self._conn.execute(
|
|
438
|
+
"DELETE FROM graph_nodes WHERE file_path=?", (file_path,)
|
|
439
|
+
)
|
|
440
|
+
else:
|
|
441
|
+
placeholders = " OR ".join(["metadata_json LIKE ?"] * len(extractors))
|
|
442
|
+
params: list[Any] = [file_path]
|
|
443
|
+
for ex in extractors:
|
|
444
|
+
params.append(f'%"extractor": "{ex}"%')
|
|
445
|
+
cursor = self._conn.execute(
|
|
446
|
+
f"DELETE FROM graph_nodes WHERE file_path=? AND ({placeholders})",
|
|
447
|
+
params,
|
|
448
|
+
)
|
|
449
|
+
self._conn.commit()
|
|
450
|
+
deleted = int(cursor.rowcount or 0)
|
|
451
|
+
if deleted:
|
|
452
|
+
logger.debug("hard-deleted %d node(s) for %s", deleted, file_path)
|
|
453
|
+
return deleted
|
|
454
|
+
|
|
455
|
+
def link_external_stubs(self, *, file_path: str | None = None) -> int:
|
|
456
|
+
with self._write_lock:
|
|
457
|
+
if file_path:
|
|
458
|
+
stub_rows = self._conn.execute(
|
|
459
|
+
"""
|
|
460
|
+
SELECT DISTINCT stub.id, stub.uid
|
|
461
|
+
FROM graph_edges_v12 e
|
|
462
|
+
JOIN graph_nodes stub ON stub.id = e.target_id
|
|
463
|
+
JOIN graph_nodes src ON src.id = e.source_id
|
|
464
|
+
WHERE src.file_path = ?
|
|
465
|
+
AND stub.uid LIKE 'code:external:%'
|
|
466
|
+
AND stub.uid NOT LIKE 'code:external:unresolved:%'
|
|
467
|
+
""",
|
|
468
|
+
(file_path,),
|
|
469
|
+
).fetchall()
|
|
470
|
+
else:
|
|
471
|
+
stub_rows = self._conn.execute(
|
|
472
|
+
"""
|
|
473
|
+
SELECT id, uid FROM graph_nodes
|
|
474
|
+
WHERE uid LIKE 'code:external:%'
|
|
475
|
+
AND uid NOT LIKE 'code:external:unresolved:%'
|
|
476
|
+
"""
|
|
477
|
+
).fetchall()
|
|
478
|
+
|
|
479
|
+
stubs_by_label: dict[str, list[tuple[int, str, str]]] = {}
|
|
480
|
+
for stub_id, stub_uid in stub_rows:
|
|
481
|
+
rest = stub_uid[len("code:external:") :]
|
|
482
|
+
module, _, name = rest.rpartition(":")
|
|
483
|
+
if not module or not name:
|
|
484
|
+
continue
|
|
485
|
+
stubs_by_label.setdefault(name, []).append((int(stub_id), module, stub_uid))
|
|
486
|
+
|
|
487
|
+
if not stubs_by_label:
|
|
488
|
+
return 0
|
|
489
|
+
|
|
490
|
+
labels = list(stubs_by_label.keys())
|
|
491
|
+
placeholders = ",".join(["?"] * len(labels))
|
|
492
|
+
real_rows = self._conn.execute(
|
|
493
|
+
f"""
|
|
494
|
+
SELECT id, label, file_path, kind FROM graph_nodes
|
|
495
|
+
WHERE kind IN ('function','method','class','variable','interface')
|
|
496
|
+
AND label IN ({placeholders})
|
|
497
|
+
AND file_path IS NOT NULL
|
|
498
|
+
""",
|
|
499
|
+
tuple(labels),
|
|
500
|
+
).fetchall()
|
|
501
|
+
real_by_label: dict[str, list[tuple[int, str, str]]] = {}
|
|
502
|
+
for real_id, real_label, real_file, real_kind in real_rows:
|
|
503
|
+
real_by_label.setdefault(real_label, []).append(
|
|
504
|
+
(int(real_id), real_file, str(real_kind))
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
rewrites = 0
|
|
508
|
+
for label, candidate_stubs in stubs_by_label.items():
|
|
509
|
+
real_candidates = real_by_label.get(label, [])
|
|
510
|
+
if not real_candidates:
|
|
511
|
+
continue
|
|
512
|
+
for stub_id, module, _stub_uid in candidate_stubs:
|
|
513
|
+
module_suffix = module.replace(".", "/")
|
|
514
|
+
# collect ALL real files whose path matches the
|
|
515
|
+
# stub's module, then resolve ONLY when exactly one does.
|
|
516
|
+
# First-match-break used to pick an arbitrary candidate
|
|
517
|
+
# when several `label`s exist in different modules (e.g. 3
|
|
518
|
+
# `fail` functions) — a false-edge risk amplified once the
|
|
519
|
+
# global cross-file pass runs. Ambiguous (>1) ⇒ skip, never
|
|
520
|
+
# guess.
|
|
521
|
+
matches = [
|
|
522
|
+
(real_id, real_kind)
|
|
523
|
+
for real_id, real_file, real_kind in real_candidates
|
|
524
|
+
if (
|
|
525
|
+
real_file == f"{module_suffix}.py"
|
|
526
|
+
or real_file.endswith(f"/{module_suffix}.py")
|
|
527
|
+
or real_file == f"{module_suffix}/__init__.py"
|
|
528
|
+
or real_file.endswith(f"/{module_suffix}/__init__.py")
|
|
529
|
+
)
|
|
530
|
+
]
|
|
531
|
+
if len(matches) != 1:
|
|
532
|
+
continue
|
|
533
|
+
matched_real_id, matched_real_kind = matches[0]
|
|
534
|
+
# N2: when stub resolves to a real CLASS node, promote
|
|
535
|
+
# any inbound `calls` edges (constructor-shaped) to
|
|
536
|
+
# `constructs`. The original extract-time gate
|
|
537
|
+
# (is_constructor_like + target.startswith('code:class:'))
|
|
538
|
+
# missed these because the stub uid was `code:external:*`
|
|
539
|
+
# at the time edges were emitted.
|
|
540
|
+
# rewrite stub→real edges with OR IGNORE. A bare
|
|
541
|
+
# UPDATE aborts the WHOLE linker pass with an IntegrityError
|
|
542
|
+
# when the rewrite would duplicate an existing edge — e.g. a
|
|
543
|
+
# caller reaches the same real symbol via two module
|
|
544
|
+
# spellings (`tools._shared:fail` + `pkg.tools._shared:fail`)
|
|
545
|
+
# so the second rewrite collides on UNIQUE(source,target,
|
|
546
|
+
# edge_type,extractor). OR IGNORE skips the (duplicate)
|
|
547
|
+
# colliding rows instead of aborting; the real edge from the
|
|
548
|
+
# first rewrite already exists, so the un-rewritten leftover
|
|
549
|
+
# row is a redundant duplicate pointing at the stub. We do
|
|
550
|
+
# NOT delete it here (a blanket DELETE on target_id risked
|
|
551
|
+
# removing rows OR IGNORE skipped for non-duplicate reasons);
|
|
552
|
+
# the stub simply retains it and surfaces as an info-level
|
|
553
|
+
# `orphaned_external_unresolved` in doctor. Net: every
|
|
554
|
+
# distinct caller reaches the real node and the pass never
|
|
555
|
+
# aborts mid-loop.
|
|
556
|
+
if matched_real_kind == "class":
|
|
557
|
+
self._conn.execute(
|
|
558
|
+
"UPDATE OR IGNORE graph_edges_v12 SET target_id = ?, "
|
|
559
|
+
"edge_type = CASE WHEN edge_type='calls' "
|
|
560
|
+
"THEN 'constructs' ELSE edge_type END "
|
|
561
|
+
"WHERE target_id = ?",
|
|
562
|
+
(matched_real_id, stub_id),
|
|
563
|
+
)
|
|
564
|
+
else:
|
|
565
|
+
self._conn.execute(
|
|
566
|
+
"UPDATE OR IGNORE graph_edges_v12 "
|
|
567
|
+
"SET target_id = ? WHERE target_id = ?",
|
|
568
|
+
(matched_real_id, stub_id),
|
|
569
|
+
)
|
|
570
|
+
rewrites += 1
|
|
571
|
+
self._conn.commit()
|
|
572
|
+
return rewrites
|
|
573
|
+
|
|
574
|
+
def edges_among(
|
|
575
|
+
self,
|
|
576
|
+
uids: Sequence[str],
|
|
577
|
+
*,
|
|
578
|
+
edge_types: Sequence[str] | None = None,
|
|
579
|
+
exclude_edge_types: Sequence[str] = ("contains",),
|
|
580
|
+
limit: int = 100_000,
|
|
581
|
+
) -> list[GraphEdge]:
|
|
582
|
+
"""Edges whose BOTH endpoints are inside ``uids`` — the semantic
|
|
583
|
+
overlay of a subtree-scoped export (TASK-406). Chunked source-side
|
|
584
|
+
IN query + in-memory target filter."""
|
|
585
|
+
if not uids:
|
|
586
|
+
return []
|
|
587
|
+
chunk = 500
|
|
588
|
+
id_to_uid: dict[int, str] = {}
|
|
589
|
+
uid_list = list(dict.fromkeys(uids))
|
|
590
|
+
for start in range(0, len(uid_list), chunk):
|
|
591
|
+
batch = uid_list[start : start + chunk]
|
|
592
|
+
for row_id, row_uid in self._conn.execute(
|
|
593
|
+
f"SELECT id, uid FROM graph_nodes WHERE uid IN ({','.join('?' * len(batch))})",
|
|
594
|
+
batch,
|
|
595
|
+
).fetchall():
|
|
596
|
+
id_to_uid[int(row_id)] = str(row_uid)
|
|
597
|
+
member_ids = list(id_to_uid)
|
|
598
|
+
member_set = set(member_ids)
|
|
599
|
+
excluded_types = set(exclude_edge_types or ())
|
|
600
|
+
wanted_types = set(edge_types) if edge_types else None
|
|
601
|
+
edges: list[GraphEdge] = []
|
|
602
|
+
for start in range(0, len(member_ids), chunk):
|
|
603
|
+
batch = member_ids[start : start + chunk]
|
|
604
|
+
rows = self._conn.execute(
|
|
605
|
+
"SELECT source_id, target_id, edge_type, extractor, confidence "
|
|
606
|
+
f"FROM graph_edges_v12 WHERE source_id IN ({','.join('?' * len(batch))})",
|
|
607
|
+
batch,
|
|
608
|
+
).fetchall()
|
|
609
|
+
for src, tgt, edge_type, extractor, confidence in rows:
|
|
610
|
+
if int(tgt) not in member_set:
|
|
611
|
+
continue
|
|
612
|
+
if edge_type in excluded_types:
|
|
613
|
+
continue
|
|
614
|
+
if wanted_types is not None and edge_type not in wanted_types:
|
|
615
|
+
continue
|
|
616
|
+
edges.append(
|
|
617
|
+
GraphEdge(
|
|
618
|
+
source_uid=id_to_uid[int(src)],
|
|
619
|
+
target_uid=id_to_uid[int(tgt)],
|
|
620
|
+
edge_type=str(edge_type),
|
|
621
|
+
extractor=str(extractor or ""),
|
|
622
|
+
confidence=float(confidence or 0.0),
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
if len(edges) >= limit:
|
|
626
|
+
return edges
|
|
627
|
+
return edges
|
|
628
|
+
|
|
629
|
+
def contains_ancestors_bulk(
|
|
630
|
+
self, uids: Sequence[str]
|
|
631
|
+
) -> tuple[list[GraphNode], list[GraphEdge]]:
|
|
632
|
+
"""Ancestor closure over ``contains`` for a uid SET — one query per
|
|
633
|
+
tree level instead of one walk per node (TASK-403: the per-node
|
|
634
|
+
loop cost ~12 s on a 30k-uid spine export).
|
|
635
|
+
"""
|
|
636
|
+
if not uids:
|
|
637
|
+
return [], []
|
|
638
|
+
chunk = 500
|
|
639
|
+
id_to_uid: dict[int, str] = {}
|
|
640
|
+
|
|
641
|
+
def _map_ids(ids: Iterable[int]) -> None:
|
|
642
|
+
pending = [i for i in ids if i not in id_to_uid]
|
|
643
|
+
for start in range(0, len(pending), chunk):
|
|
644
|
+
batch = pending[start : start + chunk]
|
|
645
|
+
for row_id, row_uid in self._conn.execute(
|
|
646
|
+
f"SELECT id, uid FROM graph_nodes WHERE id IN ({','.join('?' * len(batch))})",
|
|
647
|
+
batch,
|
|
648
|
+
).fetchall():
|
|
649
|
+
id_to_uid[int(row_id)] = str(row_uid)
|
|
650
|
+
|
|
651
|
+
frontier: set[int] = set()
|
|
652
|
+
uid_list = list(dict.fromkeys(uids))
|
|
653
|
+
for start in range(0, len(uid_list), chunk):
|
|
654
|
+
batch = uid_list[start : start + chunk]
|
|
655
|
+
for row_id, row_uid in self._conn.execute(
|
|
656
|
+
f"SELECT id, uid FROM graph_nodes WHERE uid IN ({','.join('?' * len(batch))})",
|
|
657
|
+
batch,
|
|
658
|
+
).fetchall():
|
|
659
|
+
id_to_uid[int(row_id)] = str(row_uid)
|
|
660
|
+
frontier.add(int(row_id))
|
|
661
|
+
|
|
662
|
+
seen: set[int] = set(frontier)
|
|
663
|
+
new_ids: set[int] = set()
|
|
664
|
+
edge_rows: list[tuple[int, int, float]] = []
|
|
665
|
+
for _ in range(32): # spine depth ceiling — real trees are ≤ ~12
|
|
666
|
+
if not frontier:
|
|
667
|
+
break
|
|
668
|
+
level_parents: set[int] = set()
|
|
669
|
+
frontier_list = list(frontier)
|
|
670
|
+
for start in range(0, len(frontier_list), chunk):
|
|
671
|
+
batch = frontier_list[start : start + chunk]
|
|
672
|
+
for src, tgt, conf in self._conn.execute(
|
|
673
|
+
"SELECT source_id, target_id, confidence FROM graph_edges_v12 "
|
|
674
|
+
f"WHERE edge_type='contains' AND target_id IN ({','.join('?' * len(batch))})",
|
|
675
|
+
batch,
|
|
676
|
+
).fetchall():
|
|
677
|
+
edge_rows.append((int(src), int(tgt), float(conf or 1.0)))
|
|
678
|
+
if int(src) not in seen:
|
|
679
|
+
level_parents.add(int(src))
|
|
680
|
+
seen |= level_parents
|
|
681
|
+
new_ids |= level_parents
|
|
682
|
+
frontier = level_parents
|
|
683
|
+
|
|
684
|
+
ancestor_nodes: list[GraphNode] = []
|
|
685
|
+
if new_ids:
|
|
686
|
+
_map_ids(new_ids)
|
|
687
|
+
hydrated = self.get_nodes_bulk([id_to_uid[i] for i in new_ids if i in id_to_uid])
|
|
688
|
+
ancestor_nodes = list(hydrated.values())
|
|
689
|
+
spine_edges = [
|
|
690
|
+
GraphEdge(
|
|
691
|
+
source_uid=id_to_uid[src],
|
|
692
|
+
target_uid=id_to_uid[tgt],
|
|
693
|
+
edge_type="contains",
|
|
694
|
+
extractor="spine_closure",
|
|
695
|
+
confidence=conf,
|
|
696
|
+
)
|
|
697
|
+
for src, tgt, conf in edge_rows
|
|
698
|
+
if src in id_to_uid and tgt in id_to_uid
|
|
699
|
+
]
|
|
700
|
+
return ancestor_nodes, spine_edges
|
|
701
|
+
|
|
702
|
+
def link_import_bindings(self, *, file_path: str | None = None) -> int:
|
|
703
|
+
"""Bind ``import_`` nodes to the symbol they import (TASK-402).
|
|
704
|
+
|
|
705
|
+
code_python emits one ``code:import:<file>::<name>`` node per
|
|
706
|
+
imported name (metadata carries ``imported`` + ``source_module``)
|
|
707
|
+
but no edge to the symbol itself, so every ``from M import name``
|
|
708
|
+
caller was invisible to references/impact (init_db probe: 16 of
|
|
709
|
+
~106 caller files reachable). Same exactly-one resolution contract
|
|
710
|
+
as link_external_stubs: ambiguous or unresolved → skip, never guess.
|
|
711
|
+
"""
|
|
712
|
+
with self._write_lock:
|
|
713
|
+
scope = " AND file_path = ?" if file_path else ""
|
|
714
|
+
params: tuple[Any, ...] = (file_path,) if file_path else ()
|
|
715
|
+
rows = self._conn.execute(
|
|
716
|
+
f"SELECT id, metadata_json FROM graph_nodes WHERE kind = 'import_'{scope}",
|
|
717
|
+
params,
|
|
718
|
+
).fetchall()
|
|
719
|
+
wanted: dict[str, list[tuple[int, str]]] = {}
|
|
720
|
+
for node_id, metadata_json in rows:
|
|
721
|
+
try:
|
|
722
|
+
metadata = json.loads(metadata_json or "{}")
|
|
723
|
+
except ValueError:
|
|
724
|
+
continue
|
|
725
|
+
name = metadata.get("imported")
|
|
726
|
+
module = metadata.get("source_module")
|
|
727
|
+
if not name or not module or metadata.get("wildcard"):
|
|
728
|
+
continue
|
|
729
|
+
wanted.setdefault(str(name), []).append((int(node_id), str(module)))
|
|
730
|
+
if not wanted:
|
|
731
|
+
return 0
|
|
732
|
+
placeholders = ",".join("?" * len(wanted))
|
|
733
|
+
real_rows = self._conn.execute(
|
|
734
|
+
f"""
|
|
735
|
+
SELECT id, label, file_path FROM graph_nodes
|
|
736
|
+
WHERE kind IN ('function','class','variable','interface')
|
|
737
|
+
AND label IN ({placeholders})
|
|
738
|
+
AND file_path IS NOT NULL
|
|
739
|
+
""",
|
|
740
|
+
tuple(wanted),
|
|
741
|
+
).fetchall()
|
|
742
|
+
real_by_label: dict[str, list[tuple[int, str]]] = {}
|
|
743
|
+
for real_id, real_label, real_file in real_rows:
|
|
744
|
+
real_by_label.setdefault(str(real_label), []).append((int(real_id), real_file))
|
|
745
|
+
now = int(time.time())
|
|
746
|
+
linked = 0
|
|
747
|
+
for name, importers in wanted.items():
|
|
748
|
+
candidates = real_by_label.get(name, [])
|
|
749
|
+
if not candidates:
|
|
750
|
+
continue
|
|
751
|
+
for import_id, module in importers:
|
|
752
|
+
module_suffix = module.replace(".", "/")
|
|
753
|
+
matches = {
|
|
754
|
+
real_id
|
|
755
|
+
for real_id, real_file in candidates
|
|
756
|
+
if (
|
|
757
|
+
real_file == f"{module_suffix}.py"
|
|
758
|
+
or real_file.endswith(f"/{module_suffix}.py")
|
|
759
|
+
or real_file == f"{module_suffix}/__init__.py"
|
|
760
|
+
or real_file.endswith(f"/{module_suffix}/__init__.py")
|
|
761
|
+
)
|
|
762
|
+
}
|
|
763
|
+
if len(matches) != 1:
|
|
764
|
+
continue
|
|
765
|
+
cursor = self._conn.execute(
|
|
766
|
+
"""
|
|
767
|
+
INSERT OR IGNORE INTO graph_edges_v12
|
|
768
|
+
(source_id, target_id, edge_type, confidence,
|
|
769
|
+
extractor, source_span, created_at, updated_at)
|
|
770
|
+
VALUES (?, ?, 'imports', 0.85, 'import_linker@v1', NULL, ?, ?)
|
|
771
|
+
""",
|
|
772
|
+
(import_id, next(iter(matches)), now, now),
|
|
773
|
+
)
|
|
774
|
+
linked += int(cursor.rowcount or 0)
|
|
775
|
+
self._conn.commit()
|
|
776
|
+
return linked
|
|
777
|
+
|
|
778
|
+
def link_php_handlers(self) -> int:
|
|
779
|
+
"""Resolve Laravel controller-handler stubs to real method nodes.
|
|
780
|
+
|
|
781
|
+
Contracts emits a route→`code:external:phproute:Ctrl.method` stub
|
|
782
|
+
because the controller lives in another file. After the global walk
|
|
783
|
+
every method node exists, so bind each stub to the unique
|
|
784
|
+
`code:method:…::Ctrl.method` node (skip when 0 or >1 match — never
|
|
785
|
+
guess). Mirrors `link_external_stubs` but for the PHP class-method
|
|
786
|
+
uid shape, which the Python-`.py` matcher there does not handle.
|
|
787
|
+
"""
|
|
788
|
+
with self._write_lock:
|
|
789
|
+
stub_rows = self._conn.execute(
|
|
790
|
+
"SELECT id, uid FROM graph_nodes WHERE uid LIKE 'code:external:phproute:%'"
|
|
791
|
+
).fetchall()
|
|
792
|
+
rewrites = 0
|
|
793
|
+
for stub_id, stub_uid in stub_rows:
|
|
794
|
+
key = stub_uid[len("code:external:phproute:") :] # Ctrl.method
|
|
795
|
+
matches = self._conn.execute(
|
|
796
|
+
"SELECT id FROM graph_nodes WHERE kind='method' AND uid LIKE ?",
|
|
797
|
+
(f"%::{key}",),
|
|
798
|
+
).fetchall()
|
|
799
|
+
if len(matches) != 1:
|
|
800
|
+
continue
|
|
801
|
+
self._conn.execute(
|
|
802
|
+
"UPDATE OR IGNORE graph_edges_v12 SET target_id = ? WHERE target_id = ?",
|
|
803
|
+
(int(matches[0][0]), int(stub_id)),
|
|
804
|
+
)
|
|
805
|
+
rewrites += 1
|
|
806
|
+
self._conn.commit()
|
|
807
|
+
return rewrites
|
|
808
|
+
|
|
809
|
+
# -- Read path ---------------------------------------------------------
|
|
810
|
+
|
|
811
|
+
def get_node(self, uid: str) -> GraphNode | None:
|
|
812
|
+
# G18: pure-SELECT, no write_lock — WAL gives concurrent
|
|
813
|
+
# readers; the lock here was serialising reads behind any
|
|
814
|
+
# in-flight write for no correctness benefit.
|
|
815
|
+
# P6: thread-local read connection so parallel get_node calls
|
|
816
|
+
# don't serialise behind the primary conn's mutex+GIL.
|
|
817
|
+
row = (
|
|
818
|
+
self._get_read_conn()
|
|
819
|
+
.execute(
|
|
820
|
+
"""
|
|
821
|
+
SELECT kind, label, uid, file_path, start_line, end_line,
|
|
822
|
+
signature, lang, doc_blob, ast_hash, content_hash,
|
|
823
|
+
metadata_json
|
|
824
|
+
FROM graph_nodes WHERE uid=?
|
|
825
|
+
""",
|
|
826
|
+
(uid,),
|
|
827
|
+
)
|
|
828
|
+
.fetchone()
|
|
829
|
+
)
|
|
830
|
+
if row is None:
|
|
831
|
+
return None
|
|
832
|
+
return self._row_to_node(row)
|
|
833
|
+
|
|
834
|
+
def get_nodes_bulk(self, uids: Sequence[str]) -> dict[str, GraphNode]:
|
|
835
|
+
"""B6: batch variant of get_node — one SELECT per call, not N."""
|
|
836
|
+
if not uids:
|
|
837
|
+
return {}
|
|
838
|
+
uniq = list(dict.fromkeys(uids))
|
|
839
|
+
result: dict[str, GraphNode] = {}
|
|
840
|
+
# SQLite parameter limit is 999; chunk to stay safely under.
|
|
841
|
+
chunk = 500
|
|
842
|
+
# G18: pure-SELECT, no write_lock. P6: thread-local read conn.
|
|
843
|
+
read_conn = self._get_read_conn()
|
|
844
|
+
for start in range(0, len(uniq), chunk):
|
|
845
|
+
group = uniq[start : start + chunk]
|
|
846
|
+
placeholders = ",".join("?" for _ in group)
|
|
847
|
+
rows = read_conn.execute(
|
|
848
|
+
f"""
|
|
849
|
+
SELECT kind, label, uid, file_path, start_line, end_line,
|
|
850
|
+
signature, lang, doc_blob, ast_hash, content_hash,
|
|
851
|
+
metadata_json
|
|
852
|
+
FROM graph_nodes WHERE uid IN ({placeholders})
|
|
853
|
+
""",
|
|
854
|
+
tuple(group),
|
|
855
|
+
).fetchall()
|
|
856
|
+
for row in rows:
|
|
857
|
+
node = self._row_to_node(row)
|
|
858
|
+
result[node.uid] = node
|
|
859
|
+
return result
|
|
860
|
+
|
|
861
|
+
def count_nodes(self, kind: str | None = None) -> int:
|
|
862
|
+
# G18: pure-SELECT, no write_lock. P6: thread-local read conn.
|
|
863
|
+
read_conn = self._get_read_conn()
|
|
864
|
+
if kind is None:
|
|
865
|
+
row = read_conn.execute("SELECT COUNT(*) FROM graph_nodes").fetchone()
|
|
866
|
+
else:
|
|
867
|
+
# Accept legacy or canonical form; storage is canonical.
|
|
868
|
+
try:
|
|
869
|
+
kind_q = normalize_kind(kind).value
|
|
870
|
+
except ValueError:
|
|
871
|
+
kind_q = kind
|
|
872
|
+
row = read_conn.execute(
|
|
873
|
+
"SELECT COUNT(*) FROM graph_nodes WHERE kind=?", (kind_q,)
|
|
874
|
+
).fetchone()
|
|
875
|
+
return int(row[0])
|
|
876
|
+
|
|
877
|
+
def count_edges(self, edge_type: str | None = None) -> int:
|
|
878
|
+
# G17: count DISTINCT logical edges (source, target, edge_type).
|
|
879
|
+
# `list_edges` dedupes via highest-confidence subquery — match
|
|
880
|
+
# that semantic here. G18: no write_lock. P6: thread-local conn.
|
|
881
|
+
read_conn = self._get_read_conn()
|
|
882
|
+
if edge_type is None:
|
|
883
|
+
row = read_conn.execute(
|
|
884
|
+
"SELECT COUNT(*) FROM (SELECT DISTINCT source_id, target_id, edge_type FROM graph_edges_v12)"
|
|
885
|
+
).fetchone()
|
|
886
|
+
else:
|
|
887
|
+
row = read_conn.execute(
|
|
888
|
+
"SELECT COUNT(*) FROM (SELECT DISTINCT source_id, target_id, edge_type FROM graph_edges_v12 WHERE edge_type=?)",
|
|
889
|
+
(edge_type,),
|
|
890
|
+
).fetchone()
|
|
891
|
+
return int(row[0])
|
|
892
|
+
|
|
893
|
+
def list_edges(
|
|
894
|
+
self,
|
|
895
|
+
*,
|
|
896
|
+
source_uid: str | None = None,
|
|
897
|
+
target_uid: str | None = None,
|
|
898
|
+
edge_types: Sequence[str] | None = None,
|
|
899
|
+
confidence_min: float = 0.0,
|
|
900
|
+
include_evidence: bool = False,
|
|
901
|
+
limit: int = 100,
|
|
902
|
+
) -> list[GraphEdge]:
|
|
903
|
+
"""List edges matching filters, ordered by confidence DESC, id ASC.
|
|
904
|
+
|
|
905
|
+
Ordering on id ASC as a tie-break makes results deterministic
|
|
906
|
+
— required for parity testing (Section 12.6 of the plan).
|
|
907
|
+
"""
|
|
908
|
+
where_parts = ["e.confidence >= ?"]
|
|
909
|
+
params: list[Any] = [float(confidence_min)]
|
|
910
|
+
|
|
911
|
+
if source_uid is not None:
|
|
912
|
+
where_parts.append("e.source_id = (SELECT id FROM graph_nodes WHERE uid=?)")
|
|
913
|
+
params.append(source_uid)
|
|
914
|
+
if target_uid is not None:
|
|
915
|
+
where_parts.append("e.target_id = (SELECT id FROM graph_nodes WHERE uid=?)")
|
|
916
|
+
params.append(target_uid)
|
|
917
|
+
if edge_types:
|
|
918
|
+
placeholders = ",".join("?" for _ in edge_types)
|
|
919
|
+
where_parts.append(f"e.edge_type IN ({placeholders})")
|
|
920
|
+
params.extend(edge_types)
|
|
921
|
+
|
|
922
|
+
where_sql = " AND ".join(where_parts)
|
|
923
|
+
# Dedupe across extractors: when multiple extractors emit the
|
|
924
|
+
# same logical edge (source, target, edge_type), keep the
|
|
925
|
+
# highest-confidence row (ties broken by lowest id) so the API
|
|
926
|
+
# surface presents one row per relationship instead of N copies.
|
|
927
|
+
query = f"""
|
|
928
|
+
SELECT e.id, ns.uid, nt.uid, e.edge_type, e.extractor,
|
|
929
|
+
e.confidence, e.source_span
|
|
930
|
+
FROM graph_edges_v12 e
|
|
931
|
+
JOIN graph_nodes ns ON ns.id = e.source_id
|
|
932
|
+
JOIN graph_nodes nt ON nt.id = e.target_id
|
|
933
|
+
WHERE {where_sql}
|
|
934
|
+
AND e.id = (
|
|
935
|
+
SELECT id FROM graph_edges_v12 ee
|
|
936
|
+
WHERE ee.source_id = e.source_id
|
|
937
|
+
AND ee.target_id = e.target_id
|
|
938
|
+
AND ee.edge_type = e.edge_type
|
|
939
|
+
ORDER BY ee.confidence DESC, ee.id ASC
|
|
940
|
+
LIMIT 1
|
|
941
|
+
)
|
|
942
|
+
ORDER BY e.confidence DESC, e.id ASC
|
|
943
|
+
LIMIT ?
|
|
944
|
+
"""
|
|
945
|
+
params.append(int(limit))
|
|
946
|
+
# G18: pure-SELECT, no write_lock. P6: thread-local read conn.
|
|
947
|
+
read_conn = self._get_read_conn()
|
|
948
|
+
rows = read_conn.execute(query, params).fetchall()
|
|
949
|
+
|
|
950
|
+
evidence_by_edge: dict[int, list[sqlite3.Row]] = {}
|
|
951
|
+
if include_evidence and rows:
|
|
952
|
+
ev_rows = read_conn.execute(
|
|
953
|
+
"""
|
|
954
|
+
SELECT edge_id, signal_name, weight, note
|
|
955
|
+
FROM graph_evidence_v12
|
|
956
|
+
WHERE edge_id IN (%s)
|
|
957
|
+
ORDER BY id ASC
|
|
958
|
+
"""
|
|
959
|
+
% ",".join(str(int(r[0])) for r in rows)
|
|
960
|
+
).fetchall()
|
|
961
|
+
for ev in ev_rows:
|
|
962
|
+
evidence_by_edge.setdefault(int(ev[0]), []).append(ev)
|
|
963
|
+
|
|
964
|
+
edges: list[GraphEdge] = []
|
|
965
|
+
for edge_row in rows:
|
|
966
|
+
evidence_tuple: tuple[EvidenceSignal, ...] = ()
|
|
967
|
+
if include_evidence:
|
|
968
|
+
ev_rows = evidence_by_edge.get(int(edge_row[0]), [])
|
|
969
|
+
evidence_tuple = tuple(
|
|
970
|
+
EvidenceSignal(signal_name=r[1], weight=float(r[2]), note=r[3]) for r in ev_rows
|
|
971
|
+
)
|
|
972
|
+
edges.append(
|
|
973
|
+
GraphEdge(
|
|
974
|
+
source_uid=edge_row[1],
|
|
975
|
+
target_uid=edge_row[2],
|
|
976
|
+
edge_type=edge_row[3],
|
|
977
|
+
extractor=edge_row[4],
|
|
978
|
+
confidence=float(edge_row[5]),
|
|
979
|
+
source_span=edge_row[6],
|
|
980
|
+
evidence=evidence_tuple,
|
|
981
|
+
)
|
|
982
|
+
)
|
|
983
|
+
return edges
|
|
984
|
+
|
|
985
|
+
def sample_nodes(self, kind: str | None, limit: int) -> list[GraphNode]:
|
|
986
|
+
"""B13: return up to `limit` nodes, optionally filtered by kind."""
|
|
987
|
+
# G18: pure-SELECT, no write_lock. P6: thread-local read conn.
|
|
988
|
+
read_conn = self._get_read_conn()
|
|
989
|
+
if kind is None:
|
|
990
|
+
rows = read_conn.execute(
|
|
991
|
+
"""
|
|
992
|
+
SELECT kind, label, uid, file_path, start_line, end_line,
|
|
993
|
+
signature, lang, doc_blob, ast_hash, content_hash,
|
|
994
|
+
metadata_json
|
|
995
|
+
FROM graph_nodes
|
|
996
|
+
ORDER BY id ASC
|
|
997
|
+
LIMIT ?
|
|
998
|
+
""",
|
|
999
|
+
(int(limit),),
|
|
1000
|
+
).fetchall()
|
|
1001
|
+
else:
|
|
1002
|
+
try:
|
|
1003
|
+
kind_q = normalize_kind(kind).value
|
|
1004
|
+
except ValueError:
|
|
1005
|
+
kind_q = kind
|
|
1006
|
+
rows = read_conn.execute(
|
|
1007
|
+
"""
|
|
1008
|
+
SELECT kind, label, uid, file_path, start_line, end_line,
|
|
1009
|
+
signature, lang, doc_blob, ast_hash, content_hash,
|
|
1010
|
+
metadata_json
|
|
1011
|
+
FROM graph_nodes
|
|
1012
|
+
WHERE kind = ?
|
|
1013
|
+
ORDER BY id ASC
|
|
1014
|
+
LIMIT ?
|
|
1015
|
+
""",
|
|
1016
|
+
(kind_q, int(limit)),
|
|
1017
|
+
).fetchall()
|
|
1018
|
+
return [self._row_to_node(row) for row in rows]
|
|
1019
|
+
|
|
1020
|
+
# -- Internal helpers --------------------------------------------------
|
|
1021
|
+
|
|
1022
|
+
def _node_id_for_uid(self, uid: str) -> int:
|
|
1023
|
+
row = self._conn.execute("SELECT id FROM graph_nodes WHERE uid=?", (uid,)).fetchone()
|
|
1024
|
+
if row is None:
|
|
1025
|
+
raise ValueError(f"unknown uid {uid!r}: upsert the node before emitting edges")
|
|
1026
|
+
return int(row[0])
|
|
1027
|
+
|
|
1028
|
+
@staticmethod
|
|
1029
|
+
def _row_to_node(row: Sequence[Any]) -> GraphNode:
|
|
1030
|
+
metadata = {}
|
|
1031
|
+
metadata_json = row[11]
|
|
1032
|
+
if metadata_json:
|
|
1033
|
+
try:
|
|
1034
|
+
metadata = json.loads(metadata_json)
|
|
1035
|
+
except json.JSONDecodeError as exc:
|
|
1036
|
+
logger.debug("metadata JSON decode failed for uid=%s: %s", row[2], exc)
|
|
1037
|
+
return GraphNode(
|
|
1038
|
+
kind=row[0],
|
|
1039
|
+
label=row[1],
|
|
1040
|
+
uid=row[2],
|
|
1041
|
+
file_path=row[3],
|
|
1042
|
+
start_line=row[4],
|
|
1043
|
+
end_line=row[5],
|
|
1044
|
+
signature=row[6],
|
|
1045
|
+
lang=row[7],
|
|
1046
|
+
doc_blob=row[8],
|
|
1047
|
+
ast_hash=row[9],
|
|
1048
|
+
content_hash=row[10],
|
|
1049
|
+
metadata=metadata,
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
__all__ = ["SqliteBackend"]
|