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,871 @@
|
|
|
1
|
+
"""Coding OS — Claude-SDK dispatcher (adapters/claude)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from thinking_os.dispatcher import DispatchRequest, DispatchResult
|
|
16
|
+
from thinking_os.dispatcher_helpers import extract_json_block, load_agent_prompt
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("coding_os.dispatcher.claude_sdk")
|
|
19
|
+
|
|
20
|
+
# High-tier reasoning models (Fable 5, Opus 4.8/4.7) take the "xhigh"
|
|
21
|
+
# effort level — the best setting for coding/agentic work and the Claude
|
|
22
|
+
# Code default. "xhigh" is available in the Py SDK since 0.1.74 (it was
|
|
23
|
+
# TS-only before) and falls back to "high" on models that don't support
|
|
24
|
+
# it. Prefix-matched so dated snapshots (…-20260101) are covered too.
|
|
25
|
+
_XHIGH_EFFORT_MODEL_PREFIXES = (
|
|
26
|
+
"claude-fable-5",
|
|
27
|
+
"claude-opus-4-8",
|
|
28
|
+
"claude-opus-4-7",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Default MCP allow-list pattern. coding-os exposes ~60 cos_* tools via
|
|
32
|
+
# the central FastMCP server registered at .mcp.json::mcpServers.coding-os.
|
|
33
|
+
# `acceptEdits` and `default` permission modes do NOT auto-approve MCP,
|
|
34
|
+
# so we list them explicitly. Wildcard form per SDK docs §D.2.
|
|
35
|
+
_DEFAULT_COS_MCP_ALLOW = "mcp__coding-os__*"
|
|
36
|
+
|
|
37
|
+
# Destructive Bash patterns headless dispatch must never run. Hard-deny
|
|
38
|
+
# wins over allow rules even in bypassPermissions (digest §B.4 order),
|
|
39
|
+
# so this is defense-in-depth on top of permission_mode="dontAsk".
|
|
40
|
+
_DESTRUCTIVE_BASH_DENY = (
|
|
41
|
+
"Bash(rm -rf:*)",
|
|
42
|
+
"Bash(rm -fr:*)",
|
|
43
|
+
"Bash(git push --force:*)",
|
|
44
|
+
"Bash(git push -f:*)",
|
|
45
|
+
"Bash(git reset --hard:*)",
|
|
46
|
+
"Bash(git clean -f:*)",
|
|
47
|
+
"Bash(sudo:*)",
|
|
48
|
+
"Bash(curl * | bash:*)",
|
|
49
|
+
"Bash(curl * | sh:*)",
|
|
50
|
+
"Bash(wget * | bash:*)",
|
|
51
|
+
"Bash(wget * | sh:*)",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Base tool allow-list for the interactive Hub-chat surface. The Claude CLI
|
|
55
|
+
# treats --allowedTools as an EXCLUSIVE allow-list under permission_mode=
|
|
56
|
+
# "dontAsk" (verified live: listing only the MCP wildcard denies Read), so
|
|
57
|
+
# the chat profile must enumerate the base tools it keeps. Write/Edit/
|
|
58
|
+
# MultiEdit are intentionally absent — chat is read+research+MCP and must
|
|
59
|
+
# not mutate code (closes the dogfood-corruption gap by construction).
|
|
60
|
+
_CHAT_BASE_TOOLS = (
|
|
61
|
+
"Read",
|
|
62
|
+
"Grep",
|
|
63
|
+
"Glob",
|
|
64
|
+
"Bash",
|
|
65
|
+
"WebFetch",
|
|
66
|
+
"WebSearch",
|
|
67
|
+
"TodoWrite",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
_CHAT_PROFILES = ("chat", "chat_resume")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _cos_mcp_servers(cwd: str) -> dict[str, Any]:
|
|
74
|
+
try:
|
|
75
|
+
data = json.loads((Path(cwd) / ".mcp.json").read_text(encoding="utf-8"))
|
|
76
|
+
cos = (data.get("mcpServers") or {}).get("coding-os")
|
|
77
|
+
return {"coding-os": cos} if cos else {}
|
|
78
|
+
except (OSError, ValueError) as exc:
|
|
79
|
+
logger.debug("cos mcp_servers unavailable for %s: %s", cwd, exc)
|
|
80
|
+
return {}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
_MODEL_ALIAS_CACHE: dict[str, str] = {}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Map a tier alias (sonnet/opus/haiku) → a concrete adapter.yaml model id before
|
|
87
|
+
# it reaches the SDK: the kernel router speaks in tiers (claude-sdk.md — the
|
|
88
|
+
# adapter owns alias→id), so a routed 'sonnet' must become 'claude-sonnet-4-6'.
|
|
89
|
+
# `claude-*` ids and None pass through; an unknown non-id falls back to the
|
|
90
|
+
# adapter default so the SDK never receives a bare tier (R10/F6).
|
|
91
|
+
def _resolve_model_alias(model: str | None) -> str | None:
|
|
92
|
+
if not model or model.startswith("claude-"):
|
|
93
|
+
return model
|
|
94
|
+
alias = model.strip().lower()
|
|
95
|
+
if not alias:
|
|
96
|
+
return model
|
|
97
|
+
if alias in _MODEL_ALIAS_CACHE:
|
|
98
|
+
return _MODEL_ALIAS_CACHE[alias]
|
|
99
|
+
resolved = model
|
|
100
|
+
try:
|
|
101
|
+
import yaml
|
|
102
|
+
|
|
103
|
+
data = (
|
|
104
|
+
yaml.safe_load(
|
|
105
|
+
(Path(__file__).resolve().parent / "adapter.yaml").read_text(encoding="utf-8")
|
|
106
|
+
)
|
|
107
|
+
or {}
|
|
108
|
+
)
|
|
109
|
+
models = [m for m in (data.get("models") or []) if isinstance(m, dict) and m.get("id")]
|
|
110
|
+
match = next((str(m["id"]) for m in models if alias in str(m["id"]).lower()), None)
|
|
111
|
+
if match is None:
|
|
112
|
+
match = next((str(m["id"]) for m in models if m.get("default")), None)
|
|
113
|
+
if match:
|
|
114
|
+
logger.warning("unknown model alias %r → adapter default %s", model, match)
|
|
115
|
+
if match:
|
|
116
|
+
resolved = match
|
|
117
|
+
except Exception as exc:
|
|
118
|
+
logger.debug("model alias resolution skipped for %r: %s", model, exc)
|
|
119
|
+
_MODEL_ALIAS_CACHE[alias] = resolved
|
|
120
|
+
return resolved
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _hub_settings_path(cwd: str) -> Path:
|
|
124
|
+
state_dir = os.environ.get("COS_STATE_DIR")
|
|
125
|
+
if state_dir:
|
|
126
|
+
return Path(state_dir) / "hub-settings.json"
|
|
127
|
+
return Path(cwd or os.getcwd()) / ".coding-os" / "hub-settings.json"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# Deterministic auth-mode override (TASK-756): Hub Settings → Claude Auth lets a
|
|
131
|
+
# project pick "subscription" (default — the CLI's own OAuth session, byte-
|
|
132
|
+
# identical to before this existed) or "api_key" (forward the user's key as
|
|
133
|
+
# ANTHROPIC_API_KEY). Per platform.claude.com/docs/en/authentication, an API
|
|
134
|
+
# key set on the subprocess env beats subscription OAuth in non-interactive/SDK
|
|
135
|
+
# mode — so "subscription" mode must EXPLICITLY clear the var (not merely omit
|
|
136
|
+
# it), or a stray ANTHROPIC_API_KEY already in the Hub server's own shell would
|
|
137
|
+
# silently override the user's chosen mode. Always returns an override (never
|
|
138
|
+
# {} = no-op) so this is a real switch, not a best-effort hint.
|
|
139
|
+
def _claude_auth_env(cwd: str) -> dict[str, str]:
|
|
140
|
+
try:
|
|
141
|
+
data = json.loads(_hub_settings_path(cwd).read_text(encoding="utf-8"))
|
|
142
|
+
auth = data.get("claude_auth") if isinstance(data, dict) else None
|
|
143
|
+
if isinstance(auth, dict) and auth.get("mode") == "api_key":
|
|
144
|
+
key = auth.get("api_key")
|
|
145
|
+
if isinstance(key, str) and key:
|
|
146
|
+
return {"ANTHROPIC_API_KEY": key}
|
|
147
|
+
except (OSError, ValueError) as exc:
|
|
148
|
+
logger.debug("claude_auth resolution skipped for cwd=%r: %s", cwd, exc)
|
|
149
|
+
return {"ANTHROPIC_API_KEY": ""}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def claude_session_options(
|
|
153
|
+
profile: str,
|
|
154
|
+
*,
|
|
155
|
+
cwd: str,
|
|
156
|
+
model: str | None = None,
|
|
157
|
+
system_prompt: Any = None,
|
|
158
|
+
resume: str | None = None,
|
|
159
|
+
fork: bool = False,
|
|
160
|
+
effort: str | None = None,
|
|
161
|
+
):
|
|
162
|
+
"""Build ClaudeAgentOptions for a profile — the SSOT for Claude SDK sessions (docs/adapters/session-options-builder.md)."""
|
|
163
|
+
from claude_agent_sdk import ClaudeAgentOptions
|
|
164
|
+
|
|
165
|
+
if profile not in _CHAT_PROFILES:
|
|
166
|
+
raise NotImplementedError(
|
|
167
|
+
f"claude_session_options: profile {profile!r} not yet migrated (TASK-417 phases)"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
model = _resolve_model_alias(model) # tier alias → concrete id (R10/F6)
|
|
171
|
+
opts: dict[str, Any] = dict(
|
|
172
|
+
cwd=cwd,
|
|
173
|
+
model=model,
|
|
174
|
+
permission_mode="dontAsk",
|
|
175
|
+
# Fast conversational surface: skip the ~40s project SessionStart hook
|
|
176
|
+
# suite. Capability comes from programmatic mcp_servers below, NOT from
|
|
177
|
+
# setting_sources, so cos_* works without that latency.
|
|
178
|
+
setting_sources=[],
|
|
179
|
+
include_partial_messages=True,
|
|
180
|
+
# P2 capability: register coding-os MCP programmatically — renders
|
|
181
|
+
# --mcp-config independent of setting_sources (subprocess_cli.py:307).
|
|
182
|
+
mcp_servers=_cos_mcp_servers(cwd),
|
|
183
|
+
# Exclusive allow-list under dontAsk: base tools + the MCP wildcard.
|
|
184
|
+
allowed_tools=[*_CHAT_BASE_TOOLS, _DEFAULT_COS_MCP_ALLOW],
|
|
185
|
+
# P3: destructive-Bash deny floor (rm -rf / force-push / sudo / pipe-to-sh).
|
|
186
|
+
disallowed_tools=list(_DESTRUCTIVE_BASH_DENY),
|
|
187
|
+
# Hub Settings → Claude Auth (TASK-756): subscription OAuth by default,
|
|
188
|
+
# ANTHROPIC_API_KEY when the project opted into api_key mode.
|
|
189
|
+
env=_claude_auth_env(cwd),
|
|
190
|
+
)
|
|
191
|
+
if system_prompt is not None:
|
|
192
|
+
opts["system_prompt"] = system_prompt
|
|
193
|
+
if effort:
|
|
194
|
+
opts["effort"] = effort
|
|
195
|
+
if profile == "chat_resume":
|
|
196
|
+
if resume:
|
|
197
|
+
opts["resume"] = resume
|
|
198
|
+
opts["fork_session"] = fork
|
|
199
|
+
return ClaudeAgentOptions(**opts)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def claude_agent_options(**kwargs: Any):
|
|
203
|
+
"""Generic ClaudeAgentOptions constructor — the adapter seam core routes every
|
|
204
|
+
non-profile option build through (P8: core never constructs the SDK type itself)."""
|
|
205
|
+
from claude_agent_sdk import ClaudeAgentOptions
|
|
206
|
+
|
|
207
|
+
return ClaudeAgentOptions(**kwargs)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# OTEL env vars the dispatcher copies from the parent process so the
|
|
211
|
+
# sub-session emits to the same collector. No collector defaults are
|
|
212
|
+
# bundled (D5) — operator sets exporter / endpoint / headers.
|
|
213
|
+
_OTEL_FORWARDED_VARS = (
|
|
214
|
+
"OTEL_TRACES_EXPORTER",
|
|
215
|
+
"OTEL_METRICS_EXPORTER",
|
|
216
|
+
"OTEL_LOGS_EXPORTER",
|
|
217
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL",
|
|
218
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
219
|
+
"OTEL_EXPORTER_OTLP_HEADERS",
|
|
220
|
+
"OTEL_RESOURCE_ATTRIBUTES",
|
|
221
|
+
"OTEL_METRIC_EXPORT_INTERVAL",
|
|
222
|
+
"OTEL_LOGS_EXPORT_INTERVAL",
|
|
223
|
+
"OTEL_TRACES_EXPORT_INTERVAL",
|
|
224
|
+
"OTEL_LOG_USER_PROMPTS",
|
|
225
|
+
"OTEL_LOG_TOOL_DETAILS",
|
|
226
|
+
"OTEL_LOG_TOOL_CONTENT",
|
|
227
|
+
"OTEL_LOG_RAW_API_BODIES",
|
|
228
|
+
"ENABLE_BETA_TRACING_DETAILED",
|
|
229
|
+
"BETA_TRACING_ENDPOINT",
|
|
230
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY",
|
|
231
|
+
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _resolve_output_schema(meta: dict[str, Any]) -> dict[str, Any] | None:
|
|
236
|
+
raw = meta.get("output_schema") if isinstance(meta, dict) else None
|
|
237
|
+
if not isinstance(raw, str) or not raw.strip():
|
|
238
|
+
return None
|
|
239
|
+
cls_name = raw.split(".")[-1].strip()
|
|
240
|
+
if not cls_name.isidentifier():
|
|
241
|
+
logger.warning("invalid output_schema reference %r — ignoring", raw)
|
|
242
|
+
return None
|
|
243
|
+
try:
|
|
244
|
+
from thinking_os import cognition_schemas as _schemas
|
|
245
|
+
except ImportError as exc:
|
|
246
|
+
logger.warning("cognition_schemas import failed: %s", exc)
|
|
247
|
+
return None
|
|
248
|
+
schema_cls = getattr(_schemas, cls_name, None)
|
|
249
|
+
if schema_cls is None or not hasattr(schema_cls, "model_json_schema"):
|
|
250
|
+
logger.warning("no Pydantic class %s in cognition_schemas", cls_name)
|
|
251
|
+
return None
|
|
252
|
+
try:
|
|
253
|
+
return schema_cls.model_json_schema()
|
|
254
|
+
except (TypeError, ValueError) as exc:
|
|
255
|
+
logger.warning("model_json_schema() failed for %s: %s", cls_name, exc)
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# ---------------------------------------------------------------------------
|
|
260
|
+
# Dispatcher
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _presence_write(
|
|
265
|
+
project_root: Path, agent: str, session_id: str, event: str, pid: int | None = None
|
|
266
|
+
) -> None:
|
|
267
|
+
"""Write a single presence event for an SDK-spawned sub-agent."""
|
|
268
|
+
import json as _json
|
|
269
|
+
import os as _os
|
|
270
|
+
import time as _time
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
d = project_root / ".coding-os" / agent / "sessions"
|
|
274
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
275
|
+
path = d / f"{session_id}.json"
|
|
276
|
+
prev: dict[str, Any] = {}
|
|
277
|
+
if path.exists():
|
|
278
|
+
try:
|
|
279
|
+
prev = _json.loads(path.read_text(encoding="utf-8"))
|
|
280
|
+
except (OSError, _json.JSONDecodeError):
|
|
281
|
+
prev = {}
|
|
282
|
+
now = int(_time.time())
|
|
283
|
+
# Schema parity with _helpers/presence_write.py (the canonical writer):
|
|
284
|
+
# preserve model/sdk_uuid/used_tokens/context_updated_at so the Hub
|
|
285
|
+
# reader resolves them for SDK sub-agents too (P5).
|
|
286
|
+
new = {
|
|
287
|
+
"agent": agent,
|
|
288
|
+
"session_id": session_id,
|
|
289
|
+
"pid": int(pid) if pid is not None else int(prev.get("pid") or _os.getpid()),
|
|
290
|
+
"started_at": prev.get("started_at"),
|
|
291
|
+
"last_prompt_at": prev.get("last_prompt_at"),
|
|
292
|
+
"last_tool_at": prev.get("last_tool_at"),
|
|
293
|
+
"last_stop_at": prev.get("last_stop_at"),
|
|
294
|
+
"ended_at": prev.get("ended_at"),
|
|
295
|
+
"model": prev.get("model"),
|
|
296
|
+
"sdk_uuid": prev.get("sdk_uuid"),
|
|
297
|
+
"used_tokens": prev.get("used_tokens"),
|
|
298
|
+
"context_updated_at": prev.get("context_updated_at"),
|
|
299
|
+
}
|
|
300
|
+
if event == "start":
|
|
301
|
+
new["started_at"] = now
|
|
302
|
+
new["ended_at"] = None
|
|
303
|
+
new["last_stop_at"] = None
|
|
304
|
+
elif event == "prompt":
|
|
305
|
+
new["last_prompt_at"] = now
|
|
306
|
+
new["last_stop_at"] = None
|
|
307
|
+
new["started_at"] = new["started_at"] or now
|
|
308
|
+
elif event == "tool":
|
|
309
|
+
new["last_tool_at"] = now
|
|
310
|
+
new["started_at"] = new["started_at"] or now
|
|
311
|
+
elif event == "stop":
|
|
312
|
+
new["last_stop_at"] = now
|
|
313
|
+
elif event == "end":
|
|
314
|
+
new["ended_at"] = now
|
|
315
|
+
# Keep the .json stem on the temp file (canonical writer uses
|
|
316
|
+
# f"{path}.tmp.{pid}") so presence_gc reaps crash-orphaned temps (P31).
|
|
317
|
+
tmp = path.parent / f"{path.name}.tmp.{_os.getpid()}"
|
|
318
|
+
tmp.write_text(_json.dumps(new, separators=(",", ":")), encoding="utf-8")
|
|
319
|
+
_os.replace(tmp, path)
|
|
320
|
+
except OSError as exc:
|
|
321
|
+
logger.debug("SDK presence write failed for %s: %s", session_id, exc)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _dispatch_trace_content_enabled() -> bool:
|
|
325
|
+
return os.environ.get("COS_DISPATCH_EVENT_CONTENT", "").strip().lower() in {
|
|
326
|
+
"1",
|
|
327
|
+
"true",
|
|
328
|
+
"yes",
|
|
329
|
+
"on",
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _emit_dispatch_trace(
|
|
334
|
+
session_id: str, kind: str, formula_id: str | None, data: dict[str, Any] | None = None
|
|
335
|
+
) -> None:
|
|
336
|
+
# Tee a dispatch lifecycle/turn event to the append-only cognition trace
|
|
337
|
+
# sink (thinking_os.tracing) so the Hub can tail + replay the run. Fail-open:
|
|
338
|
+
# a tracing failure must never alter the returned EvidenceBundle or break the
|
|
339
|
+
# dispatch. Partial-message text rides along only when content is explicitly
|
|
340
|
+
# enabled (COS_DISPATCH_EVENT_CONTENT), off by default.
|
|
341
|
+
try:
|
|
342
|
+
from thinking_os.tracing import emit
|
|
343
|
+
|
|
344
|
+
emit(session_id, kind, data or {}, role=formula_id)
|
|
345
|
+
except Exception as exc:
|
|
346
|
+
logger.debug("dispatch trace emit skipped (%s): %s", kind, exc)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class ClaudeSDKDispatcher:
|
|
350
|
+
name = "claude-sdk"
|
|
351
|
+
|
|
352
|
+
def __init__(self) -> None:
|
|
353
|
+
self._sdk_ok = False
|
|
354
|
+
self._import_error: str | None = None
|
|
355
|
+
try:
|
|
356
|
+
import claude_agent_sdk # noqa: F401
|
|
357
|
+
|
|
358
|
+
self._sdk_ok = True
|
|
359
|
+
except ImportError as exc:
|
|
360
|
+
self._import_error = str(exc)
|
|
361
|
+
|
|
362
|
+
def available(self) -> bool:
|
|
363
|
+
return self._sdk_ok
|
|
364
|
+
|
|
365
|
+
async def dispatch(self, request: DispatchRequest) -> DispatchResult:
|
|
366
|
+
t0 = time.monotonic()
|
|
367
|
+
if not self._sdk_ok:
|
|
368
|
+
return DispatchResult(
|
|
369
|
+
formula_id=request.formula_id,
|
|
370
|
+
status="error",
|
|
371
|
+
error=f"claude-agent-sdk not importable: {self._import_error}",
|
|
372
|
+
dispatcher_name=self.name,
|
|
373
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
from claude_agent_sdk import (
|
|
377
|
+
AssistantMessage,
|
|
378
|
+
ClaudeAgentOptions,
|
|
379
|
+
HookMatcher,
|
|
380
|
+
ResultMessage,
|
|
381
|
+
TextBlock,
|
|
382
|
+
UserMessage,
|
|
383
|
+
query,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# SDK searches cwd if agent_file is relative — silently picks the
|
|
387
|
+
# wrong file if a same-named role lives in another path. Force
|
|
388
|
+
# absolute so a misconfigured caller fails loudly here, not 30s
|
|
389
|
+
# into a wrong dispatch.
|
|
390
|
+
if not Path(request.agent_file).is_absolute():
|
|
391
|
+
return DispatchResult(
|
|
392
|
+
formula_id=request.formula_id,
|
|
393
|
+
status="error",
|
|
394
|
+
error=f"agent_file must be absolute, got: {request.agent_file!r}",
|
|
395
|
+
dispatcher_name=self.name,
|
|
396
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
try:
|
|
400
|
+
system_prompt_body, agent_meta = load_agent_prompt(request.agent_file)
|
|
401
|
+
except FileNotFoundError as exc:
|
|
402
|
+
return DispatchResult(
|
|
403
|
+
formula_id=request.formula_id,
|
|
404
|
+
status="error",
|
|
405
|
+
error=str(exc),
|
|
406
|
+
dispatcher_name=self.name,
|
|
407
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Skill inheritance — formula sub-sessions get a fresh context, so
|
|
411
|
+
# they don't inherit parent skills. Each role's frontmatter
|
|
412
|
+
# declares which skills it needs (e.g. implementer → ["clean-code"]).
|
|
413
|
+
# Other adapters that don't honor this key ignore it (Rule 1).
|
|
414
|
+
role_skills = agent_meta.get("skills") if isinstance(agent_meta, dict) else None
|
|
415
|
+
if role_skills is not None and (
|
|
416
|
+
not isinstance(role_skills, list) or not all(isinstance(s, str) for s in role_skills)
|
|
417
|
+
):
|
|
418
|
+
role_skills = None
|
|
419
|
+
logger.warning(
|
|
420
|
+
"agent file %s has invalid `skills` frontmatter — ignoring",
|
|
421
|
+
request.agent_file,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Append the formula spec to Claude Code's preset prompt. The
|
|
425
|
+
# preset carries Claude Code's coding/safety baseline; the formula
|
|
426
|
+
# body adds the role-specific Output contract. Setting
|
|
427
|
+
# `exclude_dynamic_sections=True` strips per-cwd state (git,
|
|
428
|
+
# date, OS, memory paths) from the system prompt and emits it as
|
|
429
|
+
# a first-user-message block instead — this is what makes the
|
|
430
|
+
# prompt cache reusable across consumer projects.
|
|
431
|
+
formula_append = (
|
|
432
|
+
f"{system_prompt_body}\n\n"
|
|
433
|
+
f"## Dispatch Context\n"
|
|
434
|
+
f"- Formula: {request.formula_id}\n"
|
|
435
|
+
f"- Persona: {request.persona_id or 'n/a'}\n"
|
|
436
|
+
f"- Intensity: {request.intensity}\n\n"
|
|
437
|
+
f"## Instruction\n"
|
|
438
|
+
f"Produce the EvidenceBundle slice for this formula as a single "
|
|
439
|
+
f"```json ... ``` block at the end of your response. Do not wrap "
|
|
440
|
+
f"it in additional prose."
|
|
441
|
+
)
|
|
442
|
+
system_prompt: dict[str, Any] = {
|
|
443
|
+
"type": "preset",
|
|
444
|
+
"preset": "claude_code",
|
|
445
|
+
"append": formula_append,
|
|
446
|
+
"exclude_dynamic_sections": True,
|
|
447
|
+
}
|
|
448
|
+
user_prompt = (
|
|
449
|
+
f"Input slice (upstream formulas only):\n"
|
|
450
|
+
f"```json\n{json.dumps(request.input_slice, indent=2, default=str)}\n```\n\n"
|
|
451
|
+
f"{request.prompt}"
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# Always include the coding-os MCP wildcard alongside any caller-
|
|
455
|
+
# provided allow-list. `acceptEdits` does NOT auto-approve MCP,
|
|
456
|
+
# so without an explicit entry every cos_* tool call would deny
|
|
457
|
+
# in dontAsk mode.
|
|
458
|
+
allow_list = list(request.allowed_tools or [])
|
|
459
|
+
if not any(item.startswith("mcp__coding-os") for item in allow_list):
|
|
460
|
+
allow_list.append(_DEFAULT_COS_MCP_ALLOW)
|
|
461
|
+
|
|
462
|
+
# Resolve a routed tier alias (sonnet/opus/...) to a concrete adapter
|
|
463
|
+
# model id BEFORE it reaches the SDK or the effort gate (R10/F6).
|
|
464
|
+
resolved_model = _resolve_model_alias(request.model)
|
|
465
|
+
|
|
466
|
+
# High-tier models get "xhigh"; everything else uses the SDK
|
|
467
|
+
# default (None → "high"). See _XHIGH_EFFORT_MODEL_PREFIXES.
|
|
468
|
+
effort: str | None = None
|
|
469
|
+
if resolved_model and resolved_model.startswith(_XHIGH_EFFORT_MODEL_PREFIXES):
|
|
470
|
+
effort = "xhigh"
|
|
471
|
+
|
|
472
|
+
# Structured output (T1) — opt-in per role via
|
|
473
|
+
# `structured_output: true` frontmatter. SDK enforces the
|
|
474
|
+
# schema and surfaces failures as
|
|
475
|
+
# subtype="error_max_structured_output_retries".
|
|
476
|
+
output_format: dict[str, Any] | None = None
|
|
477
|
+
wants_structured = bool(
|
|
478
|
+
isinstance(agent_meta, dict) and agent_meta.get("structured_output")
|
|
479
|
+
)
|
|
480
|
+
if wants_structured:
|
|
481
|
+
schema = _resolve_output_schema(agent_meta)
|
|
482
|
+
if schema is not None:
|
|
483
|
+
output_format = {"type": "json_schema", "schema": schema}
|
|
484
|
+
else:
|
|
485
|
+
logger.warning(
|
|
486
|
+
"role %s requested structured_output but schema could not be "
|
|
487
|
+
"resolved — falling back to regex extraction",
|
|
488
|
+
request.formula_id,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
# Forward telemetry env so the sub-session emits to the same
|
|
492
|
+
# OTEL collector as the parent (D5 leaves the collector to
|
|
493
|
+
# operators; we just propagate). OTEL_SERVICE_NAME identifies
|
|
494
|
+
# the dispatcher distinctly from a normal claude-code session.
|
|
495
|
+
# Hub Settings → Claude Auth (TASK-756) first, so an OTEL var can never
|
|
496
|
+
# shadow the ANTHROPIC_API_KEY override (disjoint key sets, but explicit
|
|
497
|
+
# ordering keeps the precedence obvious to a future reader).
|
|
498
|
+
env: dict[str, str] = _claude_auth_env(request.cwd or os.getcwd())
|
|
499
|
+
for var in _OTEL_FORWARDED_VARS:
|
|
500
|
+
value = os.environ.get(var)
|
|
501
|
+
if value:
|
|
502
|
+
env[var] = value
|
|
503
|
+
env.setdefault("OTEL_SERVICE_NAME", "coding-os-claude")
|
|
504
|
+
env.setdefault("OTEL_METRICS_INCLUDE_SESSION_ID", "true")
|
|
505
|
+
|
|
506
|
+
# Long-context beta (D6). Caller opts in per request; default
|
|
507
|
+
# short context to keep cache hits high and cost predictable.
|
|
508
|
+
betas = ["context-1m-2025-08-07"] if request.long_context else None
|
|
509
|
+
|
|
510
|
+
logger.debug(
|
|
511
|
+
"claude-sdk dispatch: formula=%s model=%s effort=%s "
|
|
512
|
+
"structured=%s budget_usd=%s tools=%s",
|
|
513
|
+
request.formula_id,
|
|
514
|
+
resolved_model,
|
|
515
|
+
effort,
|
|
516
|
+
bool(output_format),
|
|
517
|
+
request.max_budget_usd,
|
|
518
|
+
allow_list,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Pre-compute sub-session identity so programmatic hook closures
|
|
522
|
+
# below can stamp presence updates with a stable id. Same path
|
|
523
|
+
# safety as the existing presence_write call (formula_id already
|
|
524
|
+
# restricted to [A-Za-z0-9_-] by DispatchRequest validator).
|
|
525
|
+
safe_formula = re.sub(r"[^A-Za-z0-9_-]", "_", request.formula_id) or "formula"
|
|
526
|
+
sub_session_id = f"ses-claude-sdk-{safe_formula}-{int(time.time())}-{os.getpid()}"
|
|
527
|
+
project_root = Path(request.cwd) if request.cwd else Path(os.getcwd())
|
|
528
|
+
|
|
529
|
+
# Per-dispatch state captured by programmatic hooks (T3.1, T3.2)
|
|
530
|
+
# and by the message-stream handler (T3.3 / T1.3). Closures
|
|
531
|
+
# below bind to these locals so concurrent dispatches stay
|
|
532
|
+
# isolated.
|
|
533
|
+
transcript_parts: list[str] = []
|
|
534
|
+
# v27: capture the model the dispatcher requested so persistence
|
|
535
|
+
# can fill formula_dispatches.model without re-deriving from usage.
|
|
536
|
+
result_meta: dict[str, Any] = {"model": resolved_model} if resolved_model else {}
|
|
537
|
+
result_subtype: str | None = None
|
|
538
|
+
structured_output: Any = None
|
|
539
|
+
tool_calls: list[dict[str, Any]] = []
|
|
540
|
+
tool_failures: list[dict[str, Any]] = []
|
|
541
|
+
# T9.2 — UserMessage UUIDs for file checkpointing replay
|
|
542
|
+
checkpoint_uuids: list[str] = []
|
|
543
|
+
|
|
544
|
+
# Hook callbacks must NEVER raise — the SDK propagates exceptions
|
|
545
|
+
# back to the CLI subprocess which exits 1, killing the dispatch.
|
|
546
|
+
# Wrap every body in try/except + log; always return `{}` so the
|
|
547
|
+
# SDK keeps streaming.
|
|
548
|
+
async def _pre_tool_use(
|
|
549
|
+
input_data: dict[str, Any],
|
|
550
|
+
tool_use_id: str | None,
|
|
551
|
+
_ctx: Any,
|
|
552
|
+
) -> dict[str, Any]:
|
|
553
|
+
try:
|
|
554
|
+
tool_calls.append(
|
|
555
|
+
{
|
|
556
|
+
"tool_use_id": tool_use_id,
|
|
557
|
+
"tool_name": input_data.get("tool_name"),
|
|
558
|
+
"tool_input": input_data.get("tool_input"),
|
|
559
|
+
}
|
|
560
|
+
)
|
|
561
|
+
except Exception as exc:
|
|
562
|
+
logger.debug("PreToolUse hook capture failed: %s", exc)
|
|
563
|
+
return {}
|
|
564
|
+
|
|
565
|
+
async def _post_tool_use_failure(
|
|
566
|
+
input_data: dict[str, Any],
|
|
567
|
+
tool_use_id: str | None,
|
|
568
|
+
_ctx: Any,
|
|
569
|
+
) -> dict[str, Any]:
|
|
570
|
+
try:
|
|
571
|
+
tool_failures.append(
|
|
572
|
+
{
|
|
573
|
+
"tool_use_id": tool_use_id,
|
|
574
|
+
"tool_name": input_data.get("tool_name"),
|
|
575
|
+
"tool_response": input_data.get("tool_response"),
|
|
576
|
+
}
|
|
577
|
+
)
|
|
578
|
+
logger.warning(
|
|
579
|
+
"claude-sdk dispatch tool failure: formula=%s tool=%s",
|
|
580
|
+
request.formula_id,
|
|
581
|
+
input_data.get("tool_name"),
|
|
582
|
+
)
|
|
583
|
+
except Exception as exc:
|
|
584
|
+
logger.debug("PostToolUseFailure hook capture failed: %s", exc)
|
|
585
|
+
return {}
|
|
586
|
+
|
|
587
|
+
# `--json-schema` activates a `StructuredOutput` tool the model
|
|
588
|
+
# invokes to deliver the typed payload. That tool call AND the
|
|
589
|
+
# tool_result it produces each burn a turn, plus the model
|
|
590
|
+
# often emits a closing assistant turn after the result. Using
|
|
591
|
+
# 3 turns avoids spurious `error_max_turns` subtypes when the
|
|
592
|
+
# model decides to acknowledge before stopping. Even then the
|
|
593
|
+
# post-stream handler treats a populated `structured_output`
|
|
594
|
+
# as success regardless of subtype.
|
|
595
|
+
max_turns = (
|
|
596
|
+
request.max_turns
|
|
597
|
+
if request.max_turns is not None
|
|
598
|
+
else (3 if output_format is not None else 1)
|
|
599
|
+
)
|
|
600
|
+
opts_kwargs: dict[str, Any] = dict(
|
|
601
|
+
system_prompt=system_prompt,
|
|
602
|
+
max_turns=max_turns,
|
|
603
|
+
allowed_tools=allow_list,
|
|
604
|
+
disallowed_tools=list(_DESTRUCTIVE_BASH_DENY),
|
|
605
|
+
cwd=request.cwd,
|
|
606
|
+
# Headless dispatch: never prompt the user. Allow-list is the
|
|
607
|
+
# contract; unmatched tools deny silently.
|
|
608
|
+
permission_mode="dontAsk",
|
|
609
|
+
# Isolate from the host user's ~/.claude/. The formula sub-
|
|
610
|
+
# session must be reproducible across machines — user
|
|
611
|
+
# settings, memory, and CLAUDE.md outside the project root
|
|
612
|
+
# would silently change behavior.
|
|
613
|
+
setting_sources=["project"],
|
|
614
|
+
model=resolved_model,
|
|
615
|
+
effort=effort,
|
|
616
|
+
# Per-role skill inheritance. Sub-sessions don't inherit
|
|
617
|
+
# parent skills, so each role declares its required skills
|
|
618
|
+
# in its agent file's frontmatter. None = use SDK default.
|
|
619
|
+
skills=role_skills,
|
|
620
|
+
env=env,
|
|
621
|
+
)
|
|
622
|
+
if output_format is not None:
|
|
623
|
+
opts_kwargs["output_format"] = output_format
|
|
624
|
+
if request.max_budget_usd is not None:
|
|
625
|
+
opts_kwargs["max_budget_usd"] = float(request.max_budget_usd)
|
|
626
|
+
if betas is not None:
|
|
627
|
+
opts_kwargs["betas"] = betas
|
|
628
|
+
# The CLI validates --session-id as a UUID, so the SDK gets a real
|
|
629
|
+
# UUID while presence/trace keys keep the readable sub_session_id;
|
|
630
|
+
# result_meta carries the mapping for resume debugging.
|
|
631
|
+
sdk_session_uuid = str(uuid.uuid4())
|
|
632
|
+
opts_kwargs["session_id"] = sdk_session_uuid
|
|
633
|
+
result_meta["sdk_session_id"] = sdk_session_uuid
|
|
634
|
+
# T9.1: enable file checkpointing for roles that declare it in frontmatter.
|
|
635
|
+
# Implements checkpoint/replay for edit-heavy roles (implementer/refactorer).
|
|
636
|
+
if isinstance(agent_meta, dict) and agent_meta.get("enable_file_checkpointing"):
|
|
637
|
+
opts_kwargs["enable_file_checkpointing"] = True
|
|
638
|
+
# Programmatic hooks (T3) — these run inline in the
|
|
639
|
+
# dispatcher process, capturing tool call metadata and failures
|
|
640
|
+
# for the formula_dispatches audit row. Empty matcher = match
|
|
641
|
+
# every tool. PostToolUseFailure surfaces tool errors that the
|
|
642
|
+
# SDK would otherwise swallow into the transcript.
|
|
643
|
+
# T3.4 [-]: SubagentStart / SubagentStop not wired here — D1
|
|
644
|
+
# chose query() over Agent-tool, so no sub-agents are spawned.
|
|
645
|
+
# These events still fire in interactive sessions via registry.yaml.
|
|
646
|
+
opts_kwargs["hooks"] = {
|
|
647
|
+
"PreToolUse": [HookMatcher(matcher="", hooks=[_pre_tool_use])],
|
|
648
|
+
"PostToolUseFailure": [HookMatcher(matcher="", hooks=[_post_tool_use_failure])],
|
|
649
|
+
}
|
|
650
|
+
options = ClaudeAgentOptions(**opts_kwargs)
|
|
651
|
+
|
|
652
|
+
# Presence: mark "start" only after options is built so the
|
|
653
|
+
# board doesn't show a session that never actually launched
|
|
654
|
+
# (e.g. ClaudeAgentOptions raised). The matching "end" emits
|
|
655
|
+
# in the finally block below.
|
|
656
|
+
_presence_write(project_root, "claude", sub_session_id, "start")
|
|
657
|
+
_emit_dispatch_trace(
|
|
658
|
+
sub_session_id,
|
|
659
|
+
"dispatch_started",
|
|
660
|
+
request.formula_id,
|
|
661
|
+
{"formula_id": request.formula_id},
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
dispatch_turn_seq = 0
|
|
665
|
+
|
|
666
|
+
async def _run() -> None:
|
|
667
|
+
nonlocal result_subtype, structured_output, dispatch_turn_seq
|
|
668
|
+
async for msg in query(prompt=user_prompt, options=options):
|
|
669
|
+
if isinstance(msg, UserMessage):
|
|
670
|
+
# Capture checkpoint UUIDs for file-checkpointing replay (T9.2)
|
|
671
|
+
try:
|
|
672
|
+
uuid_val = getattr(msg, "uuid", None)
|
|
673
|
+
if uuid_val is not None:
|
|
674
|
+
checkpoint_uuids.append(str(uuid_val))
|
|
675
|
+
except Exception as exc:
|
|
676
|
+
logger.debug("UUID capture failed: %s", exc)
|
|
677
|
+
elif isinstance(msg, AssistantMessage):
|
|
678
|
+
_presence_write(project_root, "claude", sub_session_id, "tool")
|
|
679
|
+
_turn_texts: list[str] = []
|
|
680
|
+
for block in msg.content:
|
|
681
|
+
if isinstance(block, TextBlock):
|
|
682
|
+
transcript_parts.append(block.text)
|
|
683
|
+
_turn_texts.append(block.text)
|
|
684
|
+
# Count + emit only text-bearing turns so `turns` tracks
|
|
685
|
+
# reply turns (tool-only assistant messages are captured
|
|
686
|
+
# separately in result_meta['tool_calls']); a pure tool
|
|
687
|
+
# turn otherwise inflated the count with a chars=0 event.
|
|
688
|
+
if _turn_texts:
|
|
689
|
+
dispatch_turn_seq += 1
|
|
690
|
+
_turn_data: dict[str, Any] = {
|
|
691
|
+
"seq": dispatch_turn_seq,
|
|
692
|
+
"chars": sum(len(t) for t in _turn_texts),
|
|
693
|
+
}
|
|
694
|
+
if _dispatch_trace_content_enabled():
|
|
695
|
+
_turn_data["text"] = "".join(_turn_texts)
|
|
696
|
+
_emit_dispatch_trace(
|
|
697
|
+
sub_session_id, "dispatch_turn", request.formula_id, _turn_data
|
|
698
|
+
)
|
|
699
|
+
elif isinstance(msg, ResultMessage):
|
|
700
|
+
# ResultMessage is emitted once per query, at the
|
|
701
|
+
# end. Capture every field the post-stream handler
|
|
702
|
+
# might need.
|
|
703
|
+
result_subtype = getattr(msg, "subtype", None)
|
|
704
|
+
structured_output = getattr(msg, "structured_output", None)
|
|
705
|
+
for attr in (
|
|
706
|
+
"total_cost_usd",
|
|
707
|
+
"duration_ms",
|
|
708
|
+
"duration_api_ms",
|
|
709
|
+
"num_turns",
|
|
710
|
+
"stop_reason",
|
|
711
|
+
"session_id",
|
|
712
|
+
):
|
|
713
|
+
val = getattr(msg, attr, None)
|
|
714
|
+
if val is not None:
|
|
715
|
+
result_meta[attr] = val
|
|
716
|
+
# usage / model_usage carry the per-step token
|
|
717
|
+
# breakdown (input, output, cache_read,
|
|
718
|
+
# cache_creation). Persist verbatim — caller
|
|
719
|
+
# decides whether to summarize.
|
|
720
|
+
for attr in ("usage", "model_usage", "permission_denials"):
|
|
721
|
+
val = getattr(msg, attr, None)
|
|
722
|
+
if val is not None:
|
|
723
|
+
result_meta[attr] = val
|
|
724
|
+
|
|
725
|
+
# Single-writer for the terminal presence event so a future
|
|
726
|
+
# side-effect (emit a stream event, decrement a gauge) can't
|
|
727
|
+
# double-fire. Happy path calls "stop" then finally emits "end";
|
|
728
|
+
# error/timeout paths skip straight to "end".
|
|
729
|
+
dispatch_outcome: DispatchResult | None = None
|
|
730
|
+
try:
|
|
731
|
+
try:
|
|
732
|
+
await asyncio.wait_for(_run(), timeout=request.timeout_s)
|
|
733
|
+
_presence_write(project_root, "claude", sub_session_id, "stop")
|
|
734
|
+
except asyncio.TimeoutError:
|
|
735
|
+
dispatch_outcome = DispatchResult(
|
|
736
|
+
formula_id=request.formula_id,
|
|
737
|
+
status="timeout",
|
|
738
|
+
error=f"timed out after {request.timeout_s}s",
|
|
739
|
+
dispatcher_name=self.name,
|
|
740
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
741
|
+
raw_transcript="\n".join(transcript_parts) or None,
|
|
742
|
+
)
|
|
743
|
+
except Exception as exc:
|
|
744
|
+
logger.exception("claude-sdk dispatch failed")
|
|
745
|
+
dispatch_outcome = DispatchResult(
|
|
746
|
+
formula_id=request.formula_id,
|
|
747
|
+
status="error",
|
|
748
|
+
error=f"{type(exc).__name__}: {exc}",
|
|
749
|
+
dispatcher_name=self.name,
|
|
750
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
751
|
+
raw_transcript="\n".join(transcript_parts) or None,
|
|
752
|
+
)
|
|
753
|
+
finally:
|
|
754
|
+
_presence_write(project_root, "claude", sub_session_id, "end")
|
|
755
|
+
# Derive the terminal status from what is known here: an early
|
|
756
|
+
# timeout/error sets dispatch_outcome; otherwise only the budget /
|
|
757
|
+
# max-turns subtypes become a status="error" DispatchResult below —
|
|
758
|
+
# error_max_structured_output_retries is recoverable (it falls
|
|
759
|
+
# through to regex extraction and can still return "ok"), so match
|
|
760
|
+
# those two exactly rather than any "error"-prefixed subtype.
|
|
761
|
+
if dispatch_outcome is not None:
|
|
762
|
+
_final_status = dispatch_outcome.status
|
|
763
|
+
elif result_subtype in ("error_max_budget_usd", "error_max_turns"):
|
|
764
|
+
_final_status = "error"
|
|
765
|
+
else:
|
|
766
|
+
_final_status = "ok"
|
|
767
|
+
_emit_dispatch_trace(
|
|
768
|
+
sub_session_id,
|
|
769
|
+
"dispatch_completed",
|
|
770
|
+
request.formula_id,
|
|
771
|
+
{"status": _final_status, "turns": dispatch_turn_seq},
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
# Programmatic-hook captures ride into result_meta so the
|
|
775
|
+
# `formula_dispatches` audit row records full tool history.
|
|
776
|
+
if tool_calls:
|
|
777
|
+
result_meta["tool_calls"] = tool_calls
|
|
778
|
+
if tool_failures:
|
|
779
|
+
result_meta["tool_failures"] = tool_failures
|
|
780
|
+
# T9.2: file-checkpointing replay UUIDs (only populated when
|
|
781
|
+
# enable_file_checkpointing is set in role frontmatter).
|
|
782
|
+
if checkpoint_uuids:
|
|
783
|
+
result_meta["checkpoints"] = checkpoint_uuids
|
|
784
|
+
|
|
785
|
+
if dispatch_outcome is not None:
|
|
786
|
+
return dispatch_outcome
|
|
787
|
+
|
|
788
|
+
transcript = "\n".join(transcript_parts)
|
|
789
|
+
latency_ms = int((time.monotonic() - t0) * 1000)
|
|
790
|
+
|
|
791
|
+
# Map SDK error subtypes to dispatcher status. Budget exhaustion
|
|
792
|
+
# and retry exhaustion are operationally distinct from a generic
|
|
793
|
+
# "error_during_execution" — keep the original subtype in the
|
|
794
|
+
# error string so callers can pattern-match.
|
|
795
|
+
if result_subtype == "error_max_budget_usd":
|
|
796
|
+
return DispatchResult(
|
|
797
|
+
formula_id=request.formula_id,
|
|
798
|
+
status="error",
|
|
799
|
+
error=(
|
|
800
|
+
f"max_budget_usd={request.max_budget_usd} exhausted; "
|
|
801
|
+
f"actual_cost_usd={result_meta.get('total_cost_usd')!r}"
|
|
802
|
+
),
|
|
803
|
+
output_json={"_meta": dict(result_meta)},
|
|
804
|
+
latency_ms=latency_ms,
|
|
805
|
+
dispatcher_name=self.name,
|
|
806
|
+
raw_transcript=transcript or None,
|
|
807
|
+
)
|
|
808
|
+
if result_subtype == "error_max_turns":
|
|
809
|
+
return DispatchResult(
|
|
810
|
+
formula_id=request.formula_id,
|
|
811
|
+
status="error",
|
|
812
|
+
error="max_turns exhausted",
|
|
813
|
+
output_json={"_meta": dict(result_meta)},
|
|
814
|
+
latency_ms=latency_ms,
|
|
815
|
+
dispatcher_name=self.name,
|
|
816
|
+
raw_transcript=transcript or None,
|
|
817
|
+
)
|
|
818
|
+
if result_subtype == "error_max_structured_output_retries":
|
|
819
|
+
# Schema enforcement gave up; fall through to regex extraction
|
|
820
|
+
# so the dispatcher still surfaces partial work instead of an
|
|
821
|
+
# opaque error. Caller sees the subtype via raw_transcript +
|
|
822
|
+
# output_json._meta.subtype.
|
|
823
|
+
result_meta["structured_output_retry_exhausted"] = True
|
|
824
|
+
|
|
825
|
+
# Prefer SDK-enforced structured output. extract_json_block is the
|
|
826
|
+
# 0.1.x fallback for roles that don't opt into structured output
|
|
827
|
+
# AND for retry-exhausted runs (logged above).
|
|
828
|
+
output_json: dict[str, Any]
|
|
829
|
+
if isinstance(structured_output, dict):
|
|
830
|
+
output_json = dict(structured_output)
|
|
831
|
+
else:
|
|
832
|
+
output_json = extract_json_block(transcript)
|
|
833
|
+
|
|
834
|
+
if result_meta:
|
|
835
|
+
output_json.setdefault("_meta", {}).update(result_meta)
|
|
836
|
+
if result_subtype:
|
|
837
|
+
output_json.setdefault("_meta", {})["subtype"] = result_subtype
|
|
838
|
+
|
|
839
|
+
ok = bool(output_json) and any(k != "_meta" for k in output_json.keys())
|
|
840
|
+
# T1.5: surface the retry-exhausted subtype in the error field so callers
|
|
841
|
+
# can route to a retry-with-relaxed-prompt path. Status stays "ok" when
|
|
842
|
+
# regex fallback recovered usable JSON — the output bundle is still
|
|
843
|
+
# populated. Callers that need strict schema compliance should check error.
|
|
844
|
+
if not ok:
|
|
845
|
+
error_str = "no usable JSON in dispatch output"
|
|
846
|
+
elif result_subtype == "error_max_structured_output_retries":
|
|
847
|
+
error_str = (
|
|
848
|
+
"error_max_structured_output_retries: schema enforcement exhausted, "
|
|
849
|
+
"fell back to regex extraction"
|
|
850
|
+
)
|
|
851
|
+
else:
|
|
852
|
+
error_str = None
|
|
853
|
+
return DispatchResult(
|
|
854
|
+
formula_id=request.formula_id,
|
|
855
|
+
status="ok" if ok else "error",
|
|
856
|
+
output_json=output_json,
|
|
857
|
+
latency_ms=latency_ms,
|
|
858
|
+
dispatcher_name=self.name,
|
|
859
|
+
error=error_str,
|
|
860
|
+
raw_transcript=transcript,
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
# ---------------------------------------------------------------------------
|
|
865
|
+
# Factory — imported by core/thinking_os/dispatcher.py via importlib
|
|
866
|
+
# ---------------------------------------------------------------------------
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def build_dispatcher() -> ClaudeSDKDispatcher:
|
|
870
|
+
"""Entry point the factory looks for when loading this module."""
|
|
871
|
+
return ClaudeSDKDispatcher()
|