@guanlin4924/devflow-pi 0.1.0

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 (254) hide show
  1. package/README.md +164 -0
  2. package/docs/OMP-NATIVE-ARCHITECTURE.md +104 -0
  3. package/package.json +40 -0
  4. package/plugins/devflow-core/extensions/devflow-core.ts +1642 -0
  5. package/plugins/devflow-core/package.json +14 -0
  6. package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +174 -0
  7. package/plugins/devflow-core/runtime/hooks/hook_common.py +696 -0
  8. package/plugins/devflow-core/runtime/hooks/index_gate.py +63 -0
  9. package/plugins/devflow-core/runtime/hooks/injection_code_specification.py +7 -0
  10. package/plugins/devflow-core/runtime/hooks/post_tool.py +44 -0
  11. package/plugins/devflow-core/runtime/hooks/start.py +288 -0
  12. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +265 -0
  13. package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +4 -0
  14. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +520 -0
  15. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +42 -0
  16. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +138 -0
  17. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +121 -0
  18. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +553 -0
  19. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +219 -0
  20. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +168 -0
  21. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +110 -0
  22. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +565 -0
  23. package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +783 -0
  24. package/plugins/devflow-core/skills/workflow-orchestrator/templates/uat-remediation.yaml +143 -0
  25. package/plugins/devflow-core/skills/workflow-orchestrator/templates/workflows-README.md +10 -0
  26. package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +117 -0
  27. package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +167 -0
  28. package/plugins/devflow-core/skills/workflow-pipeline/references/slice-safety-guide.md +125 -0
  29. package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +819 -0
  30. package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +25 -0
  31. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +68 -0
  32. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +64 -0
  33. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +168 -0
  34. package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +43 -0
  35. package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +45 -0
  36. package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +57 -0
  37. package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +40 -0
  38. package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +109 -0
  39. package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +80 -0
  40. package/plugins/devflow-core/skills/workflow-project-context/assets/run-manifest-template.json +40 -0
  41. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/directory-structure.md +46 -0
  42. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/engine.md +46 -0
  43. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/errors.md +46 -0
  44. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/locator.md +46 -0
  45. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/model.md +46 -0
  46. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/repository.md +46 -0
  47. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/router.md +46 -0
  48. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/scheduler.md +46 -0
  49. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/schema.md +46 -0
  50. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/service.md +46 -0
  51. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/testing.md +46 -0
  52. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/api-client.md +46 -0
  53. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/case-editor.md +46 -0
  54. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/components.md +46 -0
  55. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/directory-structure.md +46 -0
  56. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/forms.md +46 -0
  57. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/routing.md +46 -0
  58. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/state.md +46 -0
  59. package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/testing.md +46 -0
  60. package/plugins/devflow-core/skills/workflow-project-context/assets/stack-spec-template.md +106 -0
  61. package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +77 -0
  62. package/plugins/devflow-core/skills/workflow-project-context/assets/toolchain-template.md +37 -0
  63. package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +262 -0
  64. package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +545 -0
  65. package/plugins/devflow-design/agents/contract-auditor.md +18 -0
  66. package/plugins/devflow-design/extensions/devflow-design.ts +47 -0
  67. package/plugins/devflow-design/package.json +14 -0
  68. package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +142 -0
  69. package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +150 -0
  70. package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +121 -0
  71. package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +257 -0
  72. package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/adr-template.md +57 -0
  73. package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +205 -0
  74. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +59 -0
  75. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/backend-conventions.md +30 -0
  76. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +40 -0
  77. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +66 -0
  78. package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +91 -0
  79. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +71 -0
  80. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +31 -0
  81. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +31 -0
  82. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +47 -0
  83. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +29 -0
  84. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +31 -0
  85. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +40 -0
  86. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +19 -0
  87. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +377 -0
  88. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/charts.csv +26 -0
  89. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/colors.csv +97 -0
  90. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/icons.csv +101 -0
  91. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/landing.csv +31 -0
  92. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/products.csv +97 -0
  93. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/react-performance.csv +45 -0
  94. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  95. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  96. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  97. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  98. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  99. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  100. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  101. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  102. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/react.csv +54 -0
  103. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  104. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  105. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  106. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  107. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/styles.csv +68 -0
  108. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/typography.csv +58 -0
  109. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  110. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  111. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/web-interface.csv +31 -0
  112. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/core.py +253 -0
  113. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/design_system.py +1067 -0
  114. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/search.py +114 -0
  115. package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +67 -0
  116. package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +370 -0
  117. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/admin-system-template.md +57 -0
  118. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/api-service-template.md +42 -0
  119. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/c-end-system-template.md +25 -0
  120. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +139 -0
  121. package/plugins/devflow-design/skills/workflow-feature-breakdown/references/bug-rules.md +12 -0
  122. package/plugins/devflow-design/skills/workflow-feature-breakdown/references/exception-checklist.md +31 -0
  123. package/plugins/devflow-design/skills/workflow-feature-breakdown/references/trace-integrity-guide.md +153 -0
  124. package/plugins/devflow-design/skills/workflow-feature-breakdown/references/writing-guide.md +92 -0
  125. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +223 -0
  126. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +204 -0
  127. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +58 -0
  128. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/component-patterns.md +42 -0
  129. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-conventions.md +28 -0
  130. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/common-web.md +53 -0
  131. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/next.md +40 -0
  132. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/react.md +41 -0
  133. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/vue.md +41 -0
  134. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +95 -0
  135. package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +172 -0
  136. package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +302 -0
  137. package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +244 -0
  138. package/plugins/devflow-design/skills/workflow-ui-spec/references/affected-module-exploration.md +55 -0
  139. package/plugins/devflow-implementation/agents/implementation-worker.md +20 -0
  140. package/plugins/devflow-implementation/package.json +6 -0
  141. package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +124 -0
  142. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/INDEX.md +13 -0
  143. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/adversarial-review.md +35 -0
  144. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/first-principles.md +26 -0
  145. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +28 -0
  146. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +51 -0
  147. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/seam-analysis.md +30 -0
  148. package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +160 -0
  149. package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +33 -0
  150. package/plugins/devflow-implementation/skills/workflow-change-plan/references/ddl-guidelines.md +60 -0
  151. package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +217 -0
  152. package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +55 -0
  153. package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +271 -0
  154. package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +1251 -0
  155. package/plugins/devflow-implementation/skills/workflow-execute-phase/references/ui-brand.md +160 -0
  156. package/plugins/devflow-intel/agents/project-scout.md +27 -0
  157. package/plugins/devflow-intel/extensions/devflow-intel.ts +104 -0
  158. package/plugins/devflow-intel/package.json +14 -0
  159. package/plugins/devflow-intel/skills/code-index-query/SKILL.md +37 -0
  160. package/plugins/devflow-intel/skills/code-index-query/references/query-patterns.md +27 -0
  161. package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +68 -0
  162. package/plugins/devflow-intel/skills/workflow-code-index/references/adapter-contract.md +38 -0
  163. package/plugins/devflow-intel/skills/workflow-code-index/references/audit-checklist.md +10 -0
  164. package/plugins/devflow-intel/skills/workflow-code-index/references/confidence-rules.md +43 -0
  165. package/plugins/devflow-intel/skills/workflow-code-index/references/data-model.md +74 -0
  166. package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +24 -0
  167. package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +20 -0
  168. package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/__init__.py +1 -0
  169. package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/base.py +30 -0
  170. package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/generic_text.py +287 -0
  171. package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/registry.py +17 -0
  172. package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +437 -0
  173. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/__init__.py +1 -0
  174. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +139 -0
  175. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/hashing.py +11 -0
  176. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +115 -0
  177. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/schema.py +123 -0
  178. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/__init__.py +1 -0
  179. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +66 -0
  180. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/relation_check.py +36 -0
  181. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/schema_validate.py +11 -0
  182. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/stale_check.py +42 -0
  183. package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/__init__.py +1 -0
  184. package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/jsonl_writer.py +25 -0
  185. package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/markdown_writer.py +106 -0
  186. package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/sqlite_writer.py +143 -0
  187. package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +381 -0
  188. package/plugins/devflow-intel/skills/workflow-context-sync/agents/openai.yaml +4 -0
  189. package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +241 -0
  190. package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +186 -0
  191. package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +47 -0
  192. package/plugins/devflow-quality/agents/code-reviewer.md +18 -0
  193. package/plugins/devflow-quality/agents/verification-runner.md +21 -0
  194. package/plugins/devflow-quality/extensions/devflow-quality.ts +59 -0
  195. package/plugins/devflow-quality/package.json +14 -0
  196. package/plugins/devflow-quality/skills/code-review/SKILL.md +288 -0
  197. package/plugins/devflow-quality/skills/code-review/references/cross-layer-checklist.md +70 -0
  198. package/plugins/devflow-quality/skills/code-review/references/dao-checklist.md +70 -0
  199. package/plugins/devflow-quality/skills/code-review/references/handler-checklist.md +48 -0
  200. package/plugins/devflow-quality/skills/code-review/references/repository-checklist.md +44 -0
  201. package/plugins/devflow-quality/skills/code-review/references/service-checklist.md +57 -0
  202. package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +34 -0
  203. package/plugins/devflow-quality/skills/code-review/references/vo-checklist.md +26 -0
  204. package/plugins/devflow-quality/skills/workflow-audit-milestone/SKILL.md +31 -0
  205. package/plugins/devflow-quality/skills/workflow-audit-milestone/references/audit-milestone.md +328 -0
  206. package/plugins/devflow-quality/skills/workflow-complete-milestone/SKILL.md +132 -0
  207. package/plugins/devflow-quality/skills/workflow-complete-milestone/assets/milestone-archive.md +123 -0
  208. package/plugins/devflow-quality/skills/workflow-complete-milestone/references/complete-milestone.md +775 -0
  209. package/plugins/devflow-quality/skills/workflow-complete-milestone/references/milestone-archive.md +123 -0
  210. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +126 -0
  211. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/api-mainflow-rules.md +27 -0
  212. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +54 -0
  213. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/intermediate-format.md +244 -0
  214. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/mainflow-selection.md +36 -0
  215. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +190 -0
  216. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/ui-mainflow-rules.md +110 -0
  217. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +583 -0
  218. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +199 -0
  219. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/normalize_case_payloads.py +177 -0
  220. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/validate_case_payloads.py +370 -0
  221. package/plugins/devflow-quality/skills/workflow-verify-work/SKILL.md +106 -0
  222. package/plugins/devflow-quality/skills/workflow-verify-work/assets/UAT.md +265 -0
  223. package/plugins/devflow-quality/skills/workflow-verify-work/references/verify-work.md +759 -0
  224. package/plugins/devflow-requirements/agents/requirements-auditor.md +20 -0
  225. package/plugins/devflow-requirements/package.json +6 -0
  226. package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +219 -0
  227. package/plugins/devflow-requirements/skills/requirement-feasibility-review/agents/openai.yaml +4 -0
  228. package/plugins/devflow-requirements/skills/requirement-feasibility-review/references/review-method.md +257 -0
  229. package/plugins/devflow-requirements/skills/requirement-feasibility-review/references/review-template.md +80 -0
  230. package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +325 -0
  231. package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +163 -0
  232. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +748 -0
  233. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +281 -0
  234. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +69 -0
  235. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +158 -0
  236. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/decomposition-guide.md +115 -0
  237. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +119 -0
  238. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +76 -0
  239. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +115 -0
  240. package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +204 -0
  241. package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +183 -0
  242. package/src/cli.ts +76 -0
  243. package/src/constants.ts +15 -0
  244. package/src/doctor.ts +66 -0
  245. package/src/paths.ts +37 -0
  246. package/src/platform-extension.ts +46 -0
  247. package/src/project-context.ts +165 -0
  248. package/src/runtime.ts +167 -0
  249. package/src/usage-stats.ts +101 -0
  250. package/templates/commands/start.md +58 -0
  251. package/templates/omp/AGENTS.md +36 -0
  252. package/templates/omp/config.yml +112 -0
  253. package/templates/omp/hook-config.json +18 -0
  254. package/templates/omp/index-policy.json +57 -0
@@ -0,0 +1,437 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import contextlib
4
+ import json
5
+ import os
6
+ import shutil
7
+ import sqlite3
8
+ import sys
9
+ import tempfile
10
+ import uuid
11
+ from datetime import datetime, timezone
12
+ from pathlib import Path
13
+ from typing import Dict, Iterable, List, Set, Tuple
14
+
15
+ import fcntl
16
+
17
+ sys.dont_write_bytecode = True
18
+
19
+ SCRIPT_DIR = Path(__file__).resolve().parent
20
+ if str(SCRIPT_DIR) not in sys.path:
21
+ sys.path.insert(0, str(SCRIPT_DIR))
22
+
23
+ from adapters.generic_text import language_for_path
24
+ from adapters.registry import AdapterRegistry
25
+ from core.discovery import discover_codebases
26
+ from core.hashing import file_sha256
27
+ from core.scanner import scan_files
28
+ from validators.project_binding_check import audit_project_binding
29
+ from validators.relation_check import audit_relations
30
+ from validators.schema_validate import validate_index
31
+ from validators.stale_check import audit_stale
32
+ from writers.jsonl_writer import read_jsonl, write_jsonl
33
+ from writers.markdown_writer import write_audit, write_markdown
34
+ from writers.sqlite_writer import write_sqlite
35
+
36
+
37
+ INDEX_FILES = {
38
+ "files": "files.jsonl",
39
+ "symbols": "symbols.jsonl",
40
+ "relations": "relations.jsonl",
41
+ "diagnostics": "diagnostics.jsonl",
42
+ }
43
+
44
+ MARKDOWN_FILES = ["INDEX.md", "SYMBOLS.md", "RELATIONS.md", "ENTRYPOINTS.md", "DATAFLOW.md"]
45
+ SNAPSHOT_FILE = "SNAPSHOT.json"
46
+
47
+
48
+ @contextlib.contextmanager
49
+ def _index_lock(output: Path, exclusive: bool):
50
+ """Serialize index publication and keep readers off a partially published snapshot."""
51
+ output.mkdir(parents=True, exist_ok=True)
52
+ lock_path = output / ".index.lock"
53
+ with lock_path.open("a+") as handle:
54
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
55
+ try:
56
+ yield
57
+ finally:
58
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
59
+
60
+
61
+ def _publish_snapshot(staging: Path, output: Path, counts: Dict[str, int]) -> None:
62
+ generation = str(uuid.uuid4())
63
+ snapshot = {
64
+ "schemaVersion": 1,
65
+ "generation": generation,
66
+ "generatedAt": datetime.now(timezone.utc).isoformat(),
67
+ "counts": counts,
68
+ "artifacts": [*INDEX_FILES.values(), "index.sqlite", *MARKDOWN_FILES],
69
+ }
70
+ snapshot_path = staging / SNAPSHOT_FILE
71
+ snapshot_path.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
72
+ with snapshot_path.open("rb") as handle:
73
+ os.fsync(handle.fileno())
74
+
75
+ # Readers in this module hold the shared lock. SNAPSHOT.json is published
76
+ # last and is therefore the commit marker for external consumers.
77
+ for name in snapshot["artifacts"]:
78
+ os.replace(staging / name, output / name)
79
+ os.replace(snapshot_path, output / SNAPSHOT_FILE)
80
+ directory_fd = os.open(output, os.O_RDONLY)
81
+ try:
82
+ os.fsync(directory_fd)
83
+ finally:
84
+ os.close(directory_fd)
85
+
86
+
87
+ def main(argv=None) -> int:
88
+ parser = argparse.ArgumentParser(description="Build and validate a generic code intelligence index.")
89
+ sub = parser.add_subparsers(dest="command", required=True)
90
+
91
+ scan = sub.add_parser("scan", help="Build or refresh the code index.")
92
+ scan.add_argument("--root", default=".", help="Project root. Defaults to current directory.")
93
+ scan.add_argument("--output", default=".planning/code-index", help="Index output directory.")
94
+ scan.add_argument("--codebase", action="append", default=[], help="Explicit codebase: name:path[:type]. Can repeat.")
95
+ scan.add_argument("--changed-files", nargs="*", default=[], help="Optional changed files for incremental refresh.")
96
+ scan.add_argument("--exclude", action="append", default=[], help="Additional directory names to exclude.")
97
+
98
+ validate = sub.add_parser("validate", help="Validate JSONL schema.")
99
+ validate.add_argument("--index", default=".planning/code-index", help="Index directory.")
100
+
101
+ audit = sub.add_parser("audit", help="Audit stale files and relation integrity.")
102
+ audit.add_argument("--root", default=".", help="Project root.")
103
+ audit.add_argument("--index", default=".planning/code-index", help="Index directory.")
104
+ audit.add_argument("--skill-root", default=".skills", help="Optional skill root for project-binding checks.")
105
+
106
+ query = sub.add_parser("query", help="Search symbols using SQLite FTS or LIKE fallback.")
107
+ query.add_argument("--index", default=".planning/code-index", help="Index directory.")
108
+ query.add_argument("--term", required=True, help="Search term.")
109
+ query.add_argument("--limit", type=int, default=20)
110
+
111
+ args = parser.parse_args(argv)
112
+ if args.command == "scan":
113
+ return command_scan(args)
114
+ if args.command == "validate":
115
+ return command_validate(args)
116
+ if args.command == "audit":
117
+ return command_audit(args)
118
+ if args.command == "query":
119
+ return command_query(args)
120
+ return 2
121
+
122
+
123
+ def command_scan(args) -> int:
124
+ root = Path(args.root).resolve()
125
+ output = (root / args.output).resolve() if not Path(args.output).is_absolute() else Path(args.output).resolve()
126
+ with _index_lock(output, exclusive=True):
127
+ codebases = discover_codebases(root, args.codebase)
128
+ registry = AdapterRegistry()
129
+ changed = _normalize_changed_files(root, args.changed_files)
130
+ existing = _load_existing(output)
131
+ secondary_changed = _incoming_evidence_files(existing, changed)
132
+ changed |= secondary_changed
133
+
134
+ files, symbols, relations, diagnostics = _existing_after_removal(existing, changed)
135
+ indexed_paths = {record["path"] for record in files}
136
+
137
+ for codebase in codebases:
138
+ if not codebase.root.exists():
139
+ diagnostics.append(_diagnostic(codebase.name, "", 1, "warning", "skipped_file", f"codebase root does not exist: {codebase.root}", "discovery@1"))
140
+ continue
141
+ all_files = scan_files(codebase.root, excludes=args.exclude)
142
+ if changed:
143
+ all_files = [path for path in all_files if _workspace_rel(root, path) in changed]
144
+ manifests = [path for path in all_files if path.name in {"package.json", "go.mod", "Cargo.toml", "pyproject.toml", "requirements.txt", "composer.json", "pubspec.yaml"}]
145
+ adapters = registry.adapters_for(codebase, all_files, manifests)
146
+ adapter = adapters[0]
147
+ for path in all_files:
148
+ rel_path = _workspace_rel(root, path)
149
+ if rel_path in indexed_paths:
150
+ continue
151
+ try:
152
+ text = path.read_text(encoding="utf-8", errors="ignore")
153
+ content_hash = file_sha256(path)
154
+ file_record = {
155
+ "id": f"file:{codebase.name}:{rel_path}",
156
+ "codebase": codebase.name,
157
+ "path": rel_path,
158
+ "language": language_for_path(path),
159
+ "size_bytes": path.stat().st_size,
160
+ "content_hash": content_hash,
161
+ "indexed_at": datetime.now(timezone.utc).isoformat(),
162
+ "parser": adapter.parser_id,
163
+ }
164
+ result = adapter.parse_file(codebase, file_record, text)
165
+ files.append(file_record)
166
+ symbols.extend(result.symbols)
167
+ relations.extend(result.relations)
168
+ diagnostics.extend(result.diagnostics)
169
+ except Exception as exc: # keep indexing useful even when one file fails
170
+ diagnostics.append(_diagnostic(codebase.name, rel_path, 1, "error", "parse_failed", str(exc), adapter.parser_id))
171
+
172
+ relations = _resolve_relations(symbols, relations)
173
+ files, symbols, relations, diagnostics = _dedupe(files, symbols, relations, diagnostics)
174
+ errors = validate_index(files, symbols, relations, diagnostics)
175
+ if errors:
176
+ for error in errors[:200]:
177
+ print(error, file=sys.stderr)
178
+ print("Index validation failed; the previous snapshot was preserved.", file=sys.stderr)
179
+ return 1
180
+
181
+ staging = Path(tempfile.mkdtemp(prefix=f".{output.name}.build-", dir=str(output.parent)))
182
+ try:
183
+ write_jsonl(staging / INDEX_FILES["files"], files)
184
+ write_jsonl(staging / INDEX_FILES["symbols"], symbols)
185
+ write_jsonl(staging / INDEX_FILES["relations"], relations)
186
+ write_jsonl(staging / INDEX_FILES["diagnostics"], diagnostics)
187
+ write_sqlite(staging / "index.sqlite", files, symbols, relations, diagnostics)
188
+ write_markdown(staging, files, symbols, relations, diagnostics)
189
+ staged_errors = validate_index(*_load_tuple(staging))
190
+ if staged_errors:
191
+ for error in staged_errors[:200]:
192
+ print(error, file=sys.stderr)
193
+ print("Staged index validation failed; the previous snapshot was preserved.", file=sys.stderr)
194
+ return 1
195
+ _publish_snapshot(
196
+ staging,
197
+ output,
198
+ {
199
+ "files": len(files),
200
+ "symbols": len(symbols),
201
+ "relations": len(relations),
202
+ "diagnostics": len(diagnostics),
203
+ },
204
+ )
205
+ finally:
206
+ shutil.rmtree(staging, ignore_errors=True)
207
+
208
+ print(f"Indexed {len(files)} files, {len(symbols)} symbols, {len(relations)} relations, {len(diagnostics)} diagnostics.")
209
+ print(f"Output: {output}")
210
+ return 0
211
+
212
+
213
+ def command_validate(args) -> int:
214
+ output = Path(args.index).resolve()
215
+ with _index_lock(output, exclusive=False):
216
+ files, symbols, relations, diagnostics = _load_tuple(output)
217
+ errors = validate_index(files, symbols, relations, diagnostics)
218
+ if errors:
219
+ for error in errors:
220
+ print(error)
221
+ return 1
222
+ print("Code index schema validation passed.")
223
+ return 0
224
+
225
+
226
+ def command_audit(args) -> int:
227
+ root = Path(args.root).resolve()
228
+ output = Path(args.index).resolve()
229
+ with _index_lock(output, exclusive=True):
230
+ files, symbols, relations, diagnostics = _load_tuple(output)
231
+ findings = []
232
+ findings.extend(audit_stale(root, files))
233
+ findings.extend(audit_relations(symbols, relations))
234
+ skill_root = Path(args.skill_root)
235
+ if skill_root.exists():
236
+ findings.extend(audit_project_binding([skill_root / "workflow-code-index", skill_root / "code-index-query"]))
237
+ write_audit(output, findings)
238
+ if findings:
239
+ for finding in findings[:200]:
240
+ location = f" {finding.get('path', '')}:{finding.get('line', '')}".rstrip(":")
241
+ print(f"{finding.get('severity', 'warning')}: {finding.get('kind', 'unknown')}: {finding.get('message', '')}{location}")
242
+ return 1
243
+ print("Code index audit passed.")
244
+ return 0
245
+
246
+
247
+ def command_query(args) -> int:
248
+ output = Path(args.index).resolve()
249
+ with _index_lock(output, exclusive=False):
250
+ index = output / "index.sqlite"
251
+ if not index.exists():
252
+ print(f"Missing index: {index}", file=sys.stderr)
253
+ return 1
254
+ conn = sqlite3.connect(str(index))
255
+ conn.row_factory = sqlite3.Row
256
+ try:
257
+ try:
258
+ rows = conn.execute(
259
+ """
260
+ SELECT id, codebase, kind, name, qualified_name, path, line_start, line_end
261
+ FROM symbols_fts
262
+ JOIN symbols USING(id)
263
+ WHERE symbols_fts MATCH ?
264
+ LIMIT ?
265
+ """,
266
+ (args.term, args.limit),
267
+ ).fetchall()
268
+ except sqlite3.OperationalError:
269
+ like = f"%{args.term}%"
270
+ rows = conn.execute(
271
+ """
272
+ SELECT id, codebase, kind, name, qualified_name, path, line_start, line_end
273
+ FROM symbols
274
+ WHERE name LIKE ? OR qualified_name LIKE ? OR path LIKE ?
275
+ LIMIT ?
276
+ """,
277
+ (like, like, like, args.limit),
278
+ ).fetchall()
279
+ for row in rows:
280
+ print(json.dumps(dict(row), ensure_ascii=False, sort_keys=True))
281
+ finally:
282
+ conn.close()
283
+ return 0
284
+
285
+
286
+ def _load_tuple(output: Path):
287
+ return (
288
+ read_jsonl(output / INDEX_FILES["files"]),
289
+ read_jsonl(output / INDEX_FILES["symbols"]),
290
+ read_jsonl(output / INDEX_FILES["relations"]),
291
+ read_jsonl(output / INDEX_FILES["diagnostics"]),
292
+ )
293
+
294
+
295
+ def _load_existing(output: Path) -> Dict[str, List[dict]]:
296
+ return {
297
+ "files": read_jsonl(output / INDEX_FILES["files"]),
298
+ "symbols": read_jsonl(output / INDEX_FILES["symbols"]),
299
+ "relations": read_jsonl(output / INDEX_FILES["relations"]),
300
+ "diagnostics": read_jsonl(output / INDEX_FILES["diagnostics"]),
301
+ }
302
+
303
+
304
+ def _normalize_changed_files(root: Path, values: Iterable[str]) -> Set[str]:
305
+ result: Set[str] = set()
306
+ for value in values:
307
+ path = Path(value)
308
+ if not path.is_absolute():
309
+ path = root / path
310
+ if path.exists():
311
+ result.add(_workspace_rel(root, path))
312
+ else:
313
+ try:
314
+ result.add(str(path.resolve().relative_to(root)).replace(os.sep, "/"))
315
+ except ValueError:
316
+ result.add(value.replace(os.sep, "/"))
317
+ return result
318
+
319
+
320
+ def _incoming_evidence_files(existing: Dict[str, List[dict]], changed: Set[str]) -> Set[str]:
321
+ if not changed:
322
+ return set()
323
+ changed_symbol_ids = {symbol["id"] for symbol in existing["symbols"] if symbol.get("path") in changed}
324
+ return {
325
+ relation["evidence_path"]
326
+ for relation in existing["relations"]
327
+ if relation.get("to_id") in changed_symbol_ids and relation.get("evidence_path")
328
+ }
329
+
330
+
331
+ def _existing_after_removal(existing: Dict[str, List[dict]], changed: Set[str]):
332
+ if not changed:
333
+ return [], [], [], []
334
+ changed_symbol_ids = {symbol["id"] for symbol in existing["symbols"] if symbol.get("path") in changed}
335
+ files = [record for record in existing["files"] if record.get("path") not in changed]
336
+ symbols = [record for record in existing["symbols"] if record.get("path") not in changed]
337
+ relations = [
338
+ record
339
+ for record in existing["relations"]
340
+ if record.get("evidence_path") not in changed
341
+ and record.get("from_id") not in changed_symbol_ids
342
+ and record.get("to_id") not in changed_symbol_ids
343
+ ]
344
+ diagnostics = [record for record in existing["diagnostics"] if record.get("path") not in changed]
345
+ return files, symbols, relations, diagnostics
346
+
347
+
348
+ def _resolve_relations(symbols: List[dict], relations: List[dict]) -> List[dict]:
349
+ by_name: Dict[Tuple[str, str], List[dict]] = {}
350
+ by_id = {symbol["id"]: symbol for symbol in symbols}
351
+ for symbol in symbols:
352
+ by_name.setdefault((symbol["codebase"], symbol["name"]), []).append(symbol)
353
+
354
+ resolved = []
355
+ for relation in relations:
356
+ if relation.get("to_id") or not relation.get("to_external", "").startswith("call:"):
357
+ resolved.append(relation)
358
+ continue
359
+ source = by_id.get(relation["from_id"])
360
+ if not source:
361
+ resolved.append(relation)
362
+ continue
363
+ name = relation["to_external"][5:]
364
+ candidates = by_name.get((source["codebase"], name), [])
365
+ if len(candidates) == 1:
366
+ upgraded = dict(relation)
367
+ upgraded["to_id"] = candidates[0]["id"]
368
+ upgraded["confidence"] = "inferred" if upgraded["confidence"] == "heuristic" else upgraded["confidence"]
369
+ upgraded.setdefault("metadata", {})["resolution"] = "unique_symbol_name_in_codebase"
370
+ resolved.append(upgraded)
371
+ else:
372
+ resolved.append(relation)
373
+ return resolved
374
+
375
+
376
+ def _dedupe(files, symbols, relations, diagnostics):
377
+ files = _dedupe_by(files, lambda record: record["id"])
378
+ symbols = _dedupe_by(symbols, lambda record: record["id"])
379
+ relations = _dedupe_by(
380
+ relations,
381
+ lambda record: (
382
+ record.get("from_id"),
383
+ record.get("relation_type"),
384
+ record.get("to_id"),
385
+ record.get("to_external"),
386
+ record.get("evidence_path"),
387
+ record.get("evidence_line"),
388
+ record.get("confidence"),
389
+ ),
390
+ )
391
+ diagnostics = _dedupe_by(
392
+ diagnostics,
393
+ lambda record: (
394
+ record.get("codebase"),
395
+ record.get("path"),
396
+ record.get("line"),
397
+ record.get("severity"),
398
+ record.get("kind"),
399
+ record.get("message"),
400
+ ),
401
+ )
402
+ return files, symbols, relations, diagnostics
403
+
404
+
405
+ def _dedupe_by(records, key_fn):
406
+ seen = set()
407
+ result = []
408
+ for record in records:
409
+ key = key_fn(record)
410
+ if key in seen:
411
+ continue
412
+ seen.add(key)
413
+ result.append(record)
414
+ return result
415
+
416
+
417
+ def _workspace_rel(root: Path, path: Path) -> str:
418
+ try:
419
+ return str(path.resolve().relative_to(root)).replace(os.sep, "/")
420
+ except ValueError:
421
+ return os.path.relpath(str(path.resolve()), str(root)).replace(os.sep, "/")
422
+
423
+
424
+ def _diagnostic(codebase, path, line, severity, kind, message, parser):
425
+ return {
426
+ "codebase": codebase or "workspace",
427
+ "path": path,
428
+ "line": line,
429
+ "severity": severity,
430
+ "kind": kind,
431
+ "message": message,
432
+ "parser": parser,
433
+ }
434
+
435
+
436
+ if __name__ == "__main__":
437
+ raise SystemExit(main())
@@ -0,0 +1,139 @@
1
+ import re
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+ from typing import Iterable, List, Optional
5
+
6
+
7
+ @dataclass
8
+ class Codebase:
9
+ name: str
10
+ root: Path
11
+ type_hint: str = ""
12
+ stack_hint: str = ""
13
+ navigation_path: str = ""
14
+ spec_path: str = ""
15
+
16
+
17
+ def discover_codebases(root: Path, explicit: Iterable[str] = ()) -> List[Codebase]:
18
+ root = root.resolve()
19
+ explicit_codebases = [_parse_explicit(root, value) for value in explicit]
20
+ explicit_codebases = [item for item in explicit_codebases if item is not None]
21
+ if explicit_codebases:
22
+ return explicit_codebases
23
+
24
+ nav_codebases = _from_navigation(root)
25
+ if nav_codebases:
26
+ return nav_codebases
27
+
28
+ spec_codebases = _from_spec_index(root)
29
+ if spec_codebases:
30
+ return spec_codebases
31
+
32
+ return [Codebase(name=root.name or "workspace", root=root)]
33
+
34
+
35
+ def _parse_explicit(root: Path, value: str) -> Optional[Codebase]:
36
+ parts = value.split(":", 2)
37
+ if len(parts) < 2:
38
+ return None
39
+ name, raw_path = parts[0].strip(), parts[1].strip()
40
+ type_hint = parts[2].strip() if len(parts) == 3 else ""
41
+ path = Path(raw_path)
42
+ if not path.is_absolute():
43
+ path = root / path
44
+ return Codebase(name=name, root=path.resolve(), type_hint=type_hint)
45
+
46
+
47
+ def _from_navigation(root: Path) -> List[Codebase]:
48
+ index = root / ".planning/navigation/INDEX.md"
49
+ if not index.exists():
50
+ return []
51
+ rows = _read_markdown_table(index)
52
+ result: List[Codebase] = []
53
+ for row in rows:
54
+ name = row.get("Name") or row.get("Codebase") or row.get("name")
55
+ raw_path = row.get("Path") or row.get("Root") or row.get("path")
56
+ if not name or not raw_path:
57
+ continue
58
+ path = _clean_cell(raw_path)
59
+ codebase_root = Path(path)
60
+ if not codebase_root.is_absolute():
61
+ codebase_root = root / codebase_root
62
+ nav = _clean_cell(row.get("Navigation", ""))
63
+ result.append(
64
+ Codebase(
65
+ name=_clean_cell(name),
66
+ root=codebase_root.resolve(),
67
+ type_hint=_clean_cell(row.get("Type", "")),
68
+ navigation_path=nav,
69
+ )
70
+ )
71
+ return [item for item in result if item.root.exists()]
72
+
73
+
74
+ def _from_spec_index(root: Path) -> List[Codebase]:
75
+ index = root / ".context/spec/index.md"
76
+ if not index.exists():
77
+ return []
78
+ rows = _read_markdown_table(index)
79
+ result: List[Codebase] = []
80
+ for row in rows:
81
+ name = row.get("Codebase") or row.get("Name")
82
+ nav = row.get("Navigation", "")
83
+ spec = row.get("Codebase Spec", "")
84
+ stack = row.get("Stack Spec", "")
85
+ if not name:
86
+ continue
87
+ nav_path = root / _clean_cell(nav)
88
+ codebase_root = _root_from_navigation_file(root, nav_path)
89
+ if codebase_root:
90
+ result.append(
91
+ Codebase(
92
+ name=_clean_cell(name),
93
+ root=codebase_root,
94
+ stack_hint=_clean_cell(stack),
95
+ navigation_path=_clean_cell(nav),
96
+ spec_path=_clean_cell(spec),
97
+ )
98
+ )
99
+ return result
100
+
101
+
102
+ def _root_from_navigation_file(project_root: Path, path: Path) -> Optional[Path]:
103
+ if not path.exists():
104
+ return None
105
+ text = path.read_text(encoding="utf-8", errors="ignore")
106
+ match = re.search(r"\|\s*Root\s*\|\s*`?([^`|\n]+)`?\s*\|", text)
107
+ if not match:
108
+ return None
109
+ raw = match.group(1).strip()
110
+ root = Path(raw)
111
+ if not root.is_absolute():
112
+ root = project_root / root
113
+ return root.resolve() if root.exists() else None
114
+
115
+
116
+ def _read_markdown_table(path: Path) -> List[dict]:
117
+ rows: List[dict] = []
118
+ header = None
119
+ for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
120
+ stripped = line.strip()
121
+ if not stripped.startswith("|") or not stripped.endswith("|"):
122
+ continue
123
+ cells = [cell.strip() for cell in stripped.strip("|").split("|")]
124
+ if all(re.match(r"^:?-{3,}:?$", cell) for cell in cells):
125
+ continue
126
+ if header is None:
127
+ header = cells
128
+ continue
129
+ if len(cells) == len(header):
130
+ rows.append(dict(zip(header, cells)))
131
+ return rows
132
+
133
+
134
+ def _clean_cell(value: str) -> str:
135
+ value = value.strip()
136
+ if value.startswith("`") and value.endswith("`"):
137
+ value = value[1:-1]
138
+ return value.strip()
139
+
@@ -0,0 +1,11 @@
1
+ import hashlib
2
+ from pathlib import Path
3
+
4
+
5
+ def file_sha256(path: Path) -> str:
6
+ digest = hashlib.sha256()
7
+ with path.open("rb") as handle:
8
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
9
+ digest.update(chunk)
10
+ return "sha256:" + digest.hexdigest()
11
+