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.
Files changed (1304) hide show
  1. adapters/claude/README.md +6 -0
  2. adapters/claude/_install_helpers/extract_stacks.py +43 -0
  3. adapters/claude/_install_helpers/update_mcp_json.py +75 -0
  4. adapters/claude/adapter.yaml +164 -0
  5. adapters/claude/hooks/README.md +40 -0
  6. adapters/claude/hooks/agent_memory_sync.py +131 -0
  7. adapters/claude/hooks/ensure-agent-memory-link.sh +36 -0
  8. adapters/claude/hooks/sync-agent-memory.sh +21 -0
  9. adapters/claude/install.sh +86 -0
  10. adapters/claude/sdk_dispatcher.py +871 -0
  11. adapters/claude/settings.local.template.json +31 -0
  12. adapters/claude/settings.template.json +808 -0
  13. adapters/claude/update_mcp_json.py +85 -0
  14. adapters/codex/adapter.yaml +254 -0
  15. adapters/codex/chat_provider.py +230 -0
  16. adapters/codex/commands/formula-f1.md +129 -0
  17. adapters/codex/commands/formula-f10.md +100 -0
  18. adapters/codex/commands/formula-f11.md +123 -0
  19. adapters/codex/commands/formula-f2.md +139 -0
  20. adapters/codex/commands/formula-f3.md +127 -0
  21. adapters/codex/commands/formula-f4.md +101 -0
  22. adapters/codex/commands/formula-f5.md +135 -0
  23. adapters/codex/commands/formula-f6.md +147 -0
  24. adapters/codex/commands/formula-f7.md +111 -0
  25. adapters/codex/commands/formula-f8.md +133 -0
  26. adapters/codex/commands/formula-f9.md +112 -0
  27. adapters/codex/enable_codex_hooks.py +94 -0
  28. adapters/codex/ensure_codex_mcp.py +124 -0
  29. adapters/codex/hooks/codex-merge-hook-output.py +72 -0
  30. adapters/codex/hooks/codex-normalize-edit.py +96 -0
  31. adapters/codex/hooks/codex-postedit-dispatch.sh +75 -0
  32. adapters/codex/hooks/codex-posttool-dispatch.sh +70 -0
  33. adapters/codex/hooks/codex-preedit-dispatch.sh +83 -0
  34. adapters/codex/hooks/codex-pretool-dispatch.sh +82 -0
  35. adapters/codex/hooks/codex-sessionend-dispatch.sh +20 -0
  36. adapters/codex/hooks/codex-sessionstart-dispatch.sh +68 -0
  37. adapters/codex/hooks/codex-stop-dispatch.sh +73 -0
  38. adapters/codex/hooks/codex-userpromptsubmit-dispatch.sh +74 -0
  39. adapters/codex/hooks.template.json +208 -0
  40. adapters/codex/install.sh +83 -0
  41. adapters/codex/sdk_dispatcher.py +449 -0
  42. board_os/__init__.py +39 -0
  43. board_os/_agent_runtime.py +256 -0
  44. board_os/config.py +421 -0
  45. board_os/git_coherence.py +107 -0
  46. board_os/hub_adapter_manifest.py +140 -0
  47. board_os/mcp_tools.py +3228 -0
  48. board_os/migration.py +166 -0
  49. board_os/parser.py +317 -0
  50. board_os/presence.py +156 -0
  51. board_os/sync.py +320 -0
  52. board_os/transition_gates.py +224 -0
  53. board_os/transition_gates_cli.py +272 -0
  54. board_os/transition_gates_validator.py +551 -0
  55. board_os/verify_suites.py +126 -0
  56. board_os/verify_suites_cli.py +328 -0
  57. board_os/workflow.py +967 -0
  58. cli/__init__.py +0 -0
  59. cli/_data_types.py +248 -0
  60. cli/_init_helpers.py +587 -0
  61. cli/_resources.py +100 -0
  62. cli/adapter_registry.py +239 -0
  63. cli/add_stack.py +315 -0
  64. cli/aggregator.py +438 -0
  65. cli/board_commands.py +1218 -0
  66. cli/brain_commands.py +255 -0
  67. cli/cognition.py +345 -0
  68. cli/config_composer.py +349 -0
  69. cli/core_version.py +41 -0
  70. cli/cron_commands.py +278 -0
  71. cli/db_reset.py +298 -0
  72. cli/doc_commands.py +111 -0
  73. cli/doctor.py +2953 -0
  74. cli/doctor_board.py +365 -0
  75. cli/doctor_extras.py +1121 -0
  76. cli/doctor_graph.py +608 -0
  77. cli/doctor_tokens.py +254 -0
  78. cli/graph_commands.py +1265 -0
  79. cli/hook_renderer.py +393 -0
  80. cli/hub_commands.py +580 -0
  81. cli/list_adapters.py +79 -0
  82. cli/list_stacks.py +105 -0
  83. cli/logs_commands.py +89 -0
  84. cli/main.py +3070 -0
  85. cli/materialize_file.py +65 -0
  86. cli/mcp_start.py +153 -0
  87. cli/module_commands.py +513 -0
  88. cli/pr_commands.py +2024 -0
  89. cli/preset_commands.py +126 -0
  90. cli/preset_registry.py +171 -0
  91. cli/project_overrides.py +119 -0
  92. cli/registry.py +365 -0
  93. cli/remove_stack.py +492 -0
  94. cli/renderer.py +620 -0
  95. cli/setup.py +464 -0
  96. cli/skill_commands.py +689 -0
  97. cli/skill_registry.py +235 -0
  98. cli/skills_list.py +332 -0
  99. cli/stack_lint.py +351 -0
  100. cli/stack_registry.py +688 -0
  101. cli/subsystems.py +335 -0
  102. cli/sync_all.py +310 -0
  103. cli/tail_command.py +410 -0
  104. cli/update.py +608 -0
  105. cli/verify_since_edit.py +439 -0
  106. coding_os-0.3.2.dist-info/METADATA +508 -0
  107. coding_os-0.3.2.dist-info/RECORD +1304 -0
  108. coding_os-0.3.2.dist-info/WHEEL +5 -0
  109. coding_os-0.3.2.dist-info/entry_points.txt +5 -0
  110. coding_os-0.3.2.dist-info/licenses/LICENSE +201 -0
  111. coding_os-0.3.2.dist-info/top_level.txt +10 -0
  112. core/__init__.py +0 -0
  113. core/board_os/__init__.py +39 -0
  114. core/board_os/_agent_runtime.py +256 -0
  115. core/board_os/config.py +421 -0
  116. core/board_os/git_coherence.py +107 -0
  117. core/board_os/hub_adapter_manifest.py +140 -0
  118. core/board_os/mcp_tools.py +3228 -0
  119. core/board_os/migration.py +166 -0
  120. core/board_os/parser.py +317 -0
  121. core/board_os/presence.py +156 -0
  122. core/board_os/sync.py +320 -0
  123. core/board_os/transition-gates.yaml +176 -0
  124. core/board_os/transition_gates.py +224 -0
  125. core/board_os/transition_gates_cli.py +272 -0
  126. core/board_os/transition_gates_validator.py +551 -0
  127. core/board_os/verify-suites.yaml +113 -0
  128. core/board_os/verify_suites.py +126 -0
  129. core/board_os/verify_suites_cli.py +328 -0
  130. core/board_os/workflow.py +967 -0
  131. core/commands/board.md +27 -0
  132. core/commands/classify.md +23 -0
  133. core/commands/compose.md +23 -0
  134. core/commands/daily.md +31 -0
  135. core/commands/diagnose.md +7 -0
  136. core/commands/memory-search.md +23 -0
  137. core/commands/new-project.md +33 -0
  138. core/commands/retro.md +38 -0
  139. core/commands/review.md +14 -0
  140. core/commands/task.md +17 -0
  141. core/commands/verify.md +34 -0
  142. core/docs/thinking_os-final-edition.md +1449 -0
  143. core/doctor-config.yaml +74 -0
  144. core/graph_os/__init__.py +29 -0
  145. core/graph_os/backend.py +233 -0
  146. core/graph_os/backends/__init__.py +13 -0
  147. core/graph_os/backends/sqlite_backend.py +1053 -0
  148. core/graph_os/bench/__init__.py +17 -0
  149. core/graph_os/bench/fixtures.py +56 -0
  150. core/graph_os/bench/harness.py +95 -0
  151. core/graph_os/bench/persian_precision.py +142 -0
  152. core/graph_os/bench/scale_500k.py +120 -0
  153. core/graph_os/bench/token_cost.py +170 -0
  154. core/graph_os/bench/viewer_fps.py +110 -0
  155. core/graph_os/communities.py +410 -0
  156. core/graph_os/enterprise.py +218 -0
  157. core/graph_os/entry_points.py +226 -0
  158. core/graph_os/extractors/__init__.py +25 -0
  159. core/graph_os/extractors/code_generic.py +914 -0
  160. core/graph_os/extractors/code_go.py +1422 -0
  161. core/graph_os/extractors/code_json.py +340 -0
  162. core/graph_os/extractors/code_php.py +979 -0
  163. core/graph_os/extractors/code_python.py +1454 -0
  164. core/graph_os/extractors/code_shell.py +538 -0
  165. core/graph_os/extractors/code_toml.py +302 -0
  166. core/graph_os/extractors/code_ts.py +1665 -0
  167. core/graph_os/extractors/code_yaml.py +394 -0
  168. core/graph_os/extractors/contracts.py +1592 -0
  169. core/graph_os/extractors/md_links.py +890 -0
  170. core/graph_os/extractors/task_deps.py +345 -0
  171. core/graph_os/groups/__init__.py +22 -0
  172. core/graph_os/groups/cross_repo.py +156 -0
  173. core/graph_os/groups/manifest.py +141 -0
  174. core/graph_os/ingest/__init__.py +19 -0
  175. core/graph_os/ingest/base.py +306 -0
  176. core/graph_os/ingest/github.py +112 -0
  177. core/graph_os/ingest/zip.py +95 -0
  178. core/graph_os/toolchain.py +393 -0
  179. core/graph_os/tools/__init__.py +9 -0
  180. core/graph_os/tools/graph.py +5573 -0
  181. core/graph_os/tools/reindex_dispatch.py +730 -0
  182. core/graph_os/tree_sitter_overlay.py +235 -0
  183. core/graph_os/types.py +252 -0
  184. core/graph_os/vec_index.py +277 -0
  185. core/graph_os/viewer/__init__.py +12 -0
  186. core/graph_os/viewer/exporter.py +93 -0
  187. core/graph_os/viewer/template.py +189 -0
  188. core/hooks/_helpers/_paths.py +40 -0
  189. core/hooks/_helpers/advance_role.py +72 -0
  190. core/hooks/_helpers/auto_compose.py +228 -0
  191. core/hooks/_helpers/auto_validate_lessons.py +55 -0
  192. core/hooks/_helpers/branch_guard_check.py +796 -0
  193. core/hooks/_helpers/check_commit_message.py +108 -0
  194. core/hooks/_helpers/check_dangerous_rm.py +80 -0
  195. core/hooks/_helpers/check_git_bypass.py +154 -0
  196. core/hooks/_helpers/check_git_destructive.py +77 -0
  197. core/hooks/_helpers/check_settings_write.py +97 -0
  198. core/hooks/_helpers/consume_override.py +51 -0
  199. core/hooks/_helpers/context_budget.py +77 -0
  200. core/hooks/_helpers/cos_say_json.py +103 -0
  201. core/hooks/_helpers/destructive_edit_check.py +163 -0
  202. core/hooks/_helpers/detect_status_transition.py +82 -0
  203. core/hooks/_helpers/digest_regen.py +56 -0
  204. core/hooks/_helpers/doc_sync_check.py +498 -0
  205. core/hooks/_helpers/drain_embedding_outbox.py +52 -0
  206. core/hooks/_helpers/extract_additional_context.py +51 -0
  207. core/hooks/_helpers/extract_commit_msg_arg.py +74 -0
  208. core/hooks/_helpers/git_command_parse.py +424 -0
  209. core/hooks/_helpers/git_settings_fields.py +47 -0
  210. core/hooks/_helpers/graph_context_match.py +37 -0
  211. core/hooks/_helpers/graph_marker_check.py +70 -0
  212. core/hooks/_helpers/jit_recall.py +56 -0
  213. core/hooks/_helpers/json_field.py +41 -0
  214. core/hooks/_helpers/narrative_signal.py +59 -0
  215. core/hooks/_helpers/observation_count.py +31 -0
  216. core/hooks/_helpers/pre_commit_batch.py +177 -0
  217. core/hooks/_helpers/pre_commit_fake_input.py +42 -0
  218. core/hooks/_helpers/presence_gc.py +102 -0
  219. core/hooks/_helpers/presence_write.py +167 -0
  220. core/hooks/_helpers/recover_indirect.py +35 -0
  221. core/hooks/_helpers/routing_evolution.py +104 -0
  222. core/hooks/_helpers/session_recap.py +72 -0
  223. core/hooks/_helpers/skill_primer.py +229 -0
  224. core/hooks/_helpers/task_sync.py +59 -0
  225. core/hooks/_helpers/tool_failure_capture.py +147 -0
  226. core/hooks/_helpers/trajectory_autosnap.py +278 -0
  227. core/hooks/_helpers/trajectory_startup.py +62 -0
  228. core/hooks/_helpers/turn_summary.py +82 -0
  229. core/hooks/_helpers/validate_task_frontmatter.py +98 -0
  230. core/hooks/_helpers/wip_limit_check.py +103 -0
  231. core/hooks/_helpers/wip_lines.py +53 -0
  232. core/hooks/_helpers/work_log_append.py +89 -0
  233. core/hooks/_helpers/wrap_dispatch_output.py +82 -0
  234. core/hooks/advance-role.sh +48 -0
  235. core/hooks/agent-presence.sh +179 -0
  236. core/hooks/auto-brain-decay.sh +184 -0
  237. core/hooks/auto-compose-roles.sh +83 -0
  238. core/hooks/auto-graph-reconcile-shell.sh +119 -0
  239. core/hooks/auto-regen-doc-index.sh +120 -0
  240. core/hooks/auto-reindex-docs.sh +130 -0
  241. core/hooks/auto-task-sync.sh +56 -0
  242. core/hooks/auto-trace-rotate.sh +88 -0
  243. core/hooks/block-bad-patterns.sh +212 -0
  244. core/hooks/block-dangerous-commands.sh +182 -0
  245. core/hooks/block-hardcoded-literals.sh +90 -0
  246. core/hooks/block-migration-conflict.sh +114 -0
  247. core/hooks/block-protected-files.sh +129 -0
  248. core/hooks/block-secrets.sh +185 -0
  249. core/hooks/block-shared-tree-edit.sh +75 -0
  250. core/hooks/block-uv-heredoc.sh +78 -0
  251. core/hooks/branch-guard.sh +122 -0
  252. core/hooks/capture-observation.sh +76 -0
  253. core/hooks/capture-tool-failure.sh +24 -0
  254. core/hooks/capture-work-log.sh +89 -0
  255. core/hooks/check-agents-md-refs.sh +75 -0
  256. core/hooks/check-agents-md-size.sh +49 -0
  257. core/hooks/check-capture-worked.sh +148 -0
  258. core/hooks/check-doc-size.sh +61 -0
  259. core/hooks/check-mcp-extras.sh +92 -0
  260. core/hooks/check-state.sh +87 -0
  261. core/hooks/classify-task-mode.sh +103 -0
  262. core/hooks/cos-env.sh +1301 -0
  263. core/hooks/drain-embedding-outbox.sh +24 -0
  264. core/hooks/enforce-anti-ambiguity.sh +74 -0
  265. core/hooks/enforce-commit-message.sh +73 -0
  266. core/hooks/enforce-doc-anchor.sh +224 -0
  267. core/hooks/enforce-doc-sync.sh +206 -0
  268. core/hooks/enforce-graph-context.sh +89 -0
  269. core/hooks/enforce-graph-first-read.sh +94 -0
  270. core/hooks/enforce-memory-check.sh +128 -0
  271. core/hooks/enforce-rename-plan.sh +78 -0
  272. core/hooks/enforce-scaffold-boundary.sh +68 -0
  273. core/hooks/enforce-skill.sh +125 -0
  274. core/hooks/enforce-task-body.sh +52 -0
  275. core/hooks/enforce-task-start.sh +81 -0
  276. core/hooks/enforce-task-transition.sh +75 -0
  277. core/hooks/enforce-template.sh +143 -0
  278. core/hooks/enforce-verify.sh +112 -0
  279. core/hooks/enforce-wip-limit.sh +43 -0
  280. core/hooks/enforce-zoom.sh +69 -0
  281. core/hooks/ensure-hub-up.sh +67 -0
  282. core/hooks/inject-mcp-caller-session.sh +70 -0
  283. core/hooks/jit-recall.sh +65 -0
  284. core/hooks/link-commit-to-task.sh +143 -0
  285. core/hooks/lint-task.sh +40 -0
  286. core/hooks/nudge-docs-first.sh +71 -0
  287. core/hooks/nudge-git-mode.sh +29 -0
  288. core/hooks/nudge-graph-os.sh +118 -0
  289. core/hooks/nudge-learn-narrative.sh +36 -0
  290. core/hooks/nudge-model-routing.sh +32 -0
  291. core/hooks/nudge-reentry.sh +101 -0
  292. core/hooks/nudge-reuse-first.sh +68 -0
  293. core/hooks/nudge-task-discovery.sh +81 -0
  294. core/hooks/nudge-thinking-os.sh +109 -0
  295. core/hooks/pr-reap.sh +23 -0
  296. core/hooks/reclaim-sweep.sh +58 -0
  297. core/hooks/record-verify-auto.sh +77 -0
  298. core/hooks/record-verify.sh +74 -0
  299. core/hooks/regen-reminder.sh +104 -0
  300. core/hooks/registry.yaml +1262 -0
  301. core/hooks/remind-daily.sh +27 -0
  302. core/hooks/remind-dogfood.sh +70 -0
  303. core/hooks/remind-learn-validate.sh +94 -0
  304. core/hooks/rules-primer.sh +50 -0
  305. core/hooks/search-enforce-inventory.sh +108 -0
  306. core/hooks/search-verify-remaining.sh +132 -0
  307. core/hooks/session-context.sh +729 -0
  308. core/hooks/session-end.sh +145 -0
  309. core/hooks/session-skill-primer.sh +43 -0
  310. core/hooks/snapshot-transcript.sh +56 -0
  311. core/hooks/sync-task-current.sh +85 -0
  312. core/hooks/test-first-reminder.sh +120 -0
  313. core/hooks/test-governor.sh +173 -0
  314. core/hooks/thinking_os-gate.sh +52 -0
  315. core/hooks/track-backtrack.sh +35 -0
  316. core/hooks/track-discovery.sh +121 -0
  317. core/hooks/track-skill.sh +53 -0
  318. core/hooks/validate-task-frontmatter.sh +49 -0
  319. core/hooks/verify-rename-callers.sh +119 -0
  320. core/hooks/warn-abandoned-task.sh +99 -0
  321. core/hooks/warn-destructive-edit.sh +64 -0
  322. core/hooks/warn-diff-size.sh +43 -0
  323. core/hooks/warn-graph-empty.sh +81 -0
  324. core/hooks/warn-mcp-down.sh +190 -0
  325. core/hooks/write-state.sh +55 -0
  326. core/logging_os/__init__.py +33 -0
  327. core/logging_os/api.py +127 -0
  328. core/logging_os/bridge.py +80 -0
  329. core/logging_os/config.py +172 -0
  330. core/logging_os/fingerprint.py +25 -0
  331. core/logging_os/redact.py +53 -0
  332. core/logging_os/render.py +83 -0
  333. core/logging_os/sinks.py +164 -0
  334. core/rules/anti-overengineering.md +44 -0
  335. core/rules/api-contract-discipline.md +41 -0
  336. core/rules/dimension-registry.md +155 -0
  337. core/rules/git-workflow.md +57 -0
  338. core/rules/memory.md +46 -0
  339. core/rules/model-routing.md +22 -0
  340. core/rules/skill-enforcement.md +75 -0
  341. core/rules/test-discipline.md +38 -0
  342. core/rules/thinking_os.md +48 -0
  343. core/rules/transparency-banner.md +37 -0
  344. core/runtime_paths.yaml +36 -0
  345. core/scaffold_manifest.json +14430 -0
  346. core/scheduled/__init__.py +0 -0
  347. core/scheduled/_activity.py +126 -0
  348. core/scheduled/_state.py +113 -0
  349. core/scheduled/config.py +86 -0
  350. core/scheduled/dep_reconcile.py +135 -0
  351. core/scheduled/error_sweep.py +137 -0
  352. core/scheduled/nightly.py +930 -0
  353. core/scheduled/responsive_extract.py +65 -0
  354. core/schemas/adapter.schema.json +269 -0
  355. core/schemas/preset.schema.json +50 -0
  356. core/schemas/skill.schema.json +81 -0
  357. core/schemas/stack.schema.json +404 -0
  358. core/scripts/_lib.sh +9 -0
  359. core/scripts/docs-lint.sh +228 -0
  360. core/scripts/docs-nav-fix.sh +133 -0
  361. core/scripts/docs-staleness-check.sh +154 -0
  362. core/scripts/install-adapter.sh +266 -0
  363. core/scripts/link-stack-skills.sh +52 -0
  364. core/scripts/log-latest.sh +106 -0
  365. core/scripts/log-search.sh +89 -0
  366. core/scripts/log-write.sh +134 -0
  367. core/scripts/ref-resolve.sh +71 -0
  368. core/skills/a11y/SKILL.md +305 -0
  369. core/skills/a11y/assets/a11y-checklist.md +137 -0
  370. core/skills/a11y/references/aria-and-focus.md +247 -0
  371. core/skills/a11y/references/rn-accessibility.md +343 -0
  372. core/skills/a11y/references/screen-reader-testing.md +190 -0
  373. core/skills/agent-memory/SKILL.md +191 -0
  374. core/skills/agent-memory/assets/memory-checklist.md +21 -0
  375. core/skills/agent-memory/references/memory-recipes.md +57 -0
  376. core/skills/api-design/SKILL.md +232 -0
  377. core/skills/api-design/assets/api-design-checklist.md +110 -0
  378. core/skills/api-design/references/error-envelope.md +381 -0
  379. core/skills/api-design/references/idempotency-pagination.md +312 -0
  380. core/skills/api-design/references/rest-contracts.md +426 -0
  381. core/skills/auth-patterns/SKILL.md +352 -0
  382. core/skills/auth-patterns/assets/auth-checklist.md +118 -0
  383. core/skills/auth-patterns/references/jwt-and-service-tokens.md +343 -0
  384. core/skills/auth-patterns/references/passkeys-2fa.md +289 -0
  385. core/skills/auth-patterns/references/sessions-vs-jwt.md +230 -0
  386. core/skills/auth-patterns/scripts/cookie-flag-check.py +146 -0
  387. core/skills/backend-fundamentals/SKILL.md +238 -0
  388. core/skills/backend-fundamentals/assets/backend-checklist.md +27 -0
  389. core/skills/backend-fundamentals/references/backend-patterns.md +56 -0
  390. core/skills/backend-fundamentals/scripts/check_layering.py +83 -0
  391. core/skills/clean-code/SKILL.md +642 -0
  392. core/skills/clean-code/scripts/audit-fail-closed.py +167 -0
  393. core/skills/codebase-explorer/SKILL.md +89 -0
  394. core/skills/codebase-explorer/assets/reading-checklist.md +24 -0
  395. core/skills/codebase-explorer/references/reading-strategies.md +52 -0
  396. core/skills/codebase-explorer/scripts/outline.py +99 -0
  397. core/skills/db-design/SKILL.md +327 -0
  398. core/skills/db-design/assets/migration-template.sql +49 -0
  399. core/skills/db-design/references/migration-discipline.md +290 -0
  400. core/skills/db-design/references/postgres-patterns.md +340 -0
  401. core/skills/db-design/scripts/migration-safety.sh +150 -0
  402. core/skills/deployment-cicd/SKILL.md +260 -0
  403. core/skills/deployment-cicd/assets/deploy-checklist.md +26 -0
  404. core/skills/deployment-cicd/references/pipeline-and-release.md +54 -0
  405. core/skills/deployment-cicd/scripts/lint_workflow.py +79 -0
  406. core/skills/docker/SKILL.md +114 -0
  407. core/skills/docker/assets/dockerfile-checklist.md +31 -0
  408. core/skills/docker/references/compose-patterns.md +66 -0
  409. core/skills/docker/references/dockerfile-optimization.md +64 -0
  410. core/skills/docker/scripts/lint_dockerfile.sh +48 -0
  411. core/skills/docker/versions.json +16 -0
  412. core/skills/end-to-end-testing/SKILL.md +101 -0
  413. core/skills/end-to-end-testing/assets/e2e-checklist.md +23 -0
  414. core/skills/end-to-end-testing/references/maestro.md +63 -0
  415. core/skills/end-to-end-testing/references/playwright.md +68 -0
  416. core/skills/end-to-end-testing/scripts/lint_e2e.py +88 -0
  417. core/skills/end-to-end-testing/versions.json +16 -0
  418. core/skills/frontend-design/SKILL.md +76 -0
  419. core/skills/frontend-design/assets/design-checklist.md +29 -0
  420. core/skills/frontend-design/references/design-principles.md +65 -0
  421. core/skills/frontend-design/scripts/check_contrast.py +89 -0
  422. core/skills/frontend-fundamentals/SKILL.md +213 -0
  423. core/skills/frontend-fundamentals/assets/frontend-checklist.md +25 -0
  424. core/skills/frontend-fundamentals/references/rendering-and-state.md +66 -0
  425. core/skills/frontend-fundamentals/scripts/check_frontend.py +86 -0
  426. core/skills/graph-explorer/SKILL.md +215 -0
  427. core/skills/graph-explorer/scripts/explain-impact.sh +64 -0
  428. core/skills/graphql/SKILL.md +187 -0
  429. core/skills/grpc-microservices/SKILL.md +174 -0
  430. core/skills/hexagonal-architecture/SKILL.md +199 -0
  431. core/skills/hexagonal-architecture/assets/folder-scaffold.md +233 -0
  432. core/skills/hexagonal-architecture/references/anti-patterns.md +129 -0
  433. core/skills/hexagonal-architecture/references/go-fiber-layout.md +429 -0
  434. core/skills/hexagonal-architecture/references/python-fastapi-layout.md +453 -0
  435. core/skills/hexagonal-architecture/references/react-native-layout.md +428 -0
  436. core/skills/i18n/SKILL.md +126 -0
  437. core/skills/incident-response/SKILL.md +225 -0
  438. core/skills/incident-response/assets/incident-checklist.md +29 -0
  439. core/skills/incident-response/references/severity-and-runbook.md +53 -0
  440. core/skills/incident-response/scripts/classify_severity.py +86 -0
  441. core/skills/linux-sysadmin/SKILL.md +115 -0
  442. core/skills/linux-sysadmin/assets/hardening-checklist.md +29 -0
  443. core/skills/linux-sysadmin/references/ssh-hardening.md +62 -0
  444. core/skills/linux-sysadmin/references/systemd-and-services.md +77 -0
  445. core/skills/linux-sysadmin/scripts/triage.sh +50 -0
  446. core/skills/linux-sysadmin/versions.json +17 -0
  447. core/skills/llm-patterns/SKILL.md +410 -0
  448. core/skills/llm-patterns/assets/llm-feature-checklist.md +26 -0
  449. core/skills/llm-patterns/references/rag-and-evals.md +59 -0
  450. core/skills/llm-patterns/scripts/estimate_tokens.py +75 -0
  451. core/skills/messaging-queues/SKILL.md +142 -0
  452. core/skills/mobile-fundamentals/SKILL.md +406 -0
  453. core/skills/mobile-fundamentals/assets/mobile-launch-checklist.md +130 -0
  454. core/skills/mobile-fundamentals/references/navigation-and-deep-links.md +337 -0
  455. core/skills/mobile-fundamentals/references/offline-sync.md +339 -0
  456. core/skills/node-backend/SKILL.md +114 -0
  457. core/skills/node-backend/assets/node-checklist.md +25 -0
  458. core/skills/node-backend/references/async-and-errors.md +67 -0
  459. core/skills/node-backend/references/event-loop.md +65 -0
  460. core/skills/node-backend/scripts/check_package.py +78 -0
  461. core/skills/node-backend/versions.json +17 -0
  462. core/skills/observability/SKILL.md +289 -0
  463. core/skills/observability/assets/observability-checklist.md +27 -0
  464. core/skills/observability/references/instrumentation.md +57 -0
  465. core/skills/observability/scripts/lint_logging.py +77 -0
  466. core/skills/payments/SKILL.md +102 -0
  467. core/skills/performance/SKILL.md +305 -0
  468. core/skills/performance/assets/perf-checklist.md +143 -0
  469. core/skills/performance/references/mobile-performance.md +249 -0
  470. core/skills/performance/references/web-vitals.md +209 -0
  471. core/skills/php/SKILL.md +116 -0
  472. core/skills/php/assets/php-checklist.md +26 -0
  473. core/skills/php/references/modern-php.md +64 -0
  474. core/skills/php/references/security.md +72 -0
  475. core/skills/php/scripts/scan_php_smells.py +99 -0
  476. core/skills/php/versions.json +9 -0
  477. core/skills/pr-mode-driver/SKILL.md +65 -0
  478. core/skills/realtime-websockets/SKILL.md +152 -0
  479. core/skills/redis/SKILL.md +105 -0
  480. core/skills/redis/assets/redis-checklist.md +27 -0
  481. core/skills/redis/references/operations.md +66 -0
  482. core/skills/redis/references/patterns.md +62 -0
  483. core/skills/redis/scripts/analyze_info.py +101 -0
  484. core/skills/redis/versions.json +9 -0
  485. core/skills/search/SKILL.md +91 -0
  486. core/skills/search/references/grep.md +76 -0
  487. core/skills/search/scripts/verify-count.sh +73 -0
  488. core/skills/search-infra/SKILL.md +109 -0
  489. core/skills/security-mobile/SKILL.md +394 -0
  490. core/skills/security-mobile/assets/mobile-security-checklist.md +117 -0
  491. core/skills/security-mobile/references/masvs-l1-checklist.md +127 -0
  492. core/skills/security-web/SKILL.md +217 -0
  493. core/skills/security-web/assets/security-web-checklist.md +167 -0
  494. core/skills/security-web/references/owasp-top-10.md +551 -0
  495. core/skills/security-web/references/supply-chain.md +179 -0
  496. core/skills/security-web/scripts/csp-check.sh +153 -0
  497. core/skills/shell-scripting/SKILL.md +128 -0
  498. core/skills/shell-scripting/assets/script-checklist.md +33 -0
  499. core/skills/shell-scripting/references/argument-parsing.md +84 -0
  500. core/skills/shell-scripting/references/bash-robustness.md +78 -0
  501. core/skills/shell-scripting/scripts/lint_script.sh +53 -0
  502. core/skills/shell-scripting/scripts/new_script.py +131 -0
  503. core/skills/shell-scripting/versions.json +23 -0
  504. core/skills/sql-authoring/SKILL.md +104 -0
  505. core/skills/sql-authoring/assets/query-review-checklist.md +26 -0
  506. core/skills/sql-authoring/references/query-patterns.md +89 -0
  507. core/skills/sql-authoring/references/reading-explain.md +52 -0
  508. core/skills/sql-authoring/scripts/analyze_plan.py +100 -0
  509. core/skills/sql-authoring/versions.json +17 -0
  510. core/skills/state-management/SKILL.md +428 -0
  511. core/skills/state-management/references/tanstack-query-recipes.md +301 -0
  512. core/skills/state-management/references/zustand-recipes.md +364 -0
  513. core/skills/supabase/SKILL.md +108 -0
  514. core/skills/supabase/assets/supabase-checklist.md +26 -0
  515. core/skills/supabase/references/realtime-and-storage.md +55 -0
  516. core/skills/supabase/references/rls-and-auth.md +65 -0
  517. core/skills/supabase/scripts/check_rls.py +88 -0
  518. core/skills/supabase/versions.json +9 -0
  519. core/skills/task-driver/SKILL.md +272 -0
  520. core/skills/task-driver/scripts/task-lint.sh +163 -0
  521. core/skills/technical-writing/SKILL.md +85 -0
  522. core/skills/technical-writing/assets/doc-checklist.md +29 -0
  523. core/skills/technical-writing/references/doc-anatomy.md +53 -0
  524. core/skills/technical-writing/references/writing-craft.md +59 -0
  525. core/skills/technical-writing/scripts/new_doc.py +81 -0
  526. core/skills/terraform-k8s/SKILL.md +133 -0
  527. core/skills/testing-strategy/SKILL.md +266 -0
  528. core/skills/testing-strategy/assets/test-review-checklist.md +25 -0
  529. core/skills/testing-strategy/references/test-types.md +52 -0
  530. core/skills/testing-strategy/scripts/coverage_gate.py +79 -0
  531. core/skills/thinking_os/SKILL.md +288 -0
  532. core/skills/thinking_os/scripts/classify.sh +122 -0
  533. core/skills/typescript/SKILL.md +110 -0
  534. core/skills/typescript/assets/typescript-checklist.md +24 -0
  535. core/skills/typescript/references/strictness.md +53 -0
  536. core/skills/typescript/references/type-system.md +86 -0
  537. core/skills/typescript/scripts/check_tsconfig.py +92 -0
  538. core/skills/typescript/versions.json +9 -0
  539. core/subsystems.yaml +202 -0
  540. core/thinking_os/__init__.py +1 -0
  541. core/thinking_os/_agent_markers.py +32 -0
  542. core/thinking_os/agents/README.md +71 -0
  543. core/thinking_os/agents/analyst.md +139 -0
  544. core/thinking_os/agents/architect.md +127 -0
  545. core/thinking_os/agents/debugger.md +111 -0
  546. core/thinking_os/agents/deployer.md +112 -0
  547. core/thinking_os/agents/distiller.md +28 -0
  548. core/thinking_os/agents/documenter.md +101 -0
  549. core/thinking_os/agents/implementer.md +135 -0
  550. core/thinking_os/agents/internal/session_observer.md +39 -0
  551. core/thinking_os/agents/observer.md +100 -0
  552. core/thinking_os/agents/onboarder.md +81 -0
  553. core/thinking_os/agents/refactorer.md +123 -0
  554. core/thinking_os/agents/repairer.md +47 -0
  555. core/thinking_os/agents/researcher.md +129 -0
  556. core/thinking_os/agents/reviewer.md +147 -0
  557. core/thinking_os/agents/security_auditor.md +133 -0
  558. core/thinking_os/background.py +405 -0
  559. core/thinking_os/bootstrap_outcomes.py +200 -0
  560. core/thinking_os/budget.py +302 -0
  561. core/thinking_os/capture.py +495 -0
  562. core/thinking_os/cognition.py +516 -0
  563. core/thinking_os/cognition_schemas.py +517 -0
  564. core/thinking_os/compress.py +192 -0
  565. core/thinking_os/concepts.py +233 -0
  566. core/thinking_os/dashboard.py +159 -0
  567. core/thinking_os/database.py +2883 -0
  568. core/thinking_os/decay.py +393 -0
  569. core/thinking_os/digest.py +295 -0
  570. core/thinking_os/dispatcher.py +192 -0
  571. core/thinking_os/dispatcher_helpers.py +48 -0
  572. core/thinking_os/dispatchers/__init__.py +3 -0
  573. core/thinking_os/dispatchers/default.py +47 -0
  574. core/thinking_os/distill.py +192 -0
  575. core/thinking_os/doc_indexer.py +905 -0
  576. core/thinking_os/embeddings.py +943 -0
  577. core/thinking_os/formula_composer.py +556 -0
  578. core/thinking_os/gate_marker.py +75 -0
  579. core/thinking_os/graph.py +296 -0
  580. core/thinking_os/graph_indexer.py +360 -0
  581. core/thinking_os/health_check.py +517 -0
  582. core/thinking_os/impact.py +119 -0
  583. core/thinking_os/memory_gc.py +356 -0
  584. core/thinking_os/migrator_embeddings.py +318 -0
  585. core/thinking_os/precision.py +194 -0
  586. core/thinking_os/presets/registry.yaml +127 -0
  587. core/thinking_os/record_outcome.py +399 -0
  588. core/thinking_os/repair.py +105 -0
  589. core/thinking_os/retrieval_quality.py +239 -0
  590. core/thinking_os/roles/analyst.yaml +83 -0
  591. core/thinking_os/roles/architect.yaml +88 -0
  592. core/thinking_os/roles/debugger.yaml +71 -0
  593. core/thinking_os/roles/deployer.yaml +71 -0
  594. core/thinking_os/roles/documenter.yaml +72 -0
  595. core/thinking_os/roles/implementer.yaml +83 -0
  596. core/thinking_os/roles/observer.yaml +70 -0
  597. core/thinking_os/roles/refactorer.yaml +71 -0
  598. core/thinking_os/roles/researcher.yaml +72 -0
  599. core/thinking_os/roles/reviewer.yaml +79 -0
  600. core/thinking_os/roles/security_auditor.yaml +83 -0
  601. core/thinking_os/roles_state.py +168 -0
  602. core/thinking_os/sanitizer.py +320 -0
  603. core/thinking_os/server.py +3160 -0
  604. core/thinking_os/session_enrich.py +272 -0
  605. core/thinking_os/session_observe_worker.py +111 -0
  606. core/thinking_os/session_startup.py +74 -0
  607. core/thinking_os/session_summary.py +245 -0
  608. core/thinking_os/situations/registry.yaml +99 -0
  609. core/thinking_os/task_analyzer.py +462 -0
  610. core/thinking_os/task_parser.py +342 -0
  611. core/thinking_os/task_sync.py +73 -0
  612. core/thinking_os/tools/__init__.py +6 -0
  613. core/thinking_os/tools/_shared.py +947 -0
  614. core/thinking_os/tools/cognition.py +1867 -0
  615. core/thinking_os/tools/docs.py +770 -0
  616. core/thinking_os/tools/learning.py +2078 -0
  617. core/thinking_os/tools/logs.py +79 -0
  618. core/thinking_os/tools/memory.py +840 -0
  619. core/thinking_os/tools/metrics.py +200 -0
  620. core/thinking_os/tools/retrieve.py +415 -0
  621. core/thinking_os/tools/routing.py +658 -0
  622. core/thinking_os/tools/tasks.py +449 -0
  623. core/thinking_os/tools/trajectory.py +181 -0
  624. core/thinking_os/tracing.py +235 -0
  625. core/web/__init__.py +5 -0
  626. core/web/_cache.py +118 -0
  627. core/web/_deps.py +56 -0
  628. core/web/_envelope.py +85 -0
  629. core/web/_project_context.py +140 -0
  630. core/web/chat_providers.py +108 -0
  631. core/web/init_jobs.py +216 -0
  632. core/web/routes/__init__.py +25 -0
  633. core/web/routes/_bounded_read.py +76 -0
  634. core/web/routes/board.py +1089 -0
  635. core/web/routes/cognition.py +1838 -0
  636. core/web/routes/config.py +635 -0
  637. core/web/routes/graph.py +513 -0
  638. core/web/routes/health.py +180 -0
  639. core/web/routes/hooks.py +288 -0
  640. core/web/routes/hub.py +1219 -0
  641. core/web/routes/logs.py +374 -0
  642. core/web/routes/metrics.py +43 -0
  643. core/web/routes/observability.py +400 -0
  644. core/web/routes/patterns.py +227 -0
  645. core/web/routes/presence.py +609 -0
  646. core/web/routes/roles.py +446 -0
  647. core/web/routes/scheduled.py +261 -0
  648. core/web/routes/search.py +238 -0
  649. core/web/routes/sessions.py +220 -0
  650. core/web/routes/settings.py +363 -0
  651. core/web/routes/stream.py +547 -0
  652. core/web/security.py +157 -0
  653. core/web/server.py +283 -0
  654. graph_os/__init__.py +29 -0
  655. graph_os/backend.py +233 -0
  656. graph_os/backends/__init__.py +13 -0
  657. graph_os/backends/sqlite_backend.py +1053 -0
  658. graph_os/communities.py +410 -0
  659. graph_os/enterprise.py +218 -0
  660. graph_os/entry_points.py +226 -0
  661. graph_os/extractors/__init__.py +25 -0
  662. graph_os/extractors/code_generic.py +914 -0
  663. graph_os/extractors/code_go.py +1422 -0
  664. graph_os/extractors/code_json.py +340 -0
  665. graph_os/extractors/code_php.py +979 -0
  666. graph_os/extractors/code_python.py +1454 -0
  667. graph_os/extractors/code_shell.py +538 -0
  668. graph_os/extractors/code_toml.py +302 -0
  669. graph_os/extractors/code_ts.py +1665 -0
  670. graph_os/extractors/code_yaml.py +394 -0
  671. graph_os/extractors/contracts.py +1592 -0
  672. graph_os/extractors/md_links.py +890 -0
  673. graph_os/extractors/task_deps.py +345 -0
  674. graph_os/groups/__init__.py +22 -0
  675. graph_os/groups/cross_repo.py +156 -0
  676. graph_os/groups/manifest.py +141 -0
  677. graph_os/ingest/__init__.py +19 -0
  678. graph_os/ingest/base.py +306 -0
  679. graph_os/ingest/github.py +112 -0
  680. graph_os/ingest/zip.py +95 -0
  681. graph_os/toolchain.py +393 -0
  682. graph_os/tools/__init__.py +9 -0
  683. graph_os/tools/graph.py +5573 -0
  684. graph_os/tools/reindex_dispatch.py +730 -0
  685. graph_os/tree_sitter_overlay.py +235 -0
  686. graph_os/types.py +252 -0
  687. graph_os/vec_index.py +277 -0
  688. graph_os/viewer/__init__.py +12 -0
  689. graph_os/viewer/exporter.py +93 -0
  690. graph_os/viewer/template.py +189 -0
  691. scheduled/__init__.py +0 -0
  692. scheduled/_activity.py +126 -0
  693. scheduled/_state.py +113 -0
  694. scheduled/config.py +86 -0
  695. scheduled/dep_reconcile.py +135 -0
  696. scheduled/error_sweep.py +137 -0
  697. scheduled/nightly.py +930 -0
  698. scheduled/responsive_extract.py +65 -0
  699. scripts/__init__.py +4 -0
  700. scripts/_commit_msg_body.sh +31 -0
  701. scripts/_post_commit_body.sh +49 -0
  702. scripts/_pre_commit_body.sh +121 -0
  703. scripts/_prepare_commit_msg_body.sh +53 -0
  704. scripts/audit_mcp_tools.py +693 -0
  705. scripts/bench_sdk_dispatcher.py +177 -0
  706. scripts/capture_golden.py +169 -0
  707. scripts/check_graph_phantoms.py +75 -0
  708. scripts/dev/audit_doc_links.py +359 -0
  709. scripts/dev/audit_scaffold_module_tags.py +107 -0
  710. scripts/dev/backfill_doc_headers.py +328 -0
  711. scripts/dev/backfill_nav_lines.py +119 -0
  712. scripts/dev/fix_nav_placement.py +106 -0
  713. scripts/dev/inspect_sdk_options.py +45 -0
  714. scripts/dev/migrate_check_ids.py +170 -0
  715. scripts/dev/strip_purpose_blocks.py +154 -0
  716. scripts/dump_openapi.py +66 -0
  717. scripts/e2e_dispatch_tool.py +195 -0
  718. scripts/generate_manifest.py +166 -0
  719. scripts/golden_sections.py +20 -0
  720. scripts/graph_demo.py +161 -0
  721. scripts/install-git-hooks.sh +47 -0
  722. scripts/migrate_embeddings_minilm_to_bge_m3.py +84 -0
  723. scripts/operational_eval.py +445 -0
  724. scripts/probe_agent_session_resolver.py +59 -0
  725. scripts/prune_deleted_path.py +127 -0
  726. scripts/refactor_agent_dual_mode.py +171 -0
  727. scripts/refresh_skill_versions.py +302 -0
  728. scripts/regen_doc_index.py +209 -0
  729. scripts/regen_doctor_schema.py +62 -0
  730. scripts/regen_rules.py +94 -0
  731. scripts/rename_formulas_to_semantic.py +241 -0
  732. scripts/smoke_db_connections.py +183 -0
  733. scripts/smoke_doc_header.py +72 -0
  734. scripts/smoke_graph_e2e.py +374 -0
  735. scripts/smoke_sdk_dispatch.py +84 -0
  736. scripts/smoke_uid_resolver.py +164 -0
  737. scripts/verify_dispatchers.py +244 -0
  738. scripts/verify_phase_c_e2e.py +436 -0
  739. templates/__init__.py +6 -0
  740. templates/_base/Makefile.base +353 -0
  741. templates/_base/base.yaml +59 -0
  742. templates/_base/coding-os.yaml.template +39 -0
  743. templates/_base/dimension-registry.template.md +68 -0
  744. templates/_base/domain-config.template.json +46 -0
  745. templates/_base/fragments/anatomy-map.md.tmpl +11 -0
  746. templates/_base/fragments/context-discipline.md.tmpl +3 -0
  747. templates/_base/fragments/core-loop.md.tmpl +52 -0
  748. templates/_base/fragments/engineering-routing.md.tmpl +3 -0
  749. templates/_base/fragments/header.md.tmpl +6 -0
  750. templates/_base/fragments/identity.md.tmpl +3 -0
  751. templates/_base/fragments/principles.md.tmpl +3 -0
  752. templates/_base/fragments/retrieval-routing.md.tmpl +23 -0
  753. templates/_base/fragments/session-handoff.md.tmpl +3 -0
  754. templates/_base/fragments/skills.md.tmpl +3 -0
  755. templates/_base/fragments/ssot-map.md.tmpl +3 -0
  756. templates/_base/fragments/stop-conditions.md.tmpl +3 -0
  757. templates/_base/fragments/subagent-dispatch.md.tmpl +3 -0
  758. templates/_base/fragments/task-authoring.md.tmpl +69 -0
  759. templates/_base/fragments/task-logging.md.tmpl +8 -0
  760. templates/_base/fragments/tool-routing.md.tmpl +9 -0
  761. templates/_base/fragments/verification-matrix.md.tmpl +12 -0
  762. templates/_base/lang/dart/analysis_options.yaml +7 -0
  763. templates/_base/lang/php/phpcs.xml.dist +10 -0
  764. templates/_base/lang/python/pyproject.toml +19 -0
  765. templates/_base/lang/rust/clippy.toml +5 -0
  766. templates/_base/lang/rust/rustfmt.toml +3 -0
  767. templates/_base/lang/typescript/eslint.config.js +26 -0
  768. templates/_base/lang/typescript/tsconfig.json +15 -0
  769. templates/_base/lang/typescript/vitest.config.ts +10 -0
  770. templates/_base/scaffold/changes.log +1 -0
  771. templates/_base/scaffold/docs/00-index.md +55 -0
  772. templates/_base/scaffold/docs/_meta/feature-dependency-tree.md +30 -0
  773. templates/_base/scaffold/docs/_meta/foundation-map.md +56 -0
  774. templates/_base/scaffold/docs/_meta/questions.md +8 -0
  775. templates/_base/scaffold/docs/_meta/roadmap.md +33 -0
  776. templates/_base/scaffold/docs/api-contracts/00-index.md +58 -0
  777. templates/_base/scaffold/docs/api-contracts/error-format.md +58 -0
  778. templates/_base/scaffold/docs/architecture/00-index.md +42 -0
  779. templates/_base/scaffold/docs/architecture/adr/00-index.md +39 -0
  780. templates/_base/scaffold/docs/engineering/00-index.md +9 -0
  781. templates/_base/scaffold/docs/governance/00-index.md +55 -0
  782. templates/_base/scaffold/docs/governance/_templates/doc-cheat-sheet.md +202 -0
  783. templates/_base/scaffold/docs/governance/_templates/playbook-template.md +81 -0
  784. templates/_base/scaffold/docs/governance/_templates/post-mortem-template.md +85 -0
  785. templates/_base/scaffold/docs/governance/_templates/runbook-template.md +88 -0
  786. templates/_base/scaffold/docs/governance/_templates/security-review-template.md +111 -0
  787. templates/_base/scaffold/docs/governance/_templates/task-detail.md +61 -0
  788. templates/_base/scaffold/docs/governance/agent-workflow.md +101 -0
  789. templates/_base/scaffold/docs/governance/anatomy-contract.md +150 -0
  790. templates/_base/scaffold/docs/governance/critical-rules.md +224 -0
  791. templates/_base/scaffold/docs/governance/decision-records.md +58 -0
  792. templates/_base/scaffold/docs/governance/docs-first-protocol.md +157 -0
  793. templates/_base/scaffold/docs/governance/docs-system.md +151 -0
  794. templates/_base/scaffold/docs/governance/gdpr-compliance.md +66 -0
  795. templates/_base/scaffold/docs/governance/mcp-tool-inventory.md +112 -0
  796. templates/_base/scaffold/docs/governance/risk-register.md +26 -0
  797. templates/_base/scaffold/docs/governance/scaffold-boundary-contract.md +161 -0
  798. templates/_base/scaffold/docs/governance/task-lifecycle.md +125 -0
  799. templates/_base/scaffold/docs/governance/wrapper-derivation.md +50 -0
  800. templates/_base/scaffold/docs/insights/00-index.md +17 -0
  801. templates/_base/scaffold/docs/ops/00-index.md +59 -0
  802. templates/_base/scaffold/docs/ops/runbooks/00-index.md +9 -0
  803. templates/_base/scaffold/docs/playbooks/00-index.md +12 -0
  804. templates/_base/scaffold/docs/playbooks/research-validation.md +29 -0
  805. templates/_base/scaffold/docs/playbooks/security-review.md +41 -0
  806. templates/_base/scaffold/docs/prd/00-index.md +43 -0
  807. templates/_base/scaffold/docs/prd/01-snapshot-vision.md +56 -0
  808. templates/_base/scaffold/docs/workflow/workflow-guide.md +138 -0
  809. templates/_base/scaffold/src/shared/README.md +23 -0
  810. templates/_base/skill-enforcement.template.md +14 -0
  811. templates/_base/task-detail.template.md +61 -0
  812. templates/_presets/ai-saas.yaml +9 -0
  813. templates/_presets/django-next.yaml +8 -0
  814. templates/_presets/dotnet-react.yaml +8 -0
  815. templates/_presets/flutter-baas.yaml +8 -0
  816. templates/_presets/go-react.yaml +8 -0
  817. templates/_presets/hexagonal-product.yaml +16 -0
  818. templates/_presets/jamstack.yaml +8 -0
  819. templates/_presets/laravel-vue.yaml +8 -0
  820. templates/_presets/mean.yaml +8 -0
  821. templates/_presets/mern.yaml +9 -0
  822. templates/_presets/nest-angular.yaml +8 -0
  823. templates/_presets/nextjs-fastapi.yaml +10 -0
  824. templates/_presets/nuxt-fullstack.yaml +9 -0
  825. templates/_presets/pern.yaml +9 -0
  826. templates/_presets/rails-react.yaml +8 -0
  827. templates/_presets/rn-api.yaml +8 -0
  828. templates/_presets/rust-svelte.yaml +8 -0
  829. templates/_presets/spring-react.yaml +8 -0
  830. templates/_presets/t3-style.yaml +9 -0
  831. templates/_presets/tall.yaml +8 -0
  832. templates/_presets/wordpress-cms.yaml +8 -0
  833. templates/angular/rules/frontend.md +19 -0
  834. templates/angular/scaffold/docs/engineering/accessibility.md +46 -0
  835. templates/angular/scaffold/docs/engineering/angular-rules.md +35 -0
  836. templates/angular/scaffold/docs/playbooks/angular-app.md +42 -0
  837. templates/angular/scaffold/src/frontend/angular.json +52 -0
  838. templates/angular/scaffold/src/frontend/package.json +28 -0
  839. templates/angular/scaffold/src/frontend/src/app/app.component.ts +19 -0
  840. templates/angular/scaffold/src/frontend/src/app/app.config.ts +22 -0
  841. templates/angular/scaffold/src/frontend/src/app/app.routes.ts +8 -0
  842. templates/angular/scaffold/src/frontend/src/app/core/global-error-handler.ts +12 -0
  843. templates/angular/scaffold/src/frontend/src/app/health/health.component.ts +14 -0
  844. templates/angular/scaffold/src/frontend/src/app/health/health.service.spec.ts +16 -0
  845. templates/angular/scaffold/src/frontend/src/app/health/health.service.ts +10 -0
  846. templates/angular/scaffold/src/frontend/src/index.html +11 -0
  847. templates/angular/scaffold/src/frontend/src/main.ts +9 -0
  848. templates/angular/scaffold/src/frontend/src/styles.css +14 -0
  849. templates/angular/scaffold/src/frontend/tsconfig.app.json +8 -0
  850. templates/angular/scaffold/src/frontend/tsconfig.json +27 -0
  851. templates/angular/scaffold/src/frontend/tsconfig.spec.json +8 -0
  852. templates/angular/scaffold-boundary.yaml +27 -0
  853. templates/angular/skills/angular/SKILL.md +79 -0
  854. templates/angular/skills/angular/references/anatomy.md +69 -0
  855. templates/angular/stack.yaml +75 -0
  856. templates/aspnet-core/rules/backend.md +20 -0
  857. templates/aspnet-core/scaffold/docs/engineering/aspnet-core-rules.md +36 -0
  858. templates/aspnet-core/scaffold/docs/playbooks/aspnet-core-service.md +40 -0
  859. templates/aspnet-core/scaffold/src/backend/Backend.csproj +11 -0
  860. templates/aspnet-core/scaffold/src/backend/Backend.sln +27 -0
  861. templates/aspnet-core/scaffold/src/backend/Common/ExceptionHandlingMiddleware.cs +35 -0
  862. templates/aspnet-core/scaffold/src/backend/Features/Health/HealthEndpoints.cs +10 -0
  863. templates/aspnet-core/scaffold/src/backend/Features/Health/HealthService.cs +9 -0
  864. templates/aspnet-core/scaffold/src/backend/Program.cs +22 -0
  865. templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/Backend.Tests.csproj +22 -0
  866. templates/aspnet-core/scaffold/src/backend/tests/Backend.Tests/HealthServiceTests.cs +15 -0
  867. templates/aspnet-core/scaffold-boundary.yaml +24 -0
  868. templates/aspnet-core/skills/aspnet-core/SKILL.md +80 -0
  869. templates/aspnet-core/skills/aspnet-core/references/anatomy.md +65 -0
  870. templates/aspnet-core/stack.yaml +68 -0
  871. templates/astro/rules/frontend.md +20 -0
  872. templates/astro/scaffold/docs/engineering/astro-rules.md +40 -0
  873. templates/astro/scaffold/docs/playbooks/astro-app.md +57 -0
  874. templates/astro/scaffold/docs/playbooks/content-seo.md +37 -0
  875. templates/astro/scaffold/src/frontend/astro.config.mjs +10 -0
  876. templates/astro/scaffold/src/frontend/package.json +23 -0
  877. templates/astro/scaffold/src/frontend/src/components/Greeting.astro +13 -0
  878. templates/astro/scaffold/src/frontend/src/content/posts/hello.md +10 -0
  879. templates/astro/scaffold/src/frontend/src/content.config.ts +19 -0
  880. templates/astro/scaffold/src/frontend/src/lib/problem.test.ts +39 -0
  881. templates/astro/scaffold/src/frontend/src/lib/problem.ts +30 -0
  882. templates/astro/scaffold/src/frontend/src/pages/api/health.ts +13 -0
  883. templates/astro/scaffold/src/frontend/src/pages/index.astro +21 -0
  884. templates/astro/scaffold/src/frontend/tsconfig.json +9 -0
  885. templates/astro/scaffold/src/frontend/vitest.config.ts +10 -0
  886. templates/astro/scaffold-boundary.yaml +28 -0
  887. templates/astro/skills/astro/SKILL.md +71 -0
  888. templates/astro/skills/astro/references/anatomy.md +66 -0
  889. templates/astro/stack.yaml +75 -0
  890. templates/csharp-plain/scaffold/src/backend/Backend.csproj +12 -0
  891. templates/csharp-plain/scaffold/src/backend/Program.cs +1 -0
  892. templates/csharp-plain/scaffold-boundary.yaml +23 -0
  893. templates/csharp-plain/stack.yaml +50 -0
  894. templates/django/rules/backend.md +18 -0
  895. templates/django/scaffold/docs/engineering/anti-ambiguity.md +74 -0
  896. templates/django/scaffold/docs/engineering/backend-rules.md +133 -0
  897. templates/django/scaffold/docs/engineering/glossary.md +51 -0
  898. templates/django/scaffold/docs/engineering/logging-standards.md +107 -0
  899. templates/django/scaffold/docs/engineering/naming-conventions.md +68 -0
  900. templates/django/scaffold/docs/engineering/secrets-rotation-runbook.md +142 -0
  901. templates/django/scaffold/docs/playbooks/backend-api.md +119 -0
  902. templates/django/scaffold/src/backend/config/__init__.py +0 -0
  903. templates/django/scaffold/src/backend/config/settings.py +31 -0
  904. templates/django/scaffold/src/backend/config/urls.py +11 -0
  905. templates/django/scaffold/src/backend/config/wsgi.py +6 -0
  906. templates/django/scaffold/src/backend/manage.py +14 -0
  907. templates/django/scaffold/src/backend/pyproject.toml +37 -0
  908. templates/django/scaffold/src/backend/tests/test_health.py +4 -0
  909. templates/django/scaffold-boundary.yaml +25 -0
  910. templates/django/skills/python-django/SKILL.md +450 -0
  911. templates/django/skills/python-django/references/anatomy.md +117 -0
  912. templates/django/skills/python-django/scripts/new_endpoint.py +89 -0
  913. templates/django/stack.yaml +73 -0
  914. templates/fastapi/rules/backend.md +18 -0
  915. templates/fastapi/scaffold/docs/engineering/fastapi-rules.md +37 -0
  916. templates/fastapi/scaffold/docs/playbooks/fastapi-service.md +30 -0
  917. templates/fastapi/scaffold/src/backend/app/__init__.py +0 -0
  918. templates/fastapi/scaffold/src/backend/app/main.py +8 -0
  919. templates/fastapi/scaffold/src/backend/pyproject.toml +39 -0
  920. templates/fastapi/scaffold/src/backend/tests/test_health.py +11 -0
  921. templates/fastapi/scaffold-boundary.yaml +25 -0
  922. templates/fastapi/skills/python-fastapi/SKILL.md +75 -0
  923. templates/fastapi/skills/python-fastapi/references/anatomy.md +117 -0
  924. templates/fastapi/skills/python-fastapi/scripts/new_endpoint.py +101 -0
  925. templates/fastapi/stack.yaml +57 -0
  926. templates/flutter/rules/mobile.md +26 -0
  927. templates/flutter/scaffold/docs/engineering/flutter-rules.md +35 -0
  928. templates/flutter/scaffold/docs/playbooks/flutter-app.md +45 -0
  929. templates/flutter/scaffold/src/mobile/lib/core/error_mapper.dart +17 -0
  930. templates/flutter/scaffold/src/mobile/lib/core/router.dart +13 -0
  931. templates/flutter/scaffold/src/mobile/lib/main.dart +22 -0
  932. templates/flutter/scaffold/src/mobile/lib/screens/health_screen.dart +32 -0
  933. templates/flutter/scaffold/src/mobile/lib/services/health_service.dart +11 -0
  934. templates/flutter/scaffold/src/mobile/lib/state/health_provider.dart +13 -0
  935. templates/flutter/scaffold/src/mobile/pubspec.yaml +22 -0
  936. templates/flutter/scaffold/src/mobile/test/health_provider_test.dart +90 -0
  937. templates/flutter/scaffold-boundary.yaml +28 -0
  938. templates/flutter/skills/flutter/SKILL.md +76 -0
  939. templates/flutter/skills/flutter/references/anatomy.md +64 -0
  940. templates/flutter/stack.yaml +70 -0
  941. templates/go/rules/backend.md +19 -0
  942. templates/go/scaffold/docs/engineering/go-rules.md +45 -0
  943. templates/go/scaffold/docs/playbooks/go-service.md +30 -0
  944. templates/go/scaffold/src/backend/cmd/api/main.go +22 -0
  945. templates/go/scaffold/src/backend/cmd/api/main_test.go +20 -0
  946. templates/go/scaffold/src/backend/go.mod +3 -0
  947. templates/go/scaffold-boundary.yaml +25 -0
  948. templates/go/skills/go-patterns/SKILL.md +68 -0
  949. templates/go/skills/go-patterns/assets/go-checklist.md +29 -0
  950. templates/go/skills/go-patterns/references/anatomy.md +115 -0
  951. templates/go/skills/go-patterns/references/go-2026-idioms.md +92 -0
  952. templates/go/skills/go-patterns/scripts/new_endpoint.py +117 -0
  953. templates/go/skills/go-patterns/versions.json +16 -0
  954. templates/go/stack.yaml +54 -0
  955. templates/go-fiber/rules/backend.md +20 -0
  956. templates/go-fiber/scaffold/docs/engineering/fiber-rules.md +97 -0
  957. templates/go-fiber/scaffold/docs/playbooks/fiber-service.md +149 -0
  958. templates/go-fiber/scaffold/src/backend/cmd/api/main.go +21 -0
  959. templates/go-fiber/scaffold/src/backend/cmd/api/main_test.go +17 -0
  960. templates/go-fiber/scaffold/src/backend/go.mod +23 -0
  961. templates/go-fiber/scaffold/src/backend/go.sum +49 -0
  962. templates/go-fiber/scaffold-boundary.yaml +26 -0
  963. templates/go-fiber/skills/go-fiber/SKILL.md +203 -0
  964. templates/go-fiber/skills/go-fiber/assets/fiber-checklist.md +26 -0
  965. templates/go-fiber/skills/go-fiber/references/anatomy.md +116 -0
  966. templates/go-fiber/skills/go-fiber/references/fiber-v3-patterns.md +89 -0
  967. templates/go-fiber/skills/go-fiber/scripts/new_endpoint.py +107 -0
  968. templates/go-fiber/skills/go-fiber/versions.json +16 -0
  969. templates/go-fiber/stack.yaml +59 -0
  970. templates/go-plain/scaffold/src/backend/go.mod +3 -0
  971. templates/go-plain/scaffold/src/backend/main.go +7 -0
  972. templates/go-plain/scaffold-boundary.yaml +22 -0
  973. templates/go-plain/stack.yaml +51 -0
  974. templates/java-plain/scaffold/src/backend/mvnw +302 -0
  975. templates/java-plain/scaffold/src/backend/pom.xml +41 -0
  976. templates/java-plain/scaffold/src/backend/src/main/java/com/example/app/Main.java +10 -0
  977. templates/java-plain/scaffold-boundary.yaml +23 -0
  978. templates/java-plain/stack.yaml +51 -0
  979. templates/laravel/rules/backend.md +20 -0
  980. templates/laravel/scaffold/docs/engineering/laravel-rules.md +28 -0
  981. templates/laravel/scaffold/docs/playbooks/laravel-service.md +28 -0
  982. templates/laravel/scaffold/src/backend/app/Exceptions/Handler.php +27 -0
  983. templates/laravel/scaffold/src/backend/app/Http/Controllers/HealthController.php +15 -0
  984. templates/laravel/scaffold/src/backend/app/Support/HealthStatus.php +14 -0
  985. templates/laravel/scaffold/src/backend/composer.json +24 -0
  986. templates/laravel/scaffold/src/backend/phpunit.xml +10 -0
  987. templates/laravel/scaffold/src/backend/public/index.php +8 -0
  988. templates/laravel/scaffold/src/backend/routes/api.php +7 -0
  989. templates/laravel/scaffold/src/backend/tests/Unit/HealthStatusTest.php +16 -0
  990. templates/laravel/scaffold-boundary.yaml +23 -0
  991. templates/laravel/skills/laravel/SKILL.md +56 -0
  992. templates/laravel/skills/laravel/references/anatomy.md +65 -0
  993. templates/laravel/stack.yaml +66 -0
  994. templates/meta/rules/graph-first.md +27 -0
  995. templates/meta/rules/hook-author.md +19 -0
  996. templates/meta/rules/mcp-tool-author.md +18 -0
  997. templates/meta/rules/meta-engineering.md +17 -0
  998. templates/meta/scaffold-boundary.yaml +55 -0
  999. templates/meta/skills/claude-sdk-integration/SKILL.md +163 -0
  1000. templates/meta/skills/claude-sdk-integration/assets/sdk-checklist.md +27 -0
  1001. templates/meta/skills/claude-sdk-integration/scripts/check_model_ids.py +97 -0
  1002. templates/meta/skills/graph-os-authoring/SKILL.md +278 -0
  1003. templates/meta/skills/graph-os-authoring/assets/graph-os-checklist.md +25 -0
  1004. templates/meta/skills/graph-os-authoring/scripts/new_extractor.py +76 -0
  1005. templates/meta/skills/hook-authoring/SKILL.md +292 -0
  1006. templates/meta/skills/hook-authoring/assets/hook-checklist.md +30 -0
  1007. templates/meta/skills/hook-authoring/scripts/new_hook.sh +75 -0
  1008. templates/meta/skills/mcp-tool-authoring/SKILL.md +301 -0
  1009. templates/meta/skills/mcp-tool-authoring/assets/mcp-tool-checklist.md +29 -0
  1010. templates/meta/skills/mcp-tool-authoring/scripts/new_tool.py +74 -0
  1011. templates/meta/skills/meta-engineering/SKILL.md +151 -0
  1012. templates/meta/skills/meta-engineering/assets/meta-edit-checklist.md +28 -0
  1013. templates/meta/skills/meta-engineering/scripts/which_layer.py +61 -0
  1014. templates/meta/skills/python-meta-server/SKILL.md +162 -0
  1015. templates/meta/skills/python-meta-server/assets/meta-server-checklist.md +28 -0
  1016. templates/meta/skills/python-meta-server/scripts/check_envelope.py +91 -0
  1017. templates/meta/skills/react-vite-hub/SKILL.md +140 -0
  1018. templates/meta/skills/react-vite-hub/assets/hub-ui-checklist.md +23 -0
  1019. templates/meta/skills/react-vite-hub/scripts/check_vite_env.py +73 -0
  1020. templates/meta/stack.yaml +119 -0
  1021. templates/nestjs/rules/backend.md +20 -0
  1022. templates/nestjs/scaffold/docs/engineering/nestjs-rules.md +33 -0
  1023. templates/nestjs/scaffold/docs/playbooks/nestjs-service.md +39 -0
  1024. templates/nestjs/scaffold/src/backend/nest-cli.json +5 -0
  1025. templates/nestjs/scaffold/src/backend/package.json +28 -0
  1026. templates/nestjs/scaffold/src/backend/src/app.module.ts +9 -0
  1027. templates/nestjs/scaffold/src/backend/src/common/all-exceptions.filter.ts +59 -0
  1028. templates/nestjs/scaffold/src/backend/src/health/health.controller.ts +14 -0
  1029. templates/nestjs/scaffold/src/backend/src/health/health.module.ts +10 -0
  1030. templates/nestjs/scaffold/src/backend/src/health/health.service.spec.ts +21 -0
  1031. templates/nestjs/scaffold/src/backend/src/health/health.service.ts +9 -0
  1032. templates/nestjs/scaffold/src/backend/src/main.ts +26 -0
  1033. templates/nestjs/scaffold/src/backend/tsconfig.json +16 -0
  1034. templates/nestjs/scaffold/src/backend/vitest.config.ts +9 -0
  1035. templates/nestjs/scaffold-boundary.yaml +25 -0
  1036. templates/nestjs/skills/nestjs/SKILL.md +67 -0
  1037. templates/nestjs/skills/nestjs/references/anatomy.md +65 -0
  1038. templates/nestjs/stack.yaml +68 -0
  1039. templates/nextjs/rules/frontend.md +18 -0
  1040. templates/nextjs/scaffold/docs/design/00-index.md +23 -0
  1041. templates/nextjs/scaffold/docs/design/colors-tokens.md +141 -0
  1042. templates/nextjs/scaffold/docs/design/components-patterns.md +159 -0
  1043. templates/nextjs/scaffold/docs/design/motion-accessibility.md +137 -0
  1044. templates/nextjs/scaffold/docs/design/typography-spacing.md +107 -0
  1045. templates/nextjs/scaffold/docs/engineering/accessibility-web.md +56 -0
  1046. templates/nextjs/scaffold/docs/engineering/copywriting-standard.md +102 -0
  1047. templates/nextjs/scaffold/docs/engineering/formatting-rules.md +89 -0
  1048. templates/nextjs/scaffold/docs/engineering/frontend-rendering-rules.md +80 -0
  1049. templates/nextjs/scaffold/docs/engineering/frontend-rules.md +183 -0
  1050. templates/nextjs/scaffold/docs/engineering/i18n-policy.md +99 -0
  1051. templates/nextjs/scaffold/docs/pages-content-spec/00-index.md +78 -0
  1052. templates/nextjs/scaffold/docs/playbooks/content-seo.md +55 -0
  1053. templates/nextjs/scaffold/docs/playbooks/docs-governance.md +51 -0
  1054. templates/nextjs/scaffold/docs/playbooks/frontend-ui.md +63 -0
  1055. templates/nextjs/scaffold/src/frontend/app/layout.tsx +14 -0
  1056. templates/nextjs/scaffold/src/frontend/app/page.tsx +3 -0
  1057. templates/nextjs/scaffold/src/frontend/eslint.config.js +17 -0
  1058. templates/nextjs/scaffold/src/frontend/lib/greeting.test.ts +9 -0
  1059. templates/nextjs/scaffold/src/frontend/lib/greeting.ts +3 -0
  1060. templates/nextjs/scaffold/src/frontend/package.json +28 -0
  1061. templates/nextjs/scaffold/src/frontend/tsconfig.json +18 -0
  1062. templates/nextjs/scaffold/src/frontend/vitest.config.ts +10 -0
  1063. templates/nextjs/scaffold-boundary.yaml +30 -0
  1064. templates/nextjs/skills/nextjs-react/SKILL.md +485 -0
  1065. templates/nextjs/skills/nextjs-react/references/anatomy.md +116 -0
  1066. templates/nextjs/skills/nextjs-react/scripts/new_component.py +72 -0
  1067. templates/nextjs/stack.yaml +81 -0
  1068. templates/node-express/rules/backend.md +20 -0
  1069. templates/node-express/scaffold/docs/engineering/express-rules.md +30 -0
  1070. templates/node-express/scaffold/docs/playbooks/express-service.md +35 -0
  1071. templates/node-express/scaffold/src/backend/package.json +24 -0
  1072. templates/node-express/scaffold/src/backend/src/index.ts +17 -0
  1073. templates/node-express/scaffold/src/backend/src/middleware/error-handler.ts +12 -0
  1074. templates/node-express/scaffold/src/backend/src/routes/health.test.ts +33 -0
  1075. templates/node-express/scaffold/src/backend/src/routes/health.ts +7 -0
  1076. templates/node-express/scaffold/src/backend/tsconfig.json +15 -0
  1077. templates/node-express/scaffold/src/backend/types/express-bootstrap.d.ts +21 -0
  1078. templates/node-express/scaffold-boundary.yaml +25 -0
  1079. templates/node-express/skills/node-express/SKILL.md +70 -0
  1080. templates/node-express/skills/node-express/references/anatomy.md +63 -0
  1081. templates/node-express/stack.yaml +63 -0
  1082. templates/python/scaffold/docs/engineering/python-rules.md +27 -0
  1083. templates/python/scaffold/docs/playbooks/python-library.md +35 -0
  1084. templates/python/stack.yaml +60 -0
  1085. templates/rails/rules/backend.md +10 -0
  1086. templates/rails/scaffold/docs/engineering/rails-rules.md +33 -0
  1087. templates/rails/scaffold/docs/playbooks/rails-service.md +42 -0
  1088. templates/rails/scaffold/src/backend/Gemfile +12 -0
  1089. templates/rails/scaffold/src/backend/app/controllers/application_controller.rb +26 -0
  1090. templates/rails/scaffold/src/backend/app/controllers/health_controller.rb +6 -0
  1091. templates/rails/scaffold/src/backend/app/models/health.rb +6 -0
  1092. templates/rails/scaffold/src/backend/config/application.rb +12 -0
  1093. templates/rails/scaffold/src/backend/config/boot.rb +3 -0
  1094. templates/rails/scaffold/src/backend/config/routes.rb +4 -0
  1095. templates/rails/scaffold/src/backend/config.ru +5 -0
  1096. templates/rails/scaffold/src/backend/spec/rails_helper.rb +18 -0
  1097. templates/rails/scaffold/src/backend/spec/requests/health_spec.rb +24 -0
  1098. templates/rails/scaffold-boundary.yaml +25 -0
  1099. templates/rails/skills/rails/SKILL.md +62 -0
  1100. templates/rails/skills/rails/references/anatomy.md +71 -0
  1101. templates/rails/stack.yaml +72 -0
  1102. templates/react-native/rules/mobile.md +26 -0
  1103. templates/react-native/scaffold/docs/engineering/accessibility-mobile.md +95 -0
  1104. templates/react-native/scaffold/docs/engineering/mobile-rules.md +56 -0
  1105. templates/react-native/scaffold/docs/engineering/offline-first.md +61 -0
  1106. templates/react-native/scaffold/docs/playbooks/mobile-app.md +49 -0
  1107. templates/react-native/scaffold/src/mobile/App.tsx +9 -0
  1108. templates/react-native/scaffold/src/mobile/eslint.config.js +17 -0
  1109. templates/react-native/scaffold/src/mobile/package.json +23 -0
  1110. templates/react-native/scaffold/src/mobile/src/greeting.test.ts +9 -0
  1111. templates/react-native/scaffold/src/mobile/src/greeting.ts +3 -0
  1112. templates/react-native/scaffold/src/mobile/tsconfig.json +17 -0
  1113. templates/react-native/scaffold/src/mobile/vitest.config.ts +10 -0
  1114. templates/react-native/scaffold-boundary.yaml +30 -0
  1115. templates/react-native/skills/react-native-mobile/SKILL.md +119 -0
  1116. templates/react-native/skills/react-native-mobile/assets/rn-mobile-checklist.md +28 -0
  1117. templates/react-native/skills/react-native-mobile/references/anatomy.md +140 -0
  1118. templates/react-native/skills/react-native-mobile/references/rn-2026-practices.md +54 -0
  1119. templates/react-native/skills/react-native-mobile/scripts/new_screen.py +73 -0
  1120. templates/react-native/skills/react-native-mobile/versions.json +16 -0
  1121. templates/react-native/skills/react-native-patterns/SKILL.md +512 -0
  1122. templates/react-native/skills/react-native-patterns/assets/rn-review-checklist.md +26 -0
  1123. templates/react-native/skills/react-native-patterns/references/anatomy.md +62 -0
  1124. templates/react-native/skills/react-native-patterns/references/list-performance.md +70 -0
  1125. templates/react-native/skills/react-native-patterns/scripts/scan_rn_perf.py +77 -0
  1126. templates/react-native/stack.yaml +70 -0
  1127. templates/ruby-plain/scaffold/src/backend/Gemfile +8 -0
  1128. templates/ruby-plain/scaffold/src/backend/main.rb +3 -0
  1129. templates/ruby-plain/scaffold-boundary.yaml +23 -0
  1130. templates/ruby-plain/stack.yaml +50 -0
  1131. templates/rust-axum/rules/backend.md +20 -0
  1132. templates/rust-axum/scaffold/docs/engineering/rust-axum-rules.md +35 -0
  1133. templates/rust-axum/scaffold/docs/playbooks/rust-axum-service.md +43 -0
  1134. templates/rust-axum/scaffold/src/backend/Cargo.toml +20 -0
  1135. templates/rust-axum/scaffold/src/backend/src/app.rs +12 -0
  1136. templates/rust-axum/scaffold/src/backend/src/error.rs +48 -0
  1137. templates/rust-axum/scaffold/src/backend/src/main.rs +24 -0
  1138. templates/rust-axum/scaffold/src/backend/src/routes/health.rs +37 -0
  1139. templates/rust-axum/scaffold/src/backend/src/routes/mod.rs +2 -0
  1140. templates/rust-axum/scaffold-boundary.yaml +25 -0
  1141. templates/rust-axum/skills/rust/SKILL.md +73 -0
  1142. templates/rust-axum/skills/rust/references/anatomy.md +63 -0
  1143. templates/rust-axum/stack.yaml +65 -0
  1144. templates/rust-plain/scaffold/src/backend/Cargo.toml +6 -0
  1145. templates/rust-plain/scaffold/src/backend/src/main.rs +3 -0
  1146. templates/rust-plain/scaffold-boundary.yaml +23 -0
  1147. templates/rust-plain/stack.yaml +51 -0
  1148. templates/spring-boot/rules/backend.md +20 -0
  1149. templates/spring-boot/scaffold/docs/engineering/spring-boot-rules.md +35 -0
  1150. templates/spring-boot/scaffold/docs/playbooks/spring-boot-service.md +45 -0
  1151. templates/spring-boot/scaffold/src/backend/mvnw +302 -0
  1152. templates/spring-boot/scaffold/src/backend/pom.xml +69 -0
  1153. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/Application.java +16 -0
  1154. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/common/GlobalExceptionHandler.java +31 -0
  1155. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthController.java +22 -0
  1156. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthService.java +12 -0
  1157. templates/spring-boot/scaffold/src/backend/src/main/java/com/example/app/health/HealthStatus.java +4 -0
  1158. templates/spring-boot/scaffold/src/backend/src/test/java/com/example/app/health/HealthServiceTest.java +15 -0
  1159. templates/spring-boot/scaffold-boundary.yaml +26 -0
  1160. templates/spring-boot/skills/spring-boot/SKILL.md +84 -0
  1161. templates/spring-boot/skills/spring-boot/references/anatomy.md +63 -0
  1162. templates/spring-boot/stack.yaml +65 -0
  1163. templates/svelte-sveltekit/rules/frontend.md +20 -0
  1164. templates/svelte-sveltekit/scaffold/docs/engineering/svelte-sveltekit-rules.md +37 -0
  1165. templates/svelte-sveltekit/scaffold/docs/playbooks/svelte-sveltekit-app.md +36 -0
  1166. templates/svelte-sveltekit/scaffold/src/frontend/package.json +22 -0
  1167. templates/svelte-sveltekit/scaffold/src/frontend/src/app.html +12 -0
  1168. templates/svelte-sveltekit/scaffold/src/frontend/src/hooks.server.ts +14 -0
  1169. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/components/Greeting.svelte +6 -0
  1170. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.test.ts +26 -0
  1171. templates/svelte-sveltekit/scaffold/src/frontend/src/lib/stores/count.ts +4 -0
  1172. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+layout.svelte +24 -0
  1173. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.svelte +9 -0
  1174. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/+page.ts +7 -0
  1175. templates/svelte-sveltekit/scaffold/src/frontend/src/routes/health/+server.ts +7 -0
  1176. templates/svelte-sveltekit/scaffold/src/frontend/svelte.config.js +10 -0
  1177. templates/svelte-sveltekit/scaffold/src/frontend/tsconfig.json +7 -0
  1178. templates/svelte-sveltekit/scaffold/src/frontend/vite.config.ts +7 -0
  1179. templates/svelte-sveltekit/scaffold/src/frontend/vitest.config.ts +11 -0
  1180. templates/svelte-sveltekit/scaffold-boundary.yaml +29 -0
  1181. templates/svelte-sveltekit/skills/svelte/SKILL.md +90 -0
  1182. templates/svelte-sveltekit/skills/svelte/references/anatomy.md +62 -0
  1183. templates/svelte-sveltekit/stack.yaml +70 -0
  1184. templates/typescript-plain/scaffold/src/index.ts +3 -0
  1185. templates/typescript-plain/scaffold/tsconfig.json +13 -0
  1186. templates/typescript-plain/scaffold-boundary.yaml +22 -0
  1187. templates/typescript-plain/stack.yaml +44 -0
  1188. templates/vue-nuxt/rules/frontend.md +19 -0
  1189. templates/vue-nuxt/scaffold/docs/engineering/nuxt-rules.md +30 -0
  1190. templates/vue-nuxt/scaffold/docs/playbooks/nuxt-app.md +29 -0
  1191. templates/vue-nuxt/scaffold/src/frontend/app.vue +3 -0
  1192. templates/vue-nuxt/scaffold/src/frontend/nuxt.config.ts +11 -0
  1193. templates/vue-nuxt/scaffold/src/frontend/package.json +20 -0
  1194. templates/vue-nuxt/scaffold/src/frontend/pages/index.test.ts +19 -0
  1195. templates/vue-nuxt/scaffold/src/frontend/pages/index.vue +11 -0
  1196. templates/vue-nuxt/scaffold/src/frontend/vitest.config.ts +12 -0
  1197. templates/vue-nuxt/scaffold-boundary.yaml +26 -0
  1198. templates/vue-nuxt/skills/vue-nuxt/SKILL.md +57 -0
  1199. templates/vue-nuxt/skills/vue-nuxt/references/anatomy.md +60 -0
  1200. templates/vue-nuxt/stack.yaml +61 -0
  1201. templates/wordpress/rules/backend.md +19 -0
  1202. templates/wordpress/scaffold/docs/engineering/wordpress-rules.md +28 -0
  1203. templates/wordpress/scaffold/docs/playbooks/wordpress-service.md +29 -0
  1204. templates/wordpress/scaffold/src/backend/composer.json +17 -0
  1205. templates/wordpress/scaffold/src/backend/phpcs.xml.dist +11 -0
  1206. templates/wordpress/scaffold/src/backend/phpunit.xml +10 -0
  1207. templates/wordpress/scaffold/src/backend/plugin/inc/health.php +8 -0
  1208. templates/wordpress/scaffold/src/backend/plugin/plugin.php +28 -0
  1209. templates/wordpress/scaffold/src/backend/tests/HealthStatusTest.php +15 -0
  1210. templates/wordpress/scaffold/src/backend/theme/functions.php +18 -0
  1211. templates/wordpress/scaffold/src/backend/theme/style.css +11 -0
  1212. templates/wordpress/scaffold-boundary.yaml +23 -0
  1213. templates/wordpress/skills/wordpress/SKILL.md +110 -0
  1214. templates/wordpress/skills/wordpress/assets/wp-checklist.md +28 -0
  1215. templates/wordpress/skills/wordpress/references/wp-development.md +75 -0
  1216. templates/wordpress/skills/wordpress/references/wp-security.md +68 -0
  1217. templates/wordpress/skills/wordpress/scripts/scan_wp_smells.py +91 -0
  1218. templates/wordpress/skills/wordpress/versions.json +16 -0
  1219. templates/wordpress/stack.yaml +60 -0
  1220. thinking_os/__init__.py +1 -0
  1221. thinking_os/_agent_markers.py +32 -0
  1222. thinking_os/background.py +405 -0
  1223. thinking_os/bootstrap_outcomes.py +200 -0
  1224. thinking_os/budget.py +302 -0
  1225. thinking_os/capture.py +495 -0
  1226. thinking_os/cognition.py +516 -0
  1227. thinking_os/cognition_schemas.py +517 -0
  1228. thinking_os/compress.py +192 -0
  1229. thinking_os/concepts.py +233 -0
  1230. thinking_os/dashboard.py +159 -0
  1231. thinking_os/database.py +2883 -0
  1232. thinking_os/decay.py +393 -0
  1233. thinking_os/digest.py +295 -0
  1234. thinking_os/dispatcher.py +192 -0
  1235. thinking_os/dispatcher_helpers.py +48 -0
  1236. thinking_os/dispatchers/__init__.py +3 -0
  1237. thinking_os/dispatchers/default.py +47 -0
  1238. thinking_os/distill.py +192 -0
  1239. thinking_os/doc_indexer.py +905 -0
  1240. thinking_os/embeddings.py +943 -0
  1241. thinking_os/formula_composer.py +556 -0
  1242. thinking_os/gate_marker.py +75 -0
  1243. thinking_os/graph.py +296 -0
  1244. thinking_os/graph_indexer.py +360 -0
  1245. thinking_os/health_check.py +517 -0
  1246. thinking_os/impact.py +119 -0
  1247. thinking_os/memory_gc.py +356 -0
  1248. thinking_os/migrator_embeddings.py +318 -0
  1249. thinking_os/precision.py +194 -0
  1250. thinking_os/record_outcome.py +399 -0
  1251. thinking_os/repair.py +105 -0
  1252. thinking_os/retrieval_quality.py +239 -0
  1253. thinking_os/roles_state.py +168 -0
  1254. thinking_os/sanitizer.py +320 -0
  1255. thinking_os/server.py +3160 -0
  1256. thinking_os/session_enrich.py +272 -0
  1257. thinking_os/session_observe_worker.py +111 -0
  1258. thinking_os/session_startup.py +74 -0
  1259. thinking_os/session_summary.py +245 -0
  1260. thinking_os/task_analyzer.py +462 -0
  1261. thinking_os/task_parser.py +342 -0
  1262. thinking_os/task_sync.py +73 -0
  1263. thinking_os/tools/__init__.py +6 -0
  1264. thinking_os/tools/_shared.py +947 -0
  1265. thinking_os/tools/cognition.py +1867 -0
  1266. thinking_os/tools/docs.py +770 -0
  1267. thinking_os/tools/learning.py +2078 -0
  1268. thinking_os/tools/logs.py +79 -0
  1269. thinking_os/tools/memory.py +840 -0
  1270. thinking_os/tools/metrics.py +200 -0
  1271. thinking_os/tools/retrieve.py +415 -0
  1272. thinking_os/tools/routing.py +658 -0
  1273. thinking_os/tools/tasks.py +449 -0
  1274. thinking_os/tools/trajectory.py +181 -0
  1275. thinking_os/tracing.py +235 -0
  1276. web/__init__.py +5 -0
  1277. web/_cache.py +118 -0
  1278. web/_deps.py +56 -0
  1279. web/_envelope.py +85 -0
  1280. web/_project_context.py +140 -0
  1281. web/chat_providers.py +108 -0
  1282. web/init_jobs.py +216 -0
  1283. web/routes/__init__.py +25 -0
  1284. web/routes/_bounded_read.py +76 -0
  1285. web/routes/board.py +1089 -0
  1286. web/routes/cognition.py +1838 -0
  1287. web/routes/config.py +635 -0
  1288. web/routes/graph.py +513 -0
  1289. web/routes/health.py +180 -0
  1290. web/routes/hooks.py +288 -0
  1291. web/routes/hub.py +1219 -0
  1292. web/routes/logs.py +374 -0
  1293. web/routes/metrics.py +43 -0
  1294. web/routes/observability.py +400 -0
  1295. web/routes/patterns.py +227 -0
  1296. web/routes/presence.py +609 -0
  1297. web/routes/roles.py +446 -0
  1298. web/routes/scheduled.py +261 -0
  1299. web/routes/search.py +238 -0
  1300. web/routes/sessions.py +220 -0
  1301. web/routes/settings.py +363 -0
  1302. web/routes/stream.py +547 -0
  1303. web/security.py +157 -0
  1304. web/server.py +283 -0
cli/graph_commands.py ADDED
@@ -0,0 +1,1265 @@
1
+ """graph_os CLI subcommands.
2
+
3
+ Registers the `cos graph-*` family on the root `cli` group:
4
+
5
+ cos graph-reindex [--path DIR] [--no-docs]
6
+ cos graph-query "<phrase>" [--limit N] [--kinds ...]
7
+ cos graph-context <uid>
8
+ cos graph-impact <uid> [--downstream|--upstream]
9
+ cos graph-references <uid>
10
+ cos graph-path <src> <dst>
11
+ cos graph-contracts [--kind http,mcp,...]
12
+ cos graph-rename-plan <uid> <new-name>
13
+ cos graph-export [--format json|mermaid|dot] [--out FILE]
14
+ cos graph-viz [--path DIR] [--out FILE] [--serve] [--port N]
15
+ cos graph-stats
16
+ cos graph-index-local <path>
17
+ cos graph-index-github <url> [--auth TOKEN]
18
+ cos graph-index-zip <archive>
19
+ cos graph-group create|add|remove|list|status|sync|query|contracts|viz ...
20
+
21
+ Every subcommand prints JSON by default so scripts and agents can parse
22
+ consistently. `--pretty` or the absence of `--json` picks a readable
23
+ form.
24
+
25
+ All commands go through the same envelope shape as the MCP tools
26
+ (Rule 14) so agents running `cos` in a shell get the same signal they
27
+ would via MCP.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import os
34
+ import sys
35
+ import tempfile
36
+ from collections.abc import Iterable
37
+ from pathlib import Path
38
+ from typing import Any
39
+
40
+ import click
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Lazy bootstrap — push core/ + src/core/thinking_os onto sys.path.
44
+ # ---------------------------------------------------------------------------
45
+
46
+
47
+ def _bootstrap_paths() -> None:
48
+ from cli._resources import core_dir as _core_dir
49
+
50
+ core_path = _core_dir()
51
+ tos_dir = core_path / "thinking_os"
52
+ for candidate in (core_path, tos_dir):
53
+ if candidate.exists() and str(candidate) not in sys.path:
54
+ sys.path.insert(0, str(candidate))
55
+
56
+
57
+ def _json_echo(payload: Any, *, pretty: bool = False) -> None:
58
+ if isinstance(payload, str):
59
+ # Already a JSON envelope from a `cos_graph_*` tool.
60
+ try:
61
+ payload = json.loads(payload)
62
+ except json.JSONDecodeError:
63
+ click.echo(payload)
64
+ return
65
+ if pretty:
66
+ click.echo(json.dumps(payload, indent=2, default=str))
67
+ else:
68
+ click.echo(json.dumps(payload, default=str))
69
+
70
+
71
+ def _open_backend():
72
+ _bootstrap_paths()
73
+ from database import init_db # type: ignore
74
+
75
+ from graph_os.backends.sqlite_backend import SqliteBackend # type: ignore
76
+ from graph_os.tools import graph as graph_tools # type: ignore
77
+
78
+ conn = init_db()
79
+ backend = SqliteBackend(conn=conn)
80
+ graph_tools._BACKEND_SINGLETON = backend
81
+ return backend, graph_tools
82
+
83
+
84
+ # ---------------------------------------------------------------------------
85
+ # Parallel reindex worker — must be module-level so ProcessPoolExecutor
86
+ # can pickle it. Each worker re-imports graph_os.tools.reindex_dispatch
87
+ # inside its own process; init_db() inside dispatch() opens a fresh
88
+ # SQLite WAL connection. The dispatcher's busy-retry loop handles
89
+ # concurrent writers without the CLI having to coordinate.
90
+ # ---------------------------------------------------------------------------
91
+
92
+
93
+ def _report_failure_reason(report: dict) -> str | None:
94
+ graph_layer = (report.get("layers") or {}).get("graph") or {}
95
+ if report.get("status") == "error" or graph_layer.get("status") == "error":
96
+ return str(graph_layer.get("reason") or report.get("reason") or "unknown")
97
+ return None
98
+
99
+
100
+ def _is_lock_shaped(reason: str) -> bool:
101
+ lowered = reason.lower()
102
+ return "locked" in lowered or "busy" in lowered
103
+
104
+
105
+ def _parallel_dispatch(
106
+ file_path: str,
107
+ project_root: str,
108
+ include_docs: bool,
109
+ force: bool,
110
+ ) -> dict:
111
+ _bootstrap_paths()
112
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
113
+
114
+ # defer per-file stub linking — graph_reindex runs ONE global
115
+ # link after the whole walk, so mid-walk resolutions aren't orphaned by a
116
+ # later file's prune-before-reindex.
117
+ return dispatch(
118
+ file_path,
119
+ project_root=project_root,
120
+ include_docs=include_docs,
121
+ force=force,
122
+ link_stubs=False,
123
+ )
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Query / context / impact / etc.
128
+ # ---------------------------------------------------------------------------
129
+
130
+
131
+ def register(cli: click.Group) -> None:
132
+ """Attach every `cos graph-*` subcommand onto the parent `cli` group."""
133
+
134
+ @cli.command(name="graph-query")
135
+ @click.argument("query", nargs=-1)
136
+ @click.option("--limit", default=10, show_default=True, type=int)
137
+ @click.option("--kinds", default="", help="Comma-separated node kinds.")
138
+ @click.option("--max-hops", default=2, type=int)
139
+ @click.option("--confidence-min", default=0.3, type=float)
140
+ @click.option("--pretty", is_flag=True)
141
+ def graph_query(query, limit, kinds, max_hops, confidence_min, pretty):
142
+ """Hybrid search over node labels + docstrings."""
143
+ q = " ".join(query).strip()
144
+ if not q:
145
+ raise click.BadParameter("query argument required")
146
+ _, tools = _open_backend()
147
+ result = tools.cos_graph_query(
148
+ q,
149
+ kinds=[k.strip() for k in kinds.split(",") if k.strip()] or None,
150
+ limit=limit,
151
+ max_hops=max_hops,
152
+ confidence_min=confidence_min,
153
+ )
154
+ _json_echo(result, pretty=pretty)
155
+
156
+ @cli.command(name="graph-search")
157
+ @click.argument("query", nargs=-1)
158
+ @click.option("--top-k", default=10, show_default=True, type=int)
159
+ @click.option("--pretty", is_flag=True)
160
+ def graph_search(query, top_k, pretty):
161
+ """Hybrid semantic + lexical + centrality search over indexed code by free text."""
162
+ q = " ".join(query).strip()
163
+ if not q:
164
+ raise click.BadParameter("query argument required")
165
+ _, tools = _open_backend()
166
+ _json_echo(tools.cos_graph_search(q, top_k=top_k), pretty=pretty)
167
+
168
+ @cli.command(name="graph-context")
169
+ @click.argument("uid")
170
+ @click.option("--direction", default="both", type=click.Choice(["in", "out", "both"]))
171
+ @click.option("--depth", default=1, type=int)
172
+ @click.option("--include-content", is_flag=True)
173
+ @click.option("--include-evidence", is_flag=True)
174
+ @click.option("--pretty", is_flag=True)
175
+ def graph_context(uid, direction, depth, include_content, include_evidence, pretty):
176
+ """Neighbourhood around a symbol (callers + callees + refs)."""
177
+ _, tools = _open_backend()
178
+ _json_echo(
179
+ tools.cos_graph_context(
180
+ uid,
181
+ direction=direction,
182
+ depth=depth,
183
+ include_content=include_content,
184
+ include_evidence=include_evidence,
185
+ ),
186
+ pretty=pretty,
187
+ )
188
+
189
+ @cli.command(name="graph-impact")
190
+ @click.argument("uid")
191
+ @click.option(
192
+ "--direction", default="downstream", type=click.Choice(["downstream", "upstream", "both"])
193
+ )
194
+ @click.option("--depth", default=3, type=int)
195
+ @click.option("--confidence-min", default=0.5, type=float)
196
+ @click.option("--pretty", is_flag=True)
197
+ def graph_impact(uid, direction, depth, confidence_min, pretty):
198
+ """Blast-radius of a change — risk-tiered."""
199
+ _, tools = _open_backend()
200
+ _json_echo(
201
+ tools.cos_graph_impact(
202
+ uid,
203
+ direction=direction,
204
+ depth=depth,
205
+ confidence_min=confidence_min,
206
+ ),
207
+ pretty=pretty,
208
+ )
209
+
210
+ @cli.command(name="graph-references")
211
+ @click.argument("uid")
212
+ @click.option("--kinds", default="")
213
+ @click.option("--limit", default=100, type=int)
214
+ @click.option("--pretty", is_flag=True)
215
+ def graph_references(uid, kinds, limit, pretty):
216
+ """Inbound edges — who references this."""
217
+ _, tools = _open_backend()
218
+ # Empty --kinds → None so the tool auto-picks the right default edge
219
+ # kinds PER node kind (class→constructs/inherits, fn→calls/imports).
220
+ # A hardcoded CSV default returned 0 for class nodes (MCP parity bug).
221
+ kset = tuple(k.strip() for k in kinds.split(",") if k.strip())
222
+ _json_echo(
223
+ tools.cos_graph_references(
224
+ uid,
225
+ kinds=kset or None,
226
+ limit=limit,
227
+ ),
228
+ pretty=pretty,
229
+ )
230
+
231
+ @cli.command(name="graph-path")
232
+ @click.argument("source")
233
+ @click.argument("target")
234
+ @click.option("--max-hops", default=5, type=int)
235
+ @click.option("--pretty", is_flag=True)
236
+ def graph_path(source, target, max_hops, pretty):
237
+ """Shortest path between two nodes."""
238
+ _, tools = _open_backend()
239
+ _json_echo(
240
+ tools.cos_graph_path(source, target, max_hops=max_hops),
241
+ pretty=pretty,
242
+ )
243
+
244
+ @cli.command(name="graph-trace")
245
+ @click.argument("entry_uid")
246
+ @click.option("--max-steps", default=50, type=int)
247
+ @click.option("--pretty", is_flag=True)
248
+ def graph_trace(entry_uid, max_steps, pretty):
249
+ """Forward execution walk from an entry point."""
250
+ _, tools = _open_backend()
251
+ _json_echo(
252
+ tools.cos_graph_trace(entry_uid, max_steps=max_steps),
253
+ pretty=pretty,
254
+ )
255
+
256
+ @cli.command(name="graph-similar")
257
+ @click.argument("uid")
258
+ @click.option("--top-k", default=5, type=int)
259
+ @click.option("--confidence-min", default=0.5, type=float)
260
+ @click.option("--pretty", is_flag=True)
261
+ def graph_similar(uid, top_k, confidence_min, pretty):
262
+ """Semantic-similar nodes (difflib baseline; BGE-M3 later)."""
263
+ _, tools = _open_backend()
264
+ _json_echo(
265
+ tools.cos_graph_similar(uid, top_k=top_k, confidence_min=confidence_min),
266
+ pretty=pretty,
267
+ )
268
+
269
+ @cli.command(name="graph-contracts")
270
+ @click.option("--kind", default="http,mcp,grpc,event,websocket")
271
+ @click.option("--scope", default="all")
272
+ @click.option("--pretty", is_flag=True)
273
+ def graph_contracts(kind, scope, pretty):
274
+ """API surface — routes, MCP tools, gRPC, events, WS."""
275
+ _, tools = _open_backend()
276
+ _json_echo(
277
+ tools.cos_graph_contracts(
278
+ scope=scope,
279
+ kinds=tuple(k.strip() for k in kind.split(",") if k.strip()),
280
+ ),
281
+ pretty=pretty,
282
+ )
283
+
284
+ @cli.command(name="graph-rename-plan")
285
+ @click.argument("uid")
286
+ @click.argument("new_name")
287
+ @click.option("--no-strings", is_flag=True)
288
+ @click.option("--pretty", is_flag=True)
289
+ def graph_rename_plan(uid, new_name, no_strings, pretty):
290
+ """Produce a rename plan — call-sites, docs, tests, strings."""
291
+ _, tools = _open_backend()
292
+ _json_echo(
293
+ tools.cos_graph_rename_plan(uid, new_name, check_strings=not no_strings),
294
+ pretty=pretty,
295
+ )
296
+
297
+ @cli.command(name="graph-export")
298
+ @click.option("--format", "fmt", default="json", type=click.Choice(["json", "mermaid", "dot"]))
299
+ @click.option("--root-uid", default=None)
300
+ @click.option("--edge-types", default="")
301
+ @click.option("--max-nodes", default=500, type=int)
302
+ @click.option("--out", default=None, help="Write to file instead of stdout.")
303
+ def graph_export(fmt, root_uid, edge_types, max_nodes, out):
304
+ """Export a subgraph as json / mermaid / dot."""
305
+ _, tools = _open_backend()
306
+ envelope = tools.cos_graph_export(
307
+ format=fmt,
308
+ root_uid=root_uid,
309
+ edge_types=[e.strip() for e in edge_types.split(",") if e.strip()] or None,
310
+ max_nodes=max_nodes,
311
+ )
312
+ parsed = json.loads(envelope) if isinstance(envelope, str) else envelope
313
+ if not parsed.get("ok"):
314
+ raise click.ClickException(json.dumps(parsed))
315
+ data = parsed["data"]
316
+ if fmt == "json":
317
+ payload = json.dumps(
318
+ {"nodes": data["nodes"], "edges": data["edges"]},
319
+ indent=2,
320
+ default=str,
321
+ )
322
+ else:
323
+ payload = data["diagram"]
324
+ if out:
325
+ Path(out).write_text(payload, encoding="utf-8")
326
+ click.echo(f"[graph-export] wrote {out}")
327
+ else:
328
+ click.echo(payload)
329
+
330
+ @cli.command(name="graph-stats")
331
+ @click.option("--pretty", is_flag=True)
332
+ def graph_stats(pretty):
333
+ """Quick snapshot: node count, edge count, schema version."""
334
+ backend, _ = _open_backend()
335
+ _bootstrap_paths()
336
+ from database import get_db_stats # type: ignore
337
+
338
+ stats = get_db_stats(backend._conn)
339
+ report = {
340
+ "schema_version": stats["schema_version"],
341
+ "node_count": backend.count_nodes(),
342
+ "edge_count": backend.count_edges(),
343
+ "backend": backend.backend_id,
344
+ "db_size_bytes": stats.get("db_size_bytes"),
345
+ }
346
+ _json_echo(report, pretty=pretty)
347
+
348
+ @cli.command(name="graph-cycles")
349
+ @click.option("--scope", default="imports", type=click.Choice(["imports", "calls"]))
350
+ @click.option("--top", default=20, type=int)
351
+ @click.option("--min-size", default=2, type=int)
352
+ @click.option("--pretty", is_flag=True)
353
+ def graph_cycles(scope, top, min_size, pretty):
354
+ """Circular dependencies — strongly-connected components."""
355
+ _, tools = _open_backend()
356
+ _json_echo(
357
+ tools.cos_graph_cycles(scope=scope, top=top, min_size=min_size),
358
+ pretty=pretty,
359
+ )
360
+
361
+ @cli.command(name="graph-test-gap")
362
+ @click.option("--kind", default="")
363
+ @click.option("--top", default=50, type=int)
364
+ @click.option("--pretty", is_flag=True)
365
+ def graph_test_gap(kind, top, pretty):
366
+ """Untested symbols — prod code with zero inbound test edge."""
367
+ _, tools = _open_backend()
368
+ _json_echo(tools.cos_graph_test_gap(kind=kind, top=top), pretty=pretty)
369
+
370
+ @cli.command(name="graph-dead-code")
371
+ @click.option("--kind", default="")
372
+ @click.option("--top", default=50, type=int)
373
+ @click.option("--include-tests", is_flag=True)
374
+ @click.option("--pretty", is_flag=True)
375
+ def graph_dead_code(kind, top, include_tests, pretty):
376
+ """Dead-code candidates — in-repo symbols with no non-test caller."""
377
+ _, tools = _open_backend()
378
+ _json_echo(
379
+ tools.cos_graph_dead_code(kind=kind, top=top, include_tests=include_tests),
380
+ pretty=pretty,
381
+ )
382
+
383
+ @cli.command(name="graph-diff")
384
+ @click.option("--base", default="HEAD~1")
385
+ @click.option("--head", default="HEAD")
386
+ @click.option("--pretty", is_flag=True)
387
+ def graph_diff(base, head, pretty):
388
+ """Blast-radius of a git range — base..head changed symbols + downstream."""
389
+ _, tools = _open_backend()
390
+ _json_echo(tools.cos_graph_diff(base=base, head=head), pretty=pretty)
391
+
392
+ @cli.command(name="graph-resolve")
393
+ @click.argument("q")
394
+ @click.option("--kinds", default="")
395
+ @click.option("--top", default=10, type=int)
396
+ @click.option("--pretty", is_flag=True)
397
+ def graph_resolve(q, kinds, top, pretty):
398
+ """Resolve a label / path / partial to canonical uids."""
399
+ _, tools = _open_backend()
400
+ kset = tuple(k.strip() for k in kinds.split(",") if k.strip())
401
+ _json_echo(
402
+ tools.cos_graph_resolve(q, kinds=kset or None, top=top),
403
+ pretty=pretty,
404
+ )
405
+
406
+ @cli.command(name="graph-centrality")
407
+ @click.option("--metric", default="degree", type=click.Choice(["degree", "betweenness"]))
408
+ @click.option("--top", default=20, type=int)
409
+ @click.option("--kind", default="")
410
+ @click.option("--pretty", is_flag=True)
411
+ def graph_centrality(metric, top, kind, pretty):
412
+ """Hub / chokepoint nodes — degree or betweenness centrality."""
413
+ _, tools = _open_backend()
414
+ _json_echo(
415
+ tools.cos_graph_centrality(metric=metric, top=top, kind=kind or None),
416
+ pretty=pretty,
417
+ )
418
+
419
+ @cli.command(name="graph-ranking")
420
+ @click.option("--query", default="")
421
+ @click.option("--top", default=20, type=int)
422
+ @click.option("--kind", default="")
423
+ @click.option("--pretty", is_flag=True)
424
+ def graph_ranking(query, top, kind, pretty):
425
+ """PageRank importance — optionally personalised by query."""
426
+ _, tools = _open_backend()
427
+ _json_echo(
428
+ tools.cos_graph_ranking(query=query or None, top=top, kind=kind or None),
429
+ pretty=pretty,
430
+ )
431
+
432
+ @cli.command(name="graph-doctor")
433
+ @click.option("--fix", is_flag=True, help="Attempt safe repairs (delete dangling/stale).")
434
+ @click.option("--pretty", is_flag=True)
435
+ def graph_doctor(fix, pretty):
436
+ """Graph health — orphans, dangling edges, duplicates, backend status."""
437
+ _, tools = _open_backend()
438
+ _json_echo(tools.cos_graph_doctor(fix=fix), pretty=pretty)
439
+
440
+ @cli.command(name="graph-reindex")
441
+ @click.option("--path", default=None, help="Directory to reindex (default: repo root).")
442
+ @click.option("--no-docs", is_flag=True, help="Skip the docs RAG layer.")
443
+ @click.option(
444
+ "--max-files",
445
+ default=1_000_000,
446
+ type=int,
447
+ help="Cap on files walked (default 1M for monorepo-scale).",
448
+ )
449
+ @click.option(
450
+ "--workers",
451
+ "-j",
452
+ default=1,
453
+ type=int,
454
+ help="Parallel worker processes (default 1 = sequential). For "
455
+ "large monorepos, set to CPU count (e.g. -j 8). SQLite WAL + "
456
+ "the dispatcher's lock-retry loop handle concurrent writes.",
457
+ )
458
+ @click.option(
459
+ "--force",
460
+ "-f",
461
+ is_flag=True,
462
+ help="V1: bypass the file_index_state cache; reindex even "
463
+ "files whose content_hash matches the last successful run.",
464
+ )
465
+ @click.option(
466
+ "--status",
467
+ is_flag=True,
468
+ help="V1: print the top 50 most-recently-indexed files from "
469
+ "file_index_state and exit (debugging aid).",
470
+ )
471
+ @click.option(
472
+ "--rebuild-kinds",
473
+ is_flag=True,
474
+ help="S3: re-run the v16 kind-normalization data migration "
475
+ "without a full reindex. Useful after the NodeKind enum "
476
+ "ships to canonicalise legacy colon-prefixed kinds in place.",
477
+ )
478
+ @click.option(
479
+ "--extractor",
480
+ type=click.Choice(["auto", "legacy", "tree-sitter"], case_sensitive=False),
481
+ default="auto",
482
+ show_default=True,
483
+ help=(
484
+ "TASK-122 A/B flag: which parser ladder extractors should "
485
+ "use. 'legacy' forces ast/regex baselines; 'tree-sitter' "
486
+ "prefers tree-sitter when grammars are installed (lands "
487
+ "in TASK-119/120/121); 'auto' picks the current default. "
488
+ "Sets COS_EXTRACTOR_PREFERENCE for downstream extractors."
489
+ ),
490
+ )
491
+ @click.option(
492
+ "--prune-stale",
493
+ is_flag=True,
494
+ help=(
495
+ "W7.5 / R4-N7: delete nodes whose file_path no longer exists "
496
+ "on disk BEFORE reindexing. Reindex is idempotent-upsert so "
497
+ "moved/renamed/deleted files leave ghost nodes that bloat "
498
+ "the graph; this flag invokes cos_graph_doctor(fix=True) "
499
+ "to prune them first."
500
+ ),
501
+ )
502
+ def graph_reindex(
503
+ path,
504
+ no_docs,
505
+ max_files,
506
+ workers,
507
+ force,
508
+ status,
509
+ rebuild_kinds,
510
+ extractor,
511
+ prune_stale,
512
+ ):
513
+ """Walk a directory and rebuild the graph via the dispatcher."""
514
+ _bootstrap_paths()
515
+ # publish the chosen ladder via env so every spawned
516
+ # subprocess (incremental indexer, doc indexer, etc.) sees it.
517
+ # Existing call sites that bypass the CLI (e.g. PostToolUse
518
+ # auto-reindex) keep their default behaviour because the env
519
+ # var is unset when the CLI flag isn't passed.
520
+ os.environ["COS_EXTRACTOR_PREFERENCE"] = (extractor or "auto").lower()
521
+ if status:
522
+ _graph_reindex_print_status()
523
+ return
524
+ if rebuild_kinds:
525
+ # S3 data migration — idempotent; can be invoked standalone.
526
+ try:
527
+ import database # type: ignore
528
+
529
+ from graph_os.types import normalize_kind # type: ignore
530
+ except ImportError as exc:
531
+ click.echo(f"[graph-reindex] rebuild-kinds import failed: {exc}", err=True)
532
+ return
533
+ conn = database.get_connection()
534
+ try:
535
+ rows = conn.execute("SELECT DISTINCT kind FROM graph_nodes").fetchall()
536
+ renames: dict[str, str] = {}
537
+ for r in rows:
538
+ legacy = r[0]
539
+ if legacy is None:
540
+ continue
541
+ try:
542
+ canonical = normalize_kind(legacy).value
543
+ except ValueError:
544
+ continue
545
+ if canonical != legacy:
546
+ renames[legacy] = canonical
547
+ total = 0
548
+ for legacy, canonical in renames.items():
549
+ cur = conn.execute(
550
+ "UPDATE graph_nodes SET kind = ? WHERE kind = ?",
551
+ (canonical, legacy),
552
+ )
553
+ total += cur.rowcount or 0
554
+ conn.commit()
555
+ click.echo(
556
+ f"[graph-reindex] rebuild-kinds: "
557
+ f"{len(renames)} distinct kind(s) rewritten, "
558
+ f"{total} row(s) updated"
559
+ )
560
+ return
561
+ finally:
562
+ conn.close()
563
+
564
+ import time as _time
565
+
566
+ from graph_os.ingest import walk_local # type: ignore
567
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
568
+
569
+ # W7.5 / R4-N7: prune stale-path nodes before reindex when asked.
570
+ if prune_stale:
571
+ from graph_os.tools.graph import cos_graph_doctor # type: ignore
572
+
573
+ doc = cos_graph_doctor(fix=True)
574
+ # cos_graph_doctor returns a JSON string (FastMCP wire); parse
575
+ # it before reading nested fields.
576
+ if isinstance(doc, str):
577
+ doc = json.loads(doc)
578
+ fixed = doc.get("data", {}).get("meta", {}).get("fixed_count", 0)
579
+ click.echo(f"[graph-reindex] prune-stale: removed {fixed} stale node(s)")
580
+
581
+ target = Path(path or Path.cwd()).resolve()
582
+ # --path is the WALK directory, not the project root. Resolve the
583
+ # enclosing repo root (nearest ancestor with .coding-os/) so a
584
+ # sub-dir --path can't spawn a stray <subdir>/.coding-os/ DB or
585
+ # emit subdir-relative file_paths.
586
+ project_root = next(
587
+ (p for p in (target, *target.parents) if (p / ".coding-os").is_dir()),
588
+ target,
589
+ )
590
+ plan = walk_local(target, max_files=max_files)
591
+ click.echo(f"[graph-reindex] walking {target}; {len(plan.files)} files (force={force})")
592
+ # Surface oversized files dropped by the per-file byte cap — a skipped
593
+ # large source file is a coverage gap, not a no-op (TASK-293).
594
+ _oversize = plan.metadata.get("skipped_oversize") or []
595
+ if _oversize:
596
+ click.echo(
597
+ f"[graph-reindex] skipped {len(_oversize)} oversized file(s) "
598
+ f"(> COS_GRAPH_MAX_FILE_BYTES): {', '.join(_oversize[:5])}"
599
+ + (" …" if len(_oversize) > 5 else ""),
600
+ err=True,
601
+ )
602
+ # Symlink / unreadable skips — counts only, surfaced so they aren't
603
+ # silent (TASK-302). A symlink target is indexed on its own pass, so
604
+ # a non-zero symlink count is informational, not an error.
605
+ _sym = int(plan.metadata.get("skipped_symlink") or 0)
606
+ _rerr = int(plan.metadata.get("skipped_read_error") or 0)
607
+ if _sym or _rerr:
608
+ click.echo(
609
+ f"[graph-reindex] skipped {_sym} symlink(s), {_rerr} unreadable file(s)",
610
+ err=True,
611
+ )
612
+ processed = skipped = errors = lock_streak = 0
613
+ failed_paths: list[str] = []
614
+ started = _time.monotonic()
615
+ # Circuit breaker: a write lock held by another process makes EVERY
616
+ # file fail after its bounded busy-wait (~5s × 3 retries). Without
617
+ # this, an 800-file walk grinds silently for an hour — the per-file
618
+ # failure used to land only in layers.graph.status, which this loop
619
+ # counted as PROCESSED (2026-06-11 stall, TASK-394/395). The streak
620
+ # resets on any success so a transient start-of-walk lock storm
621
+ # (workers warming up alongside hub/MCP writers) doesn't abort a
622
+ # run that is actually progressing.
623
+ _LOCK_STREAK_ABORT = 10
624
+
625
+ def _record(report: dict) -> None:
626
+ nonlocal processed, skipped, errors, lock_streak
627
+ cache = report.get("cache")
628
+ if cache == "hit":
629
+ skipped += 1
630
+ return
631
+ processed += 1
632
+ reason = _report_failure_reason(report)
633
+ if reason is None:
634
+ lock_streak = 0
635
+ return
636
+ errors += 1
637
+ failed_paths.append(str(report.get("path") or ""))
638
+ if errors <= 5:
639
+ click.echo(f"[graph-reindex] ! {report.get('path')}: {reason}", err=True)
640
+ if _is_lock_shaped(reason):
641
+ lock_streak += 1
642
+ if lock_streak >= _LOCK_STREAK_ABORT:
643
+ raise click.ClickException(
644
+ f"aborting: {lock_streak} consecutive lock-shaped graph write "
645
+ "failures — another process is holding the DB write lock; "
646
+ "close it and re-run"
647
+ )
648
+
649
+ # Live per-file progress bar — click.progressbar auto-hides when
650
+ # stdout is not a TTY (pipes / CI), so non-interactive runs keep
651
+ # the summary-only output. Replaces the per-file cache-hit echo.
652
+ bar = click.progressbar(length=len(plan.files), label="[graph-reindex] indexing")
653
+ if workers and workers > 1:
654
+ # ProcessPoolExecutor parallelism for monorepo-scale walks. Each
655
+ # worker opens its own SQLite connection via init_db() inside
656
+ # dispatch(); WAL mode + the dispatcher's busy-retry loop handle
657
+ # concurrent writers.
658
+ from concurrent.futures import ProcessPoolExecutor, as_completed
659
+
660
+ click.echo(f"[graph-reindex] parallel workers={workers}")
661
+ futures = {}
662
+ with ProcessPoolExecutor(max_workers=workers) as pool, bar:
663
+ for file_path in plan.files:
664
+ fut = pool.submit(
665
+ _parallel_dispatch,
666
+ str(file_path),
667
+ str(project_root),
668
+ not no_docs,
669
+ force,
670
+ )
671
+ futures[fut] = file_path
672
+ for fut in as_completed(futures):
673
+ file_path = futures[fut]
674
+ try:
675
+ report = fut.result()
676
+ _record(report)
677
+ except click.ClickException:
678
+ # Circuit breaker tripped — drop queued work NOW;
679
+ # the context manager's shutdown(wait=True) would
680
+ # otherwise grind through every pending future.
681
+ pool.shutdown(wait=False, cancel_futures=True)
682
+ raise
683
+ except Exception as exc:
684
+ errors += 1
685
+ failed_paths.append(str(file_path))
686
+ click.echo(f"[graph-reindex] ! {file_path}: {exc}", err=True)
687
+ bar.update(1)
688
+ else:
689
+ with bar:
690
+ for file_path in plan.files:
691
+ try:
692
+ report = dispatch(
693
+ file_path,
694
+ project_root=project_root,
695
+ include_docs=not no_docs,
696
+ force=force,
697
+ link_stubs=False, # global link after the walk
698
+ )
699
+ _record(report)
700
+ except click.ClickException:
701
+ raise
702
+ except Exception as exc:
703
+ errors += 1
704
+ failed_paths.append(str(file_path))
705
+ click.echo(f"[graph-reindex] ! {file_path}: {exc}", err=True)
706
+ bar.update(1)
707
+
708
+ # Writers serialize in SQLite, so parallel workers can drop a few
709
+ # files to lock contention (their hash is NOT advanced). Retry the
710
+ # casualties sequentially — no sibling contention — so a parallel
711
+ # walk converges to the same zero-error result as a sequential one.
712
+ if failed_paths:
713
+ recovered = 0
714
+ for rel in [p for p in failed_paths if p]:
715
+ try:
716
+ report = dispatch(
717
+ project_root / rel,
718
+ project_root=project_root,
719
+ include_docs=not no_docs,
720
+ force=force,
721
+ link_stubs=False,
722
+ )
723
+ if _report_failure_reason(report) is None:
724
+ recovered += 1
725
+ except Exception as exc:
726
+ click.echo(f"[graph-reindex] ! retry {rel}: {exc}", err=True)
727
+ errors -= recovered
728
+ click.echo(f"[graph-reindex] retry pass: {recovered}/{len(failed_paths)} recovered")
729
+ duration = _time.monotonic() - started
730
+
731
+ # Reconcile the graph to the current walk BEFORE reporting: file_index_state
732
+ # rows (and their nodes/edges) for files no longer indexed — deleted from
733
+ # disk OR now excluded by .gitignore — are stale and make cos_graph_doctor
734
+ # over-count. GC them first so the parse-error summary below reads the
735
+ # reconciled (exact) state. ONLY on a full, uncapped repo walk
736
+ # (target == project_root); a --path sub-walk or a max_files-capped walk
737
+ # would wrongly flag every other file as stale (TASK-302).
738
+ if target == project_root and len(plan.files) < max_files:
739
+ try:
740
+ from database import init_db, resolve_db_path # type: ignore
741
+
742
+ gc_conn = init_db(str(resolve_db_path(project_root)))
743
+ walked = {p.relative_to(project_root).as_posix() for p in plan.files}
744
+ stale = [
745
+ r[0]
746
+ for r in gc_conn.execute(
747
+ "SELECT DISTINCT file_path FROM file_index_state"
748
+ ).fetchall()
749
+ if r[0] and r[0] not in walked
750
+ ]
751
+ gc_nodes = 0
752
+ for sp in stale:
753
+ gc_conn.execute(
754
+ "DELETE FROM graph_edges_v12 WHERE source_id IN "
755
+ "(SELECT id FROM graph_nodes WHERE file_path=?) "
756
+ "OR target_id IN (SELECT id FROM graph_nodes WHERE file_path=?)",
757
+ (sp, sp),
758
+ )
759
+ cur = gc_conn.execute("DELETE FROM graph_nodes WHERE file_path=?", (sp,))
760
+ gc_nodes += cur.rowcount or 0
761
+ gc_conn.execute("DELETE FROM file_index_state WHERE file_path=?", (sp,))
762
+ if stale:
763
+ gc_conn.commit()
764
+ click.echo(
765
+ f"[graph-reindex] reconcile: pruned {len(stale)} no-longer-indexed "
766
+ f"file(s), {gc_nodes} node(s)"
767
+ )
768
+ except Exception as exc:
769
+ click.echo(f"[graph-reindex] reconcile skipped: {exc}", err=True)
770
+
771
+ # Surface partial-extraction coverage gaps (some symbols dropped on a
772
+ # parse error) — "errors" only counts hard exceptions, so this was
773
+ # silent (TASK-293). Read the cumulative truth from file_index_state,
774
+ # the same source cos_graph_doctor reports, so the two always agree.
775
+ parse_err_total = parse_err_files = 0
776
+ try:
777
+ from database import init_db, resolve_db_path # type: ignore
778
+
779
+ _pe_conn = init_db(str(resolve_db_path(project_root)))
780
+ _pe_row = _pe_conn.execute(
781
+ "SELECT COALESCE(SUM(parse_errors_count), 0), COUNT(DISTINCT file_path) "
782
+ "FROM file_index_state WHERE parse_errors_count > 0"
783
+ ).fetchone()
784
+ parse_err_total = int(_pe_row[0] or 0)
785
+ parse_err_files = int(_pe_row[1] or 0)
786
+ except Exception as exc: # fail-open — never block the summary
787
+ import logging
788
+
789
+ logging.getLogger("graph_os.cli").debug("parse-error summary probe skipped: %s", exc)
790
+ click.echo(
791
+ f"[graph-reindex] processed={processed} skipped={skipped} "
792
+ f"errors={errors} parse_errors={parse_err_total} in {parse_err_files} files "
793
+ f"duration={duration:.2f}s"
794
+ )
795
+
796
+ # per-file linking during the walk cannot resolve a stub
797
+ # whose real target is indexed LATER (file-order dependency), so
798
+ # cross-module bare-name calls (ok()/fail()/...) leak to external
799
+ # stubs and references/impact under-report callers. Run the GLOBAL
800
+ # linker once now that every node exists — same precise module→file
801
+ # matching as the per-file pass, just applied across the whole graph.
802
+ try:
803
+ _bootstrap_paths()
804
+ from database import init_db, resolve_db_path # type: ignore
805
+
806
+ from graph_os.backends.sqlite_backend import SqliteBackend # type: ignore
807
+
808
+ # Link the same DB the walk indexed — the repo-root DB, NOT a
809
+ # <target>/.coding-os/ stray when --path points at a sub-dir.
810
+ conn = init_db(str(resolve_db_path(project_root)))
811
+ backend = SqliteBackend(conn=conn)
812
+ relinked = backend.link_external_stubs()
813
+ imports_linked = backend.link_import_bindings()
814
+ php_linked = backend.link_php_handlers()
815
+ click.echo(
816
+ f"[graph-reindex] cross-file link: {relinked} stub(s) resolved, "
817
+ f"{imports_linked} import binding(s)"
818
+ + (f" (+{php_linked} php handler(s))" if php_linked else "")
819
+ )
820
+ except Exception as exc:
821
+ click.echo(f"[graph-reindex] cross-file link skipped: {exc}", err=True)
822
+
823
+ # Residue sweep — the file reconcile above reads file_index_state,
824
+ # which holds file rows only, so folder-spine nodes and zero-edge
825
+ # phantoms (a folder is not a file row; a phantom carries a NULL /
826
+ # off-tree file_path) survive it. A bulk `git mv` / `rm -rf` of a
827
+ # directory fires no per-file deletion-prune, so only this
828
+ # authoritative full-walk pass clears the old-path folder/phantom
829
+ # residue. Runs AFTER the global link so a live external stub already
830
+ # holds its edges and is never swept; the doctor safe-repair deletes
831
+ # only absent-on-disk paths and zero-edge orphans, so an on-disk
832
+ # `src/`-prefixed node with `contains` edges survives. Same full-walk
833
+ # guard as the file reconcile — a sub-walk / capped walk is not
834
+ # authoritative.
835
+ if target == project_root and len(plan.files) < max_files:
836
+ try:
837
+ from graph_os.tools.graph import cos_graph_doctor # type: ignore
838
+
839
+ swept = cos_graph_doctor(fix=True)
840
+ if isinstance(swept, str):
841
+ swept = json.loads(swept)
842
+ swept_n = swept.get("data", {}).get("meta", {}).get("fixed_count", 0)
843
+ if swept_n:
844
+ click.echo(
845
+ f"[graph-reindex] reconcile-sweep: pruned {swept_n} "
846
+ "residual folder/phantom node(s)"
847
+ )
848
+ except Exception as exc:
849
+ click.echo(f"[graph-reindex] reconcile-sweep skipped: {exc}", err=True)
850
+
851
+ @cli.command(name="graph-index-local")
852
+ @click.argument("path")
853
+ @click.option("--alias", default=None)
854
+ @click.option("--max-files", default=50_000, type=int)
855
+ @click.option("--max-size-mb", default=500, type=int)
856
+ def graph_index_local(path, alias, max_files, max_size_mb):
857
+ """Index a local folder (outside the current repo)."""
858
+ _bootstrap_paths()
859
+ from graph_os.ingest import walk_local # type: ignore
860
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
861
+
862
+ target = Path(path).expanduser().resolve()
863
+ plan = walk_local(
864
+ target,
865
+ alias=alias,
866
+ max_files=max_files,
867
+ max_size_bytes=max_size_mb * 1024 * 1024,
868
+ )
869
+ click.echo(f"[local] alias={plan.alias} files={len(plan.files)}")
870
+ indexed = 0
871
+ with click.progressbar(plan.files, label="[local] indexing") as bar:
872
+ for file_path in bar:
873
+ report = dispatch(file_path, project_root=target, include_docs=True)
874
+ if report.get("status") == "ok":
875
+ indexed += 1
876
+ click.echo(f"[local] indexed {indexed}/{len(plan.files)}")
877
+
878
+ @cli.command(name="graph-index-github")
879
+ @click.argument("url")
880
+ @click.option("--branch", default=None)
881
+ @click.option("--alias", default=None)
882
+ @click.option("--auth", default=None, help="Token for private repos (never logged).")
883
+ @click.option("--max-size-mb", default=500, type=int)
884
+ @click.option("--timeout", default=300, type=int)
885
+ def graph_index_github(url, branch, alias, auth, max_size_mb, timeout):
886
+ """Clone a public GitHub repo + index (shallow by default)."""
887
+ _bootstrap_paths()
888
+ from graph_os.ingest import GithubSize, clone_github # type: ignore
889
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
890
+
891
+ plan = clone_github(
892
+ url,
893
+ branch=branch,
894
+ alias=alias,
895
+ auth=auth,
896
+ size=GithubSize(
897
+ max_size_bytes=max_size_mb * 1024 * 1024,
898
+ timeout_seconds=timeout,
899
+ ),
900
+ )
901
+ click.echo(f"[github] alias={plan.alias} files={len(plan.files)}")
902
+ indexed = 0
903
+ with click.progressbar(plan.files, label="[github] indexing") as bar:
904
+ for file_path in bar:
905
+ report = dispatch(file_path, project_root=plan.root, include_docs=True)
906
+ if report.get("status") == "ok":
907
+ indexed += 1
908
+ click.echo(f"[github] indexed {indexed}/{len(plan.files)}")
909
+
910
+ @cli.command(name="graph-index-zip")
911
+ @click.argument("archive")
912
+ @click.option("--alias", default=None)
913
+ @click.option("--out-dir", default=None, help="Extraction root (default tmp).")
914
+ @click.option("--max-size-mb", default=500, type=int)
915
+ def graph_index_zip(archive, alias, out_dir, max_size_mb):
916
+ """Extract a ZIP archive with bomb protection + index."""
917
+ _bootstrap_paths()
918
+ from graph_os.ingest import ZipSize, extract_zip # type: ignore
919
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
920
+
921
+ out = Path(out_dir) if out_dir else Path(tempfile.mkdtemp(prefix="cos-zip-"))
922
+ plan = extract_zip(
923
+ archive,
924
+ alias=alias,
925
+ out_dir=out,
926
+ size=ZipSize(max_size_bytes=max_size_mb * 1024 * 1024),
927
+ )
928
+ click.echo(f"[zip] alias={plan.alias} files={len(plan.files)}")
929
+ indexed = 0
930
+ with click.progressbar(plan.files, label="[zip] indexing") as bar:
931
+ for file_path in bar:
932
+ report = dispatch(file_path, project_root=plan.root, include_docs=True)
933
+ if report.get("status") == "ok":
934
+ indexed += 1
935
+ click.echo(f"[zip] indexed {indexed}/{len(plan.files)}")
936
+
937
+ @cli.command(name="graph-detect-changes")
938
+ @click.option(
939
+ "--staged",
940
+ "mode",
941
+ flag_value="staged",
942
+ help="Diff staged changes (git diff --cached --name-only).",
943
+ )
944
+ @click.option(
945
+ "--working",
946
+ "mode",
947
+ flag_value="working",
948
+ default=True,
949
+ help="Diff working-tree changes (git diff --name-only). [default]",
950
+ )
951
+ @click.option(
952
+ "--range",
953
+ "git_range",
954
+ default=None,
955
+ metavar="RANGE",
956
+ help="Diff a commit range, e.g. HEAD~1..HEAD (git diff --name-only RANGE).",
957
+ )
958
+ @click.option("--pretty", is_flag=True)
959
+ def graph_detect_changes(mode, git_range, pretty):
960
+ """Map changed files to affected graph symbols + downstream tasks."""
961
+ import subprocess
962
+
963
+ # Build the git command based on selected mode.
964
+ if git_range:
965
+ scope = git_range
966
+ git_cmd = ["git", "diff", "--name-only", git_range]
967
+ elif mode == "staged":
968
+ scope = "staged"
969
+ git_cmd = ["git", "diff", "--cached", "--name-only"]
970
+ else:
971
+ scope = "working"
972
+ git_cmd = ["git", "diff", "--name-only"]
973
+
974
+ try:
975
+ result = subprocess.run(
976
+ git_cmd,
977
+ capture_output=True,
978
+ text=True,
979
+ timeout=30,
980
+ )
981
+ if result.returncode != 0:
982
+ raise click.ClickException(
983
+ f"git exited {result.returncode}: {result.stderr.strip()}"
984
+ )
985
+ files = [line.strip() for line in result.stdout.splitlines() if line.strip()]
986
+ except FileNotFoundError:
987
+ raise click.ClickException("git not found on PATH")
988
+ except subprocess.TimeoutExpired:
989
+ raise click.ClickException("git diff timed out after 30 s")
990
+
991
+ _, tools = _open_backend()
992
+ _json_echo(
993
+ tools.cos_graph_detect_changes(
994
+ scope=scope,
995
+ files=files or None,
996
+ analyze_downstream=True,
997
+ ),
998
+ pretty=pretty,
999
+ )
1000
+
1001
+ @cli.command(name="graph-communities")
1002
+ @click.option("--top", default=20, show_default=True, type=int)
1003
+ @click.option("--min-size", default=2, show_default=True, type=int)
1004
+ @click.option("--pretty", is_flag=True)
1005
+ def graph_communities(top, min_size, pretty):
1006
+ """Louvain process clusters (TASK-075).
1007
+
1008
+ Reads the indexed graph; computes Louvain communities over
1009
+ the call/import subgraph. Use to seed the Search tab grouping
1010
+ or to audit how the graph clusters into named processes.
1011
+ """
1012
+ _, tools = _open_backend()
1013
+ result = tools.cos_graph_communities(top=int(top), min_size=int(min_size))
1014
+ _json_echo(result, pretty=pretty)
1015
+
1016
+ @cli.command(name="graph-entrypoints")
1017
+ @click.option("--top", default=20, show_default=True, type=int)
1018
+ @click.option(
1019
+ "--kind",
1020
+ default="",
1021
+ help="Filter on entry_kind: main / cli / http / cron / test.",
1022
+ )
1023
+ @click.option("--min-score", default=0.05, show_default=True, type=float)
1024
+ @click.option("--pretty", is_flag=True)
1025
+ def graph_entrypoints(top, kind, min_score, pretty):
1026
+ """Top-N scored entry points (main / cli / http / cron / test) — TASK-081.
1027
+
1028
+ Reads the indexed graph; no file re-parsing. Use to seed
1029
+ cos_graph_trace, populate the Hub Graph tab "Start from entry
1030
+ point" panel, or sanity-check after a reindex.
1031
+ """
1032
+ _, tools = _open_backend()
1033
+ result = tools.cos_graph_entrypoints(
1034
+ top=int(top),
1035
+ kind=(kind or None),
1036
+ min_score=float(min_score),
1037
+ )
1038
+ _json_echo(result, pretty=pretty)
1039
+
1040
+ @cli.command(name="graph-viz")
1041
+ @click.option("--path", default=None)
1042
+ @click.option("--out", default=None, help="Output HTML path.")
1043
+ @click.option("--root-uid", default=None)
1044
+ @click.option("--title", default="graph_os")
1045
+ @click.option("--bundled", is_flag=True)
1046
+ @click.option("--serve", is_flag=True)
1047
+ @click.option("--port", default=0, type=int)
1048
+ @click.option("--open/--no-open", "open_browser", default=True)
1049
+ def graph_viz(path, out, root_uid, title, bundled, serve, port, open_browser):
1050
+ """Generate the HTML graph viewer and optionally open / serve it."""
1051
+ _bootstrap_paths()
1052
+ from graph_os.ingest import walk_local # type: ignore
1053
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
1054
+ from graph_os.viewer import build_view # type: ignore
1055
+
1056
+ backend, _ = _open_backend()
1057
+ if path:
1058
+ target = Path(path).expanduser().resolve()
1059
+ plan = walk_local(target)
1060
+ for file_path in plan.files:
1061
+ dispatch(file_path, project_root=target, include_docs=True)
1062
+
1063
+ out_path = Path(out or Path(".coding-os") / "graph-viz.html").resolve()
1064
+ build_view(backend, out_path, title=title, root_uid=root_uid, bundled=bundled)
1065
+ click.echo(f"[graph-viz] wrote {out_path}")
1066
+ if serve:
1067
+ _serve_static(out_path, port=port, open_browser=open_browser)
1068
+ elif open_browser:
1069
+ import webbrowser
1070
+
1071
+ webbrowser.open(out_path.as_uri())
1072
+
1073
+ # ── group family --------------------------------------------------
1074
+ @cli.group(name="graph-group")
1075
+ def graph_group():
1076
+ """Cross-repo group operations."""
1077
+
1078
+ @graph_group.command("create")
1079
+ @click.argument("name")
1080
+ @click.option(
1081
+ "--manifest-dir", default=None, help="Root for ~/.coding-os/groups/<name>/ overrides."
1082
+ )
1083
+ def group_create(name, manifest_dir):
1084
+ _bootstrap_paths()
1085
+ from graph_os.groups import GroupManifest, save_manifest # type: ignore
1086
+
1087
+ target = _group_manifest_path(name, manifest_dir)
1088
+ if target.exists():
1089
+ raise click.ClickException(f"group already exists at {target}")
1090
+ save_manifest(GroupManifest(name=name, members=[]), target)
1091
+ click.echo(f"[group] created {target}")
1092
+
1093
+ @graph_group.command("add")
1094
+ @click.argument("name")
1095
+ @click.argument("path")
1096
+ @click.option("--alias", default=None)
1097
+ @click.option("--owns-route", multiple=True)
1098
+ @click.option("--manifest-dir", default=None)
1099
+ def group_add(name, path, alias, owns_route, manifest_dir):
1100
+ _bootstrap_paths()
1101
+ from graph_os.groups import load_manifest, register_member, save_manifest # type: ignore
1102
+
1103
+ target = _group_manifest_path(name, manifest_dir)
1104
+ if not target.exists():
1105
+ raise click.ClickException("group missing; run `cos graph-group create` first")
1106
+ manifest = load_manifest(target)
1107
+ alias = alias or Path(path).expanduser().resolve().name
1108
+ manifest = register_member(
1109
+ manifest,
1110
+ alias=alias,
1111
+ path=str(Path(path).expanduser().resolve()),
1112
+ owned_routes=list(owns_route),
1113
+ )
1114
+ save_manifest(manifest, target)
1115
+ click.echo(f"[group] added {alias} to {name}")
1116
+
1117
+ @graph_group.command("list")
1118
+ @click.option("--manifest-dir", default=None)
1119
+ def group_list(manifest_dir):
1120
+ base = _group_root(manifest_dir)
1121
+ if not base.exists():
1122
+ click.echo("(no groups)")
1123
+ return
1124
+ for entry in sorted(base.iterdir()):
1125
+ if entry.is_dir() and (entry / "group.json").exists():
1126
+ click.echo(entry.name)
1127
+
1128
+ @graph_group.command("status")
1129
+ @click.argument("name")
1130
+ @click.option("--manifest-dir", default=None)
1131
+ def group_status(name, manifest_dir):
1132
+ _bootstrap_paths()
1133
+ from graph_os.groups import load_manifest # type: ignore
1134
+
1135
+ target = _group_manifest_path(name, manifest_dir)
1136
+ if not target.exists():
1137
+ raise click.ClickException("group missing")
1138
+ manifest = load_manifest(target)
1139
+ payload = {
1140
+ "name": manifest.name,
1141
+ "members": [
1142
+ {
1143
+ "alias": m.alias,
1144
+ "path": m.path,
1145
+ "exists": Path(m.path).exists(),
1146
+ "owned_routes": m.owned_routes,
1147
+ }
1148
+ for m in manifest.members
1149
+ ],
1150
+ }
1151
+ click.echo(json.dumps(payload, indent=2))
1152
+
1153
+ @graph_group.command("sync")
1154
+ @click.argument("name")
1155
+ @click.option("--manifest-dir", default=None)
1156
+ def group_sync(name, manifest_dir):
1157
+ _bootstrap_paths()
1158
+ from graph_os.groups import load_manifest # type: ignore
1159
+ from graph_os.ingest import walk_local # type: ignore
1160
+ from graph_os.tools.reindex_dispatch import dispatch # type: ignore
1161
+
1162
+ target = _group_manifest_path(name, manifest_dir)
1163
+ manifest = load_manifest(target)
1164
+ indexed = 0
1165
+ for member in manifest.members:
1166
+ root = Path(member.path)
1167
+ if not root.exists():
1168
+ click.echo(f"[sync] skip {member.alias} (missing: {root})", err=True)
1169
+ continue
1170
+ plan = walk_local(root)
1171
+ for file_path in plan.files:
1172
+ report = dispatch(file_path, project_root=root, include_docs=True)
1173
+ if report.get("status") == "ok":
1174
+ indexed += 1
1175
+ click.echo(f"[sync] {member.alias}: {len(plan.files)} files")
1176
+ click.echo(f"[sync] indexed {indexed} entries total")
1177
+
1178
+
1179
+ # ---------------------------------------------------------------------------
1180
+ # Helpers
1181
+ # ---------------------------------------------------------------------------
1182
+
1183
+
1184
+ def _group_root(manifest_dir: str | None) -> Path:
1185
+ return (
1186
+ Path(manifest_dir).expanduser() if manifest_dir else (Path.home() / ".coding-os" / "groups")
1187
+ )
1188
+
1189
+
1190
+ def _group_manifest_path(name: str, manifest_dir: str | None) -> Path:
1191
+ root = _group_root(manifest_dir)
1192
+ root.mkdir(parents=True, exist_ok=True)
1193
+ folder = root / name
1194
+ folder.mkdir(exist_ok=True)
1195
+ return folder / "group.json"
1196
+
1197
+
1198
+ def _graph_reindex_print_status() -> None:
1199
+ """V1 ``--status``: print top 50 most-recently-indexed file_index_state rows."""
1200
+ from datetime import datetime
1201
+
1202
+ _bootstrap_paths()
1203
+ try:
1204
+ import database # type: ignore
1205
+ except ImportError as exc:
1206
+ raise click.ClickException(f"thinking_os db import failed: {exc}") from exc
1207
+ conn = database.init_db()
1208
+ try:
1209
+ if not database.has_file_index_state_table(conn):
1210
+ click.echo(
1211
+ "[graph-reindex] file_index_state table missing (migration v17 not applied)."
1212
+ )
1213
+ return
1214
+ rows = conn.execute(
1215
+ "SELECT file_path, content_hash, extractor_chain, "
1216
+ "last_indexed_at, last_error FROM file_index_state "
1217
+ "ORDER BY last_indexed_at DESC LIMIT 50"
1218
+ ).fetchall()
1219
+ finally:
1220
+ conn.close()
1221
+
1222
+ if not rows:
1223
+ click.echo("[graph-reindex] file_index_state is empty.")
1224
+ return
1225
+
1226
+ click.echo(f"{'file_path':<60} {'hash':<12} {'indexed_at':<20} status")
1227
+ click.echo("-" * 110)
1228
+ for file_path, chash, chain, ts, err in rows:
1229
+ when = datetime.fromtimestamp(int(ts)).isoformat(timespec="seconds")
1230
+ status = "error" if err else "ok"
1231
+ chain_hint = chain[:20] + ("…" if len(chain) > 20 else "")
1232
+ display = f"{file_path} [{chain_hint}]"
1233
+ if len(display) > 60:
1234
+ display = display[:57] + "..."
1235
+ click.echo(f"{display:<60} {chash[:12]:<12} {when:<20} {status}")
1236
+
1237
+
1238
+ def _serve_static(path: Path, *, port: int, open_browser: bool) -> None:
1239
+ import socket
1240
+ import webbrowser
1241
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
1242
+
1243
+ os.chdir(path.parent)
1244
+
1245
+ class _Handler(SimpleHTTPRequestHandler):
1246
+ def log_message(self, *_args, **_kwargs):
1247
+ pass
1248
+
1249
+ if port == 0:
1250
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
1251
+ s.bind(("127.0.0.1", 0))
1252
+ port = s.getsockname()[1]
1253
+ httpd = HTTPServer(("127.0.0.1", port), _Handler)
1254
+ url = f"http://127.0.0.1:{port}/{path.name}"
1255
+ click.echo(f"[serve] {url}")
1256
+ if open_browser:
1257
+ webbrowser.open(url)
1258
+ try:
1259
+ httpd.serve_forever()
1260
+ except KeyboardInterrupt:
1261
+ httpd.shutdown()
1262
+ click.echo("\n[serve] bye")
1263
+
1264
+
1265
+ __all__ = ["register"]