source-kb 0.2.4__tar.gz → 0.2.26__tar.gz

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 (356) hide show
  1. source_kb-0.2.26/MANIFEST.in +15 -0
  2. {source_kb-0.2.4/source_kb.egg-info → source_kb-0.2.26}/PKG-INFO +15 -7
  3. {source_kb-0.2.4 → source_kb-0.2.26}/README.en.md +14 -6
  4. {source_kb-0.2.4 → source_kb-0.2.26}/README.md +15 -6
  5. {source_kb-0.2.4 → source_kb-0.2.26}/cli/__init__.py +21 -0
  6. {source_kb-0.2.4 → source_kb-0.2.26}/cli/__main__.py +1 -0
  7. source_kb-0.2.26/cli/commands/anchor_fix.py +126 -0
  8. source_kb-0.2.26/cli/commands/audit.py +53 -0
  9. source_kb-0.2.26/cli/commands/check_triggers.py +69 -0
  10. source_kb-0.2.26/cli/commands/detect.py +44 -0
  11. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/dispatch.py +18 -5
  12. source_kb-0.2.26/cli/commands/history_cmd.py +94 -0
  13. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/index.py +18 -6
  14. source_kb-0.2.26/cli/commands/install_skills.py +135 -0
  15. source_kb-0.2.26/cli/commands/merge.py +118 -0
  16. source_kb-0.2.26/cli/commands/merge_delta.py +49 -0
  17. source_kb-0.2.26/cli/commands/module_dag.py +39 -0
  18. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/post_merge.py +10 -0
  19. source_kb-0.2.26/cli/commands/query_preset.py +50 -0
  20. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/record_feedback.py +19 -2
  21. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/render.py +24 -5
  22. source_kb-0.2.26/cli/commands/report.py +48 -0
  23. source_kb-0.2.26/cli/commands/scan_repos.py +179 -0
  24. source_kb-0.2.26/cli/commands/score.py +149 -0
  25. source_kb-0.2.26/cli/commands/status.py +82 -0
  26. source_kb-0.2.26/cli/commands/validate.py +332 -0
  27. source_kb-0.2.26/cli/global_assets.py +59 -0
  28. source_kb-0.2.26/core/bootstrap.py +414 -0
  29. {source_kb-0.2.4 → source_kb-0.2.26}/core/docs/shared.py +114 -0
  30. source_kb-0.2.26/core/docs/trigger_detect.py +106 -0
  31. source_kb-0.2.26/core/history.py +438 -0
  32. {source_kb-0.2.4 → source_kb-0.2.26}/core/paths.py +15 -0
  33. {source_kb-0.2.4 → source_kb-0.2.26}/core/preset.py +6 -1
  34. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/__main__.py +19 -4
  35. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/variables.py +0 -1
  36. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/__main__.py +3 -6
  37. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/bm25_index.py +39 -8
  38. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/chunker.py +36 -3
  39. source_kb-0.2.26/core/scan_repos.py +414 -0
  40. source_kb-0.2.26/core/skeleton/__main__.py +453 -0
  41. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/anchor_fix.py +98 -0
  42. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/cmd_anchor_fix.py +5 -1
  43. source_kb-0.2.26/core/skeleton/cmd_cross_graph.py +76 -0
  44. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/cmd_diff_doc.py +5 -1
  45. source_kb-0.2.26/core/skeleton/cmd_dispatch_preview.py +74 -0
  46. source_kb-0.2.26/core/skeleton/cmd_extract.py +84 -0
  47. source_kb-0.2.26/core/skeleton/cmd_file_list.py +66 -0
  48. source_kb-0.2.26/core/skeleton/cmd_graph.py +65 -0
  49. source_kb-0.2.26/core/skeleton/cmd_impact.py +68 -0
  50. source_kb-0.2.26/core/skeleton/cmd_inject_version.py +57 -0
  51. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/cmd_lock.py +5 -1
  52. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/cmd_merge_delta.py +5 -1
  53. source_kb-0.2.26/core/skeleton/cmd_report.py +117 -0
  54. source_kb-0.2.26/core/skeleton/cmd_split_apply.py +120 -0
  55. source_kb-0.2.26/core/skeleton/cmd_split_files.py +169 -0
  56. source_kb-0.2.26/core/skeleton/cmd_stale_files.py +108 -0
  57. source_kb-0.2.26/core/skeleton/cross_module_graph.py +245 -0
  58. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/dispatch.py +25 -1
  59. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/extract.py +24 -6
  60. source_kb-0.2.26/core/skeleton/graph.py +298 -0
  61. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/impact.py +92 -2
  62. source_kb-0.2.26/core/skeleton/impact_cross_module.py +86 -0
  63. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/jqassistant.py +19 -3
  64. source_kb-0.2.26/core/skeleton/section_ops.py +237 -0
  65. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/__init__.py +1 -0
  66. source_kb-0.2.26/core/validators/density.py +215 -0
  67. source_kb-0.2.26/core/validators/score.py +285 -0
  68. source_kb-0.2.26/examples/README.md +97 -0
  69. source_kb-0.2.26/examples/__init__.py +0 -0
  70. source_kb-0.2.26/examples/monorepo-backend/kb-project.yaml +70 -0
  71. source_kb-0.2.26/examples/multi-module-ecommerce/kb-project.yaml +88 -0
  72. source_kb-0.2.26/examples/spring-petclinic/README.md +39 -0
  73. source_kb-0.2.26/examples/spring-petclinic/kb-project.yaml +71 -0
  74. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/doc_types.yaml +121 -1
  75. source_kb-0.2.26/presets/java-spring/templates/subagent-data-flow.md +105 -0
  76. source_kb-0.2.26/presets/java-spring/templates/subagent-deployment.md +143 -0
  77. source_kb-0.2.26/presets/java-spring/templates/subagent-extension-points.md +98 -0
  78. source_kb-0.2.26/presets/java-spring/templates/subagent-public-api.md +119 -0
  79. source_kb-0.2.26/presets/java-spring/templates/subagent-rate-limiting.md +103 -0
  80. source_kb-0.2.26/presets/java-spring/templates/subagent-routing.md +101 -0
  81. source_kb-0.2.26/presets/java-spring/templates/subagent-runbook.md +137 -0
  82. source_kb-0.2.26/presets/java-spring/templates/subagent-scheduling.md +106 -0
  83. source_kb-0.2.26/presets/java-spring/templates/subagent-starters.md +109 -0
  84. {source_kb-0.2.4 → source_kb-0.2.26}/pyproject.toml +5 -2
  85. {source_kb-0.2.4 → source_kb-0.2.26/source_kb.egg-info}/PKG-INFO +15 -7
  86. {source_kb-0.2.4 → source_kb-0.2.26}/source_kb.egg-info/SOURCES.txt +46 -142
  87. source_kb-0.2.26/source_kb.egg-info/top_level.txt +6 -0
  88. source_kb-0.2.4/cli/commands/anchor_fix.py +0 -47
  89. source_kb-0.2.4/cli/commands/audit.py +0 -18
  90. source_kb-0.2.4/cli/commands/merge.py +0 -60
  91. source_kb-0.2.4/cli/commands/merge_delta.py +0 -19
  92. source_kb-0.2.4/cli/commands/module_dag.py +0 -17
  93. source_kb-0.2.4/cli/commands/scan_repos.py +0 -44
  94. source_kb-0.2.4/cli/commands/validate.py +0 -191
  95. source_kb-0.2.4/core/scan_repos.py +0 -664
  96. source_kb-0.2.4/core/skeleton/__main__.py +0 -934
  97. source_kb-0.2.4/engine/__init__.py +0 -7
  98. source_kb-0.2.4/engine/assembler.py +0 -231
  99. source_kb-0.2.4/engine/confirm.py +0 -65
  100. source_kb-0.2.4/engine/dedup.py +0 -106
  101. source_kb-0.2.4/engine/main.py +0 -211
  102. source_kb-0.2.4/engine/pipeline/__init__.py +0 -163
  103. source_kb-0.2.4/engine/pipeline/recovery.py +0 -250
  104. source_kb-0.2.4/engine/pipeline/steps/__init__.py +0 -23
  105. source_kb-0.2.4/engine/pipeline/steps/audit.py +0 -220
  106. source_kb-0.2.4/engine/pipeline/steps/audit_apply.py +0 -195
  107. source_kb-0.2.4/engine/pipeline/steps/audit_helpers.py +0 -155
  108. source_kb-0.2.4/engine/pipeline/steps/classify_llm.py +0 -236
  109. source_kb-0.2.4/engine/pipeline/steps/classify_prompt.py +0 -223
  110. source_kb-0.2.4/engine/pipeline/steps/finalize.py +0 -160
  111. source_kb-0.2.4/engine/pipeline/steps/generate.py +0 -169
  112. source_kb-0.2.4/engine/pipeline/steps/generate_batch.py +0 -197
  113. source_kb-0.2.4/engine/pipeline/steps/generate_recovery.py +0 -170
  114. source_kb-0.2.4/engine/pipeline/steps/llm_plan_split.py +0 -253
  115. source_kb-0.2.4/engine/pipeline/steps/lock.py +0 -64
  116. source_kb-0.2.4/engine/pipeline/steps/preflight.py +0 -237
  117. source_kb-0.2.4/engine/pipeline/steps/preflight_adjust.py +0 -147
  118. source_kb-0.2.4/engine/pipeline/steps/pregenerate.py +0 -130
  119. source_kb-0.2.4/engine/pipeline/steps/quality.py +0 -81
  120. source_kb-0.2.4/engine/pipeline/steps/skeleton.py +0 -149
  121. source_kb-0.2.4/engine/pipeline/steps/source.py +0 -163
  122. source_kb-0.2.4/engine/pipeline/steps/sync.py +0 -117
  123. source_kb-0.2.4/engine/pipeline/steps/sync_finalize.py +0 -237
  124. source_kb-0.2.4/engine/pipeline/steps/sync_update.py +0 -341
  125. source_kb-0.2.4/engine/pipelines.py +0 -91
  126. source_kb-0.2.4/engine/runner.py +0 -335
  127. source_kb-0.2.4/engine/strategies/__init__.py +0 -86
  128. source_kb-0.2.4/engine/strategies/api.py +0 -128
  129. source_kb-0.2.4/engine/strategies/delegated.py +0 -50
  130. source_kb-0.2.4/engine/strategies/dryrun.py +0 -25
  131. source_kb-0.2.4/engine/two_phase.py +0 -143
  132. source_kb-0.2.4/mcp_server/__init__.py +0 -73
  133. source_kb-0.2.4/mcp_server/__main__.py +0 -5
  134. source_kb-0.2.4/mcp_server/tools/__init__.py +0 -1
  135. source_kb-0.2.4/mcp_server/tools/config.py +0 -63
  136. source_kb-0.2.4/mcp_server/tools/discovery.py +0 -276
  137. source_kb-0.2.4/mcp_server/tools/generation.py +0 -184
  138. source_kb-0.2.4/mcp_server/tools/planning.py +0 -144
  139. source_kb-0.2.4/mcp_server/tools/source.py +0 -175
  140. source_kb-0.2.4/mcp_server/tools/validation.py +0 -140
  141. source_kb-0.2.4/mcp_server/tools/workflow.py +0 -166
  142. source_kb-0.2.4/mcp_server/workflow_loader.py +0 -204
  143. source_kb-0.2.4/skills/__init__.py +0 -1
  144. source_kb-0.2.4/skills/_shared/README.md +0 -30
  145. source_kb-0.2.4/skills/_shared/doc-coverage-shared.md +0 -134
  146. source_kb-0.2.4/skills/_shared/doc-quality-standard.md +0 -1058
  147. source_kb-0.2.4/skills/_shared/doc-subagent-rules.md +0 -762
  148. source_kb-0.2.4/skills/_shared/windows-compat.md +0 -89
  149. source_kb-0.2.4/skills/kb-audit/SKILL.md +0 -52
  150. source_kb-0.2.4/skills/kb-audit/rules.md +0 -88
  151. source_kb-0.2.4/skills/kb-audit/steps/step-01-prepare.md +0 -75
  152. source_kb-0.2.4/skills/kb-audit/steps/step-02-audit.md +0 -96
  153. source_kb-0.2.4/skills/kb-audit/steps/step-03-verify.md +0 -65
  154. source_kb-0.2.4/skills/kb-audit/steps/step-04-report.md +0 -64
  155. source_kb-0.2.4/skills/kb-init/SKILL.md +0 -142
  156. source_kb-0.2.4/skills/kb-init/rules.md +0 -187
  157. source_kb-0.2.4/skills/kb-init/steps/step-01-scope.md +0 -62
  158. source_kb-0.2.4/skills/kb-init/steps/step-02-source.md +0 -410
  159. source_kb-0.2.4/skills/kb-init/steps/step-03-generate.md +0 -279
  160. source_kb-0.2.4/skills/kb-init/steps/step-04-quality.md +0 -92
  161. source_kb-0.2.4/skills/kb-init/steps/step-05-finalize.md +0 -132
  162. source_kb-0.2.4/skills/kb-init/templates/core/execution-modes.md +0 -29
  163. source_kb-0.2.4/skills/kb-init/templates/core/output-only.md +0 -4
  164. source_kb-0.2.4/skills/kb-init/templates/core/readwrite.md +0 -33
  165. source_kb-0.2.4/skills/kb-search/SKILL.md +0 -138
  166. source_kb-0.2.4/skills/kb-search/rules.md +0 -64
  167. source_kb-0.2.4/skills/kb-sync/SKILL.md +0 -43
  168. source_kb-0.2.4/skills/kb-sync/rules.md +0 -70
  169. source_kb-0.2.4/skills/kb-sync/steps/step-01-detect.md +0 -72
  170. source_kb-0.2.4/skills/kb-sync/steps/step-02-update.md +0 -71
  171. source_kb-0.2.4/skills/kb-sync/steps/step-03-verify.md +0 -47
  172. source_kb-0.2.4/skills/kb-sync/steps/step-04-finalize.md +0 -52
  173. source_kb-0.2.4/source_kb.egg-info/top_level.txt +0 -3
  174. source_kb-0.2.4/tests/test_anchor_fix.py +0 -133
  175. source_kb-0.2.4/tests/test_assembler.py +0 -144
  176. source_kb-0.2.4/tests/test_bm25_index.py +0 -174
  177. source_kb-0.2.4/tests/test_classify.py +0 -94
  178. source_kb-0.2.4/tests/test_classify_files.py +0 -142
  179. source_kb-0.2.4/tests/test_cli_commands.py +0 -73
  180. source_kb-0.2.4/tests/test_cli_runner.py +0 -116
  181. source_kb-0.2.4/tests/test_cli_strategies.py +0 -176
  182. source_kb-0.2.4/tests/test_cli_sync_steps.py +0 -371
  183. source_kb-0.2.4/tests/test_community.py +0 -155
  184. source_kb-0.2.4/tests/test_config.py +0 -219
  185. source_kb-0.2.4/tests/test_context_manager.py +0 -107
  186. source_kb-0.2.4/tests/test_core_config.py +0 -231
  187. source_kb-0.2.4/tests/test_core_remaining.py +0 -174
  188. source_kb-0.2.4/tests/test_dependency_graph.py +0 -151
  189. source_kb-0.2.4/tests/test_diff_doc.py +0 -106
  190. source_kb-0.2.4/tests/test_dispatch.py +0 -62
  191. source_kb-0.2.4/tests/test_dispatch_render.py +0 -183
  192. source_kb-0.2.4/tests/test_e2e_agent.py +0 -433
  193. source_kb-0.2.4/tests/test_e2e_engine.py +0 -385
  194. source_kb-0.2.4/tests/test_engine_pipeline_full.py +0 -168
  195. source_kb-0.2.4/tests/test_engine_remaining.py +0 -173
  196. source_kb-0.2.4/tests/test_engine_steps_batch.py +0 -306
  197. source_kb-0.2.4/tests/test_generation.py +0 -144
  198. source_kb-0.2.4/tests/test_git.py +0 -83
  199. source_kb-0.2.4/tests/test_impact.py +0 -310
  200. source_kb-0.2.4/tests/test_mcp_config.py +0 -58
  201. source_kb-0.2.4/tests/test_mcp_discovery.py +0 -182
  202. source_kb-0.2.4/tests/test_mcp_planning.py +0 -103
  203. source_kb-0.2.4/tests/test_mcp_source.py +0 -179
  204. source_kb-0.2.4/tests/test_mcp_validation.py +0 -174
  205. source_kb-0.2.4/tests/test_mcp_workflow.py +0 -100
  206. source_kb-0.2.4/tests/test_merge.py +0 -191
  207. source_kb-0.2.4/tests/test_merge_delta.py +0 -170
  208. source_kb-0.2.4/tests/test_module_dag.py +0 -144
  209. source_kb-0.2.4/tests/test_phase1_quick.py +0 -167
  210. source_kb-0.2.4/tests/test_pipeline_infra.py +0 -216
  211. source_kb-0.2.4/tests/test_post_merge.py +0 -62
  212. source_kb-0.2.4/tests/test_preset.py +0 -81
  213. source_kb-0.2.4/tests/test_progress.py +0 -77
  214. source_kb-0.2.4/tests/test_prompt_assembler.py +0 -84
  215. source_kb-0.2.4/tests/test_rag_chunker.py +0 -95
  216. source_kb-0.2.4/tests/test_rag_indexer.py +0 -100
  217. source_kb-0.2.4/tests/test_rag_loader.py +0 -91
  218. source_kb-0.2.4/tests/test_recovery.py +0 -158
  219. source_kb-0.2.4/tests/test_regex_parser.py +0 -204
  220. source_kb-0.2.4/tests/test_response_parser_quick.py +0 -69
  221. source_kb-0.2.4/tests/test_shard_context.py +0 -157
  222. source_kb-0.2.4/tests/test_skeleton_main_cmds.py +0 -194
  223. source_kb-0.2.4/tests/test_skeleton_query.py +0 -172
  224. source_kb-0.2.4/tests/test_split.py +0 -73
  225. source_kb-0.2.4/tests/test_split_cache.py +0 -153
  226. source_kb-0.2.4/tests/test_split_feedback.py +0 -57
  227. source_kb-0.2.4/tests/test_split_plan.py +0 -213
  228. source_kb-0.2.4/tests/test_templates.py +0 -117
  229. source_kb-0.2.4/tests/test_utils.py +0 -80
  230. source_kb-0.2.4/tests/test_validate_parity.py +0 -120
  231. source_kb-0.2.4/tests/test_validators_consistency.py +0 -116
  232. source_kb-0.2.4/tests/test_validators_coverage.py +0 -176
  233. source_kb-0.2.4/tests/test_validators_duplicates.py +0 -62
  234. source_kb-0.2.4/tests/test_validators_engine.py +0 -157
  235. source_kb-0.2.4/tests/test_validators_links.py +0 -81
  236. source_kb-0.2.4/tests/test_validators_registry.py +0 -61
  237. source_kb-0.2.4/tests/test_validators_sampling.py +0 -163
  238. source_kb-0.2.4/tests/test_workflow_loader.py +0 -140
  239. {source_kb-0.2.4 → source_kb-0.2.26}/LICENSE +0 -0
  240. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/__init__.py +0 -0
  241. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/diff_doc.py +0 -0
  242. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/extract.py +0 -0
  243. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/file_list.py +0 -0
  244. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/jar_resolve.py +0 -0
  245. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/lock.py +0 -0
  246. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/metadata.py +0 -0
  247. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/query.py +0 -0
  248. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/setup.py +0 -0
  249. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/split.py +0 -0
  250. {source_kb-0.2.4 → source_kb-0.2.26}/cli/commands/stale_files.py +0 -0
  251. {source_kb-0.2.4 → source_kb-0.2.26}/core/__init__.py +0 -0
  252. {source_kb-0.2.4 → source_kb-0.2.26}/core/config.py +0 -0
  253. {source_kb-0.2.4 → source_kb-0.2.26}/core/docs/__init__.py +0 -0
  254. {source_kb-0.2.4 → source_kb-0.2.26}/core/docs/section_updater.py +0 -0
  255. {source_kb-0.2.4 → source_kb-0.2.26}/core/git.py +0 -0
  256. {source_kb-0.2.4 → source_kb-0.2.26}/core/interfaces.py +0 -0
  257. {source_kb-0.2.4 → source_kb-0.2.26}/core/monitor/__init__.py +0 -0
  258. {source_kb-0.2.4 → source_kb-0.2.26}/core/monitor/progress.py +0 -0
  259. {source_kb-0.2.4 → source_kb-0.2.26}/core/monitor/prompt_store.py +0 -0
  260. {source_kb-0.2.4 → source_kb-0.2.26}/core/preset_accessors.py +0 -0
  261. {source_kb-0.2.4 → source_kb-0.2.26}/core/preset_classify.py +0 -0
  262. {source_kb-0.2.4 → source_kb-0.2.26}/core/preset_hooks.py +0 -0
  263. {source_kb-0.2.4 → source_kb-0.2.26}/core/preset_profile.py +0 -0
  264. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/__init__.py +0 -0
  265. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/content.py +0 -0
  266. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/context_manager.py +0 -0
  267. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/renderer.py +0 -0
  268. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/response_parser.py +0 -0
  269. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/templates.py +0 -0
  270. {source_kb-0.2.4 → source_kb-0.2.26}/core/prompt/validate_parity.py +0 -0
  271. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/__init__.py +0 -0
  272. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/embedder.py +0 -0
  273. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/indexer.py +0 -0
  274. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/loader.py +0 -0
  275. {source_kb-0.2.4 → source_kb-0.2.26}/core/rag/retriever.py +0 -0
  276. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/__init__.py +0 -0
  277. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/classify.py +0 -0
  278. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/community.py +0 -0
  279. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/dependency_graph.py +0 -0
  280. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/diff_doc.py +0 -0
  281. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/dispatch_render.py +0 -0
  282. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/dispatch_source.py +0 -0
  283. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/extract_methods.py +0 -0
  284. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/file_list.py +0 -0
  285. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/jar_download.py +0 -0
  286. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/jar_resolver.py +0 -0
  287. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/loader.py +0 -0
  288. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/merge.py +0 -0
  289. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/merge_delta.py +0 -0
  290. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/metadata.py +0 -0
  291. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/metadata_builders.py +0 -0
  292. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/module_dag.py +0 -0
  293. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/__init__.py +0 -0
  294. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/jqassistant_cypher.py +0 -0
  295. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/regex.py +0 -0
  296. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/treesitter.py +0 -0
  297. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/treesitter_java.py +0 -0
  298. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/parsers/treesitter_multi.py +0 -0
  299. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/pom_parser.py +0 -0
  300. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/post_merge.py +0 -0
  301. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/post_merge_llm.py +0 -0
  302. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/query.py +0 -0
  303. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/shard_context.py +0 -0
  304. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split.py +0 -0
  305. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split_cache.py +0 -0
  306. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split_feedback.py +0 -0
  307. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split_plan.py +0 -0
  308. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split_plan_helpers.py +0 -0
  309. {source_kb-0.2.4 → source_kb-0.2.26}/core/skeleton/split_plan_llm.py +0 -0
  310. {source_kb-0.2.4 → source_kb-0.2.26}/core/utils.py +0 -0
  311. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/__main__.py +0 -0
  312. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/consistency.py +0 -0
  313. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/coverage.py +0 -0
  314. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/duplicates.py +0 -0
  315. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/engine.py +0 -0
  316. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/links.py +0 -0
  317. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/sampling.py +0 -0
  318. {source_kb-0.2.4 → source_kb-0.2.26}/core/validators/structure.py +0 -0
  319. {source_kb-0.2.4 → source_kb-0.2.26}/presets/generic/audit_dimensions.md +0 -0
  320. {source_kb-0.2.4 → source_kb-0.2.26}/presets/generic/doc_types.yaml +0 -0
  321. {source_kb-0.2.4 → source_kb-0.2.26}/presets/generic/preset.yaml +0 -0
  322. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/audit_dimensions.md +0 -0
  323. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/audit_dimensions.yaml +0 -0
  324. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/hooks.py +0 -0
  325. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/preset.yaml +0 -0
  326. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/README.md +0 -0
  327. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/audit-system.md +0 -0
  328. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-aop.md +0 -0
  329. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-api.md +0 -0
  330. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-architecture.md +0 -0
  331. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-async-events.md +0 -0
  332. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-audit-api-contracts.md +0 -0
  333. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-audit-architecture.md +0 -0
  334. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-audit-business.md +0 -0
  335. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-audit-data-models.md +0 -0
  336. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-business.md +0 -0
  337. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-caching.md +0 -0
  338. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-database-access.md +0 -0
  339. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-enum.md +0 -0
  340. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-error-handling.md +0 -0
  341. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-external-integrations.md +0 -0
  342. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-index.md +0 -0
  343. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-messaging.md +0 -0
  344. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-model.md +0 -0
  345. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-observability.md +0 -0
  346. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-scheduled.md +0 -0
  347. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-security.md +0 -0
  348. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-structure.md +0 -0
  349. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-sync-section.md +0 -0
  350. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/subagent-utils.md +0 -0
  351. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/templates/sync-system.md +0 -0
  352. {source_kb-0.2.4 → source_kb-0.2.26}/presets/java-spring/workflow-extensions.md +0 -0
  353. {source_kb-0.2.4 → source_kb-0.2.26}/setup.cfg +0 -0
  354. {source_kb-0.2.4 → source_kb-0.2.26}/source_kb.egg-info/dependency_links.txt +0 -0
  355. {source_kb-0.2.4 → source_kb-0.2.26}/source_kb.egg-info/entry_points.txt +0 -0
  356. {source_kb-0.2.4 → source_kb-0.2.26}/source_kb.egg-info/requires.txt +0 -0
@@ -0,0 +1,15 @@
1
+ include LICENSE
2
+ include README.en.md
3
+ include pyproject.toml
4
+ include setup.cfg
5
+
6
+ recursive-include cli *.py
7
+ recursive-include core *.py
8
+ recursive-include presets *.yaml *.md *.py
9
+
10
+ prune engine
11
+ prune mcp_server
12
+ prune skills
13
+ prune tests
14
+ prune docs
15
+ prune .source-cache
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: source-kb
3
- Version: 0.2.4
3
+ Version: 0.2.26
4
4
  Summary: Auto-generate structured knowledge base documents from source code. Supports AI agent mode (skill-based) and standalone CLI.
5
5
  License-Expression: MIT
6
6
  Keywords: knowledge-base,documentation,code-analysis,llm,rag
@@ -61,15 +61,24 @@ Install the CLI toolchain:
61
61
 
62
62
  ```bash
63
63
  pip install source-kb[full]
64
+
65
+ # If you have multiple Python versions, specify the target version:
66
+ py -3.12 -m pip install source-kb[full]
67
+ ```
68
+
69
+ Install skills to your Agent's directory:
70
+
71
+ ```bash
72
+ source-kb install-skills --agent kiro # Kiro
73
+ source-kb install-skills --agent cursor # Cursor
74
+ source-kb install-skills --agent claude # Claude Code
64
75
  ```
65
76
 
66
- Clone the presets repo into your project:
77
+ This automatically installs `presets/` and `examples/` to `~/.source-kb/` (shared globally across all projects).
78
+ To install global assets only:
67
79
 
68
80
  ```bash
69
- git clone https://github.com/anthropics/source-kb-presets.git
70
- cp -r source-kb-presets/skills ./skills
71
- cp -r source-kb-presets/presets ./presets
72
- cp -r source-kb-presets/examples ./examples
81
+ source-kb install-skills --assets-only # Install presets/ + examples/ to ~/.source-kb/
73
82
  ```
74
83
 
75
84
  Then talk to your AI Agent (Kiro / Claude Code / Cursor / Windsurf):
@@ -168,6 +177,5 @@ source-kb --help
168
177
  - [Getting Started](docs/getting-started.md) — Installation, configuration, first run
169
178
  - [Configuration Reference](docs/configuration.md) — Full kb-project.yaml field reference
170
179
  - [Custom Presets](docs/custom-presets.md) — Create and customize language presets
171
- - [Preset Development](docs/presets.md) — Built-in preset internals
172
180
  - [Design Document](docs/design-v5.md) — Architecture design and technical decisions
173
181
  - [CLI vs Agent Mode](docs/cli-vs-agent-mode-analysis.md) — Two modes compared
@@ -28,15 +28,24 @@ Install the CLI toolchain:
28
28
 
29
29
  ```bash
30
30
  pip install source-kb[full]
31
+
32
+ # If you have multiple Python versions, specify the target version:
33
+ py -3.12 -m pip install source-kb[full]
34
+ ```
35
+
36
+ Install skills to your Agent's directory:
37
+
38
+ ```bash
39
+ source-kb install-skills --agent kiro # Kiro
40
+ source-kb install-skills --agent cursor # Cursor
41
+ source-kb install-skills --agent claude # Claude Code
31
42
  ```
32
43
 
33
- Clone the presets repo into your project:
44
+ This automatically installs `presets/` and `examples/` to `~/.source-kb/` (shared globally across all projects).
45
+ To install global assets only:
34
46
 
35
47
  ```bash
36
- git clone https://github.com/anthropics/source-kb-presets.git
37
- cp -r source-kb-presets/skills ./skills
38
- cp -r source-kb-presets/presets ./presets
39
- cp -r source-kb-presets/examples ./examples
48
+ source-kb install-skills --assets-only # Install presets/ + examples/ to ~/.source-kb/
40
49
  ```
41
50
 
42
51
  Then talk to your AI Agent (Kiro / Claude Code / Cursor / Windsurf):
@@ -135,6 +144,5 @@ source-kb --help
135
144
  - [Getting Started](docs/getting-started.md) — Installation, configuration, first run
136
145
  - [Configuration Reference](docs/configuration.md) — Full kb-project.yaml field reference
137
146
  - [Custom Presets](docs/custom-presets.md) — Create and customize language presets
138
- - [Preset Development](docs/presets.md) — Built-in preset internals
139
147
  - [Design Document](docs/design-v5.md) — Architecture design and technical decisions
140
148
  - [CLI vs Agent Mode](docs/cli-vs-agent-mode-analysis.md) — Two modes compared
@@ -28,15 +28,24 @@
28
28
 
29
29
  ```bash
30
30
  pip install source-kb[full]
31
+
32
+ # 如果系统有多个 Python 版本,指定目标版本安装:
33
+ py -3.12 -m pip install source-kb[full]
34
+ ```
35
+
36
+ 一键安装 Skill 到 Agent 目录:
37
+
38
+ ```bash
39
+ source-kb install-skills --agent kiro # Kiro
40
+ source-kb install-skills --agent cursor # Cursor
41
+ source-kb install-skills --agent claude # Claude Code
31
42
  ```
32
43
 
33
- 克隆 presets 仓库到你的项目中:
44
+ 安装过程会自动将 `presets/` 和 `examples/` 安装到 `~/.source-kb/`(全局共享,所有项目通用)。
45
+ 如需单独安装全局资产:
34
46
 
35
47
  ```bash
36
- git clone https://github.com/anthropics/source-kb-presets.git
37
- cp -r source-kb-presets/skills ./skills
38
- cp -r source-kb-presets/presets ./presets
39
- cp -r source-kb-presets/examples ./examples
48
+ source-kb install-skills --assets-only # 仅安装 presets/ + examples/ 到 ~/.source-kb/
40
49
  ```
41
50
 
42
51
  在 AI Agent(Kiro / Claude Code / Cursor / Windsurf)中直接对话:
@@ -136,4 +145,4 @@ source-kb --help
136
145
  - [配置参考](docs/configuration.md) — kb-project.yaml 完整字段说明
137
146
  - [设计文档](docs/design-v5.md) — 架构设计与技术决策
138
147
  - [CLI vs Agent 模式](docs/cli-vs-agent-mode-analysis.md) — 两种模式对比
139
- - [Preset 开发](docs/presets.md) — 自定义语言预设指南
148
+ - [Preset 开发](docs/custom-presets.md) — 自定义语言预设指南
@@ -23,10 +23,31 @@ def _discover_commands(subparsers: argparse._SubParsersAction) -> None:
23
23
  module.register(subparsers)
24
24
 
25
25
 
26
+ def _warn_python_version() -> None:
27
+ """Warn if tree-sitter-languages is unavailable (Python 3.13+)."""
28
+ v = sys.version_info
29
+ if v >= (3, 13):
30
+ try:
31
+ import tree_sitter_languages # noqa: F401
32
+ except ImportError:
33
+ print(
34
+ f"[WARNING] Python {v.major}.{v.minor} detected. "
35
+ f"tree-sitter-languages has no wheel for 3.13+, "
36
+ f"skeleton extraction will fall back to regex (lower accuracy).\n"
37
+ f" Recommended: py -3.12 -m pip install source-kb[full]",
38
+ file=sys.stderr,
39
+ )
40
+
41
+
26
42
  def main(argv: list[str] | None = None) -> None:
27
43
  sys.stdout.reconfigure(encoding="utf-8")
28
44
  sys.stderr.reconfigure(encoding="utf-8")
29
45
 
46
+ _warn_python_version()
47
+
48
+ from cli.global_assets import ensure_global_assets
49
+ ensure_global_assets()
50
+
30
51
  from core import __version__
31
52
 
32
53
  parser = argparse.ArgumentParser(
@@ -1,4 +1,5 @@
1
1
  """Allow running as: python -m cli"""
2
+ from __future__ import annotations
2
3
 
3
4
  from cli import main
4
5
 
@@ -0,0 +1,126 @@
1
+ """source-kb anchor-fix — Fix broken cross-document anchor links."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+
10
+
11
+ def register(subparsers: argparse._SubParsersAction) -> None:
12
+ p = subparsers.add_parser("anchor-fix", help="Fix broken cross-document anchor links")
13
+ p.add_argument("--module-dir", required=True, help="Module documentation directory")
14
+ p.add_argument("--dry-run", action="store_true", help="Report without fixing")
15
+ p.add_argument("--threshold", type=float, default=0.8, help="Fuzzy match threshold (0.0-1.0)")
16
+ p.add_argument("--detect-cascade", metavar="FILE",
17
+ help="Detect cascade: check which links break due to title changes in FILE")
18
+ p.add_argument("--old-content", help="Path to old version of file (for --detect-cascade)")
19
+ p.set_defaults(func=run)
20
+
21
+
22
+ def run(args: argparse.Namespace) -> None:
23
+ module_dir = Path(args.module_dir)
24
+ if not module_dir.is_dir():
25
+ print(f"Error: directory not found: {module_dir}", file=sys.stderr)
26
+ sys.exit(1)
27
+
28
+ if getattr(args, "detect_cascade", None):
29
+ _run_detect_cascade(args, module_dir)
30
+ else:
31
+ _run_fix(args, module_dir)
32
+
33
+
34
+ def _run_fix(args: argparse.Namespace, module_dir: Path) -> None:
35
+ from core.skeleton.anchor_fix import fix_anchors
36
+
37
+ result = fix_anchors(
38
+ module_dir,
39
+ dry_run=args.dry_run,
40
+ similarity_threshold=args.threshold,
41
+ )
42
+
43
+ mode = " (dry-run)" if args.dry_run else ""
44
+ print(f"Anchor fix{mode}: scanned {result.files_scanned} files, checked {result.links_checked} links")
45
+ if result.links_fixed or result.links_degraded:
46
+ print(f" Fixed: {result.links_fixed}, Degraded: {result.links_degraded}")
47
+ for d in result.details:
48
+ action = "fixed" if d["action"] == "fixed" else "degraded"
49
+ print(f" [{action}] {d['file']}: {d['old_link']} -> {d['new_link']}")
50
+ else:
51
+ print(" All links valid.")
52
+
53
+ print(json.dumps({
54
+ "status": "ok", "files_scanned": result.files_scanned,
55
+ "links_checked": result.links_checked, "links_fixed": result.links_fixed,
56
+ "links_degraded": result.links_degraded,
57
+ }, ensure_ascii=False), file=sys.stderr)
58
+
59
+
60
+ def _run_detect_cascade(args: argparse.Namespace, module_dir: Path) -> None:
61
+ from core.skeleton.anchor_fix import detect_cascade
62
+
63
+ changed_file = args.detect_cascade
64
+ target_path = module_dir / changed_file
65
+
66
+ if not target_path.exists():
67
+ print(f"Error: {changed_file} not found in {module_dir}", file=sys.stderr)
68
+ sys.exit(1)
69
+
70
+ new_content = target_path.read_text(encoding="utf-8", errors="replace")
71
+
72
+ if args.old_content:
73
+ old_path = Path(args.old_content)
74
+ if not old_path.exists():
75
+ print(f"Error: old content file not found: {old_path}", file=sys.stderr)
76
+ sys.exit(1)
77
+ old_content = old_path.read_text(encoding="utf-8", errors="replace")
78
+ else:
79
+ # Try git to get previous version
80
+ try:
81
+ import subprocess
82
+ result = subprocess.run(
83
+ ["git", "show", f"HEAD:{changed_file}"],
84
+ cwd=str(module_dir), capture_output=True, text=True, timeout=10,
85
+ )
86
+ if result.returncode == 0:
87
+ old_content = result.stdout
88
+ else:
89
+ print("Error: --old-content required (git HEAD version not available)",
90
+ file=sys.stderr)
91
+ sys.exit(1)
92
+ except Exception:
93
+ print("Error: --old-content required (git not available)", file=sys.stderr)
94
+ sys.exit(1)
95
+
96
+ cascade = detect_cascade(
97
+ module_dir, changed_file, old_content, new_content,
98
+ similarity_threshold=args.threshold,
99
+ )
100
+
101
+ if not cascade.removed_anchors and not cascade.renamed_anchors:
102
+ print(f"No title changes detected in {changed_file}")
103
+ print(json.dumps({"status": "ok", "cascade": False}, ensure_ascii=False),
104
+ file=sys.stderr)
105
+ return
106
+
107
+ print(f"Cascade detection for {changed_file}:")
108
+ if cascade.renamed_anchors:
109
+ print(f" Renamed ({len(cascade.renamed_anchors)}):")
110
+ for r in cascade.renamed_anchors:
111
+ print(f" #{r['old']} -> #{r['new']} (score={r['score']})")
112
+ if cascade.removed_anchors:
113
+ print(f" Removed ({len(cascade.removed_anchors)}):")
114
+ for a in cascade.removed_anchors:
115
+ print(f" #{a}")
116
+ if cascade.affected_links:
117
+ print(f" Affected links ({len(cascade.affected_links)}):")
118
+ for link in cascade.affected_links:
119
+ print(f" {link['file']}: {link['link']}")
120
+
121
+ print(json.dumps({
122
+ "status": "cascade_detected",
123
+ "renamed": len(cascade.renamed_anchors),
124
+ "removed": len(cascade.removed_anchors),
125
+ "affected_links": len(cascade.affected_links),
126
+ }, ensure_ascii=False), file=sys.stderr)
@@ -0,0 +1,53 @@
1
+ """source-kb audit — Low-confidence classification audit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+
10
+
11
+ def register(subparsers: argparse._SubParsersAction) -> None:
12
+ p = subparsers.add_parser("audit", help="Low-confidence classification audit")
13
+ p.add_argument("--skeleton", required=True, help="Skeleton JSON path")
14
+ p.add_argument("--preset", required=True, help="Preset name")
15
+ p.add_argument("--threshold", type=float, default=0.7, help="Confidence threshold (default: 0.7)")
16
+ p.set_defaults(func=run)
17
+
18
+
19
+ def run(args: argparse.Namespace) -> None:
20
+ from core.skeleton.classify import find_suspicious_files
21
+ from core.preset import load_preset
22
+ from core.skeleton.query import load_skeleton
23
+
24
+ preset = load_preset(args.preset)
25
+ skeleton_path = Path(args.skeleton)
26
+ entries = load_skeleton(skeleton_path)
27
+ threshold = args.threshold
28
+
29
+ suspicious = find_suspicious_files(entries, preset, threshold=threshold)
30
+
31
+ if suspicious:
32
+ print(f"Found {len(suspicious)} low-confidence files (threshold < {threshold}):\n")
33
+ print("| File | Category | Confidence | Match Reason | Suspicious Signal |")
34
+ print("|------|------|--------|---------|---------|")
35
+ for entry in suspicious:
36
+ cats = ", ".join(entry.categories) if entry.categories else "-"
37
+ reasons = "; ".join(entry.match_reasons[:2]) if entry.match_reasons else "-"
38
+ signals = "; ".join(entry.suspicious_signals[:2]) if entry.suspicious_signals else "-"
39
+ print(f"| {entry.file} | {cats} | {entry.confidence:.2f} | {reasons} | {signals} |")
40
+ else:
41
+ print(f"No low-confidence files (threshold < {threshold})")
42
+
43
+ from core.history import record_global_event
44
+ skeleton_dir = skeleton_path.parent
45
+ knowledge_dir = skeleton_dir.parent if skeleton_dir.name == ".meta" else skeleton_dir
46
+ record_global_event(
47
+ knowledge_dir, "audit",
48
+ docs_updated=[skeleton_path.name],
49
+ trigger="cli",
50
+ )
51
+
52
+ print(json.dumps({"status": "ok", "suspicious": len(suspicious),
53
+ "threshold": threshold}, ensure_ascii=False), file=sys.stderr)
@@ -0,0 +1,69 @@
1
+ """source-kb check-triggers — Detect optional document triggers from changed files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+
10
+
11
+ def register(subparsers: argparse._SubParsersAction) -> None:
12
+ p = subparsers.add_parser(
13
+ "check-triggers",
14
+ help="Detect optional documents triggered by source changes",
15
+ )
16
+ p.add_argument("--module-dir", required=True, help="Module documentation directory")
17
+ p.add_argument("--preset", default="java-spring", help="Preset name")
18
+ p.add_argument("--files", nargs="+", help="Changed source files to scan")
19
+ p.add_argument("--source-cache", help="Source cache directory (reads files from here)")
20
+ p.set_defaults(func=run)
21
+
22
+
23
+ def run(args: argparse.Namespace) -> None:
24
+ from core.preset import load_preset
25
+ from core.docs.trigger_detect import detect_triggers_from_dir
26
+
27
+ module_dir = Path(args.module_dir)
28
+ preset = load_preset(args.preset)
29
+
30
+ changed_files: dict[str, str] = {}
31
+
32
+ if args.files:
33
+ source_base = Path(args.source_cache) if args.source_cache else Path(".")
34
+ for filepath in args.files:
35
+ full_path = source_base / filepath
36
+ if full_path.exists():
37
+ try:
38
+ changed_files[filepath] = full_path.read_text(encoding="utf-8", errors="replace")
39
+ except OSError:
40
+ pass
41
+
42
+ if not changed_files:
43
+ print("No changed files to scan.")
44
+ print(json.dumps({"status": "ok", "triggers": []}, ensure_ascii=False),
45
+ file=sys.stderr)
46
+ return
47
+
48
+ triggers = detect_triggers_from_dir(changed_files, preset, module_dir)
49
+
50
+ if not triggers:
51
+ print("No optional document triggers detected.")
52
+ print(json.dumps({"status": "ok", "triggers": []}, ensure_ascii=False),
53
+ file=sys.stderr)
54
+ return
55
+
56
+ print(f"Detected {len(triggers)} document trigger(s):")
57
+ for t in triggers:
58
+ action_label = "CREATE" if t["action"] == "create" else "UPDATE"
59
+ print(f" [{action_label}] {t['filename']} ({t['doc_type']})")
60
+ for m in t["matches"][:3]:
61
+ print(f" matched '{m['keyword']}' in {m['file']}")
62
+
63
+ print(json.dumps({
64
+ "status": "ok",
65
+ "triggers": [
66
+ {"doc_type": t["doc_type"], "filename": t["filename"], "action": t["action"]}
67
+ for t in triggers
68
+ ],
69
+ }, ensure_ascii=False), file=sys.stderr)
@@ -0,0 +1,44 @@
1
+ """source-kb detect — Detect project language, framework, and structure."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+
10
+ def register(subparsers: argparse._SubParsersAction) -> None:
11
+ p = subparsers.add_parser(
12
+ "detect",
13
+ help="Detect project language, framework, structure, and modules",
14
+ )
15
+ p.add_argument("--path", default=".", help="Path to source repo (default: current dir)")
16
+ p.add_argument("--json", action="store_true", dest="as_json", help="Output as JSON")
17
+ p.set_defaults(func=run)
18
+
19
+
20
+ def run(args: argparse.Namespace) -> None:
21
+ from core.bootstrap import detect_project, format_detection_summary
22
+
23
+ repo_path = Path(args.path).resolve()
24
+ if not repo_path.is_dir():
25
+ print(f"Error: path does not exist: {repo_path}")
26
+ sys.exit(1)
27
+
28
+ info = detect_project(repo_path)
29
+
30
+ if args.as_json:
31
+ data = {
32
+ "language": info.language,
33
+ "framework": info.framework,
34
+ "preset": info.preset,
35
+ "structure": info.structure,
36
+ "repo_name": info.repo_name,
37
+ "modules": [
38
+ {"name": m.name, "path": m.path, "type": m.module_type, "description": m.description}
39
+ for m in info.modules
40
+ ],
41
+ }
42
+ print(json.dumps(data, ensure_ascii=False, indent=2))
43
+ else:
44
+ print(format_detection_summary(info))
@@ -34,14 +34,27 @@ def run(args: argparse.Namespace) -> None:
34
34
 
35
35
  module_name = args.module
36
36
  module_dir = knowledge_dir / module_name
37
- source_cache = cache_dir / module_name
38
37
 
39
38
  module_type = "service"
40
- repos = source.get("repos", [])
41
- for repo in repos:
42
- if repo.get("name") == module_name:
43
- module_type = repo.get("type", "service")
39
+ source_cache = cache_dir / module_name
40
+
41
+ modules = kb_config.get("modules", [])
42
+ for mod in modules:
43
+ if mod.get("name") == module_name:
44
+ module_type = mod.get("module_type", mod.get("type", "service"))
45
+ repo_name = mod.get("repo", module_name)
46
+ mod_path = mod.get("path", ".")
47
+ if mod_path and mod_path != ".":
48
+ source_cache = cache_dir / repo_name / mod_path
49
+ else:
50
+ source_cache = cache_dir / repo_name
44
51
  break
52
+ else:
53
+ repos = source.get("repos", [])
54
+ for repo in repos:
55
+ if repo.get("name") == module_name:
56
+ module_type = repo.get("type", "service")
57
+ break
45
58
 
46
59
  mode = args.mode
47
60
 
@@ -0,0 +1,94 @@
1
+ """source-kb history — Show operation history for a module or knowledge base."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
11
+
12
+ def register(subparsers: argparse._SubParsersAction) -> None:
13
+ p = subparsers.add_parser("history", help="Show operation history")
14
+ p.add_argument("--config", help="kb-project.yaml path")
15
+ p.add_argument("--kb", required=True, help="Knowledge base name")
16
+ p.add_argument("--module", help="Module name (omit for global changelog)")
17
+ p.add_argument("--limit", type=int, default=20, help="Max entries (default: 20)")
18
+ p.add_argument("--json", dest="as_json", action="store_true", help="Output as JSON")
19
+ p.set_defaults(func=run)
20
+
21
+
22
+ def run(args: argparse.Namespace) -> None:
23
+ from core.config import load_config, find_config
24
+ from core.history import get_module_history, get_global_changelog
25
+
26
+ config_path = Path(args.config) if args.config else find_config()
27
+ config = load_config(config_path)
28
+ kb_cfg = config.get_kb(args.kb)
29
+
30
+ knowledge_dir = Path(kb_cfg.get("knowledge_dir", f"./knowledge/{args.kb}"))
31
+ if not knowledge_dir.is_absolute():
32
+ config_dir = Path(config.raw.get("_config_dir", ".")).resolve()
33
+ knowledge_dir = (config_dir / knowledge_dir).resolve()
34
+
35
+ if not knowledge_dir.is_dir():
36
+ print(f"Error: knowledge directory not found: {knowledge_dir}", file=sys.stderr)
37
+ sys.exit(1)
38
+
39
+ if args.module:
40
+ module_dir = knowledge_dir / args.module
41
+ if not module_dir.is_dir():
42
+ print(f"Error: module not found: {module_dir}", file=sys.stderr)
43
+ sys.exit(1)
44
+ entries = get_module_history(module_dir, limit=args.limit)
45
+ scope = f"{args.kb}/{args.module}"
46
+ else:
47
+ entries = get_global_changelog(knowledge_dir, limit=args.limit)
48
+ scope = f"{args.kb} (global)"
49
+
50
+ if args.as_json:
51
+ print(json.dumps(entries, ensure_ascii=False, indent=2))
52
+ return
53
+
54
+ if not entries:
55
+ print(f"No history for {scope}")
56
+ print(json.dumps({"status": "ok", "entries": 0}, ensure_ascii=False), file=sys.stderr)
57
+ return
58
+
59
+ print(f"History: {scope} (showing {len(entries)}/{args.limit} max)\n")
60
+
61
+ for entry in entries:
62
+ ts = _format_ts(entry.get("ts", ""))
63
+ op = entry.get("op", "?")
64
+ doc_type = entry.get("doc_type", "")
65
+ version = entry.get("version")
66
+ modules = entry.get("modules_affected", [])
67
+
68
+ line = f" {ts} {op:<12}"
69
+ if doc_type:
70
+ line += f" {doc_type}"
71
+ if version is not None:
72
+ line += f" v{version}"
73
+ if modules:
74
+ line += f" [{', '.join(modules[:3])}]"
75
+
76
+ stats = entry.get("stats", {})
77
+ if stats:
78
+ delta = stats.get("delta", "")
79
+ if delta:
80
+ line += f" ({delta})"
81
+
82
+ print(line)
83
+
84
+ print(json.dumps({"status": "ok", "entries": len(entries)}, ensure_ascii=False), file=sys.stderr)
85
+
86
+
87
+ def _format_ts(ts: str) -> str:
88
+ if not ts:
89
+ return " "
90
+ try:
91
+ dt = datetime.fromisoformat(ts)
92
+ return dt.strftime("%Y-%m-%d %H:%M")
93
+ except (ValueError, TypeError):
94
+ return ts[:16]
@@ -35,14 +35,12 @@ def run_index(args: argparse.Namespace) -> None:
35
35
  knowledge_dir = Path(kb_cfg["knowledge_dir"])
36
36
  collection_name = kb_cfg["collection"]
37
37
 
38
- incremental = bool(args.module or args.files)
39
- if args.module:
40
- knowledge_dir = knowledge_dir / args.module
38
+ module = args.module
41
39
 
42
40
  if args.files:
43
- docs = load_documents(knowledge_dir, file_filter=args.files)
41
+ docs = load_documents(knowledge_dir, module=module, files=args.files)
44
42
  else:
45
- docs = load_documents(knowledge_dir)
43
+ docs = load_documents(knowledge_dir, module=module)
46
44
 
47
45
  if not docs:
48
46
  print(f"No documents found in {knowledge_dir}")
@@ -51,8 +49,22 @@ def run_index(args: argparse.Namespace) -> None:
51
49
 
52
50
  chunks = chunk_documents(docs)
53
51
  print(f"Indexing {len(docs)} docs, {len(chunks)} chunks...")
54
- build_index(chunks, collection_name, config, kb_name=args.kb, incremental=incremental)
52
+ build_index(chunks, collection_name, config, kb_name=args.kb, module=module, files=args.files)
55
53
  print(f"Index built: {len(chunks)} chunks -> collection '{collection_name}'")
54
+
55
+ from core.history import mark_indexed, record_global_event, refresh_kb_state
56
+ if module:
57
+ module_dir = knowledge_dir / module
58
+ if module_dir.is_dir():
59
+ mark_indexed(module_dir, config.embed_model, len(chunks), collection=collection_name)
60
+ else:
61
+ for d in sorted(knowledge_dir.iterdir()):
62
+ if d.is_dir() and not d.name.startswith(".") and (d / ".meta").is_dir():
63
+ mark_indexed(d, config.embed_model, len(chunks), collection=collection_name)
64
+
65
+ record_global_event(knowledge_dir, "index", trigger="cli")
66
+ refresh_kb_state(knowledge_dir)
67
+
56
68
  print(json.dumps({"status": "ok", "docs": len(docs), "chunks": len(chunks),
57
69
  "collection": collection_name}, ensure_ascii=False), file=sys.stderr)
58
70