periplus-map 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. periplus/__init__.py +70 -0
  2. periplus/checkmap.py +80 -0
  3. periplus/cli.py +658 -0
  4. periplus/contract/schema/map.schema.json +138 -0
  5. periplus/contract/schema/navigation.schema.json +200 -0
  6. periplus/contract/schema/pack-file.schema.json +1405 -0
  7. periplus/contract/schema/pack-manifest.schema.json +245 -0
  8. periplus/contract/schema/project-settings.schema.json +85 -0
  9. periplus/contract/spec/engine.md +151 -0
  10. periplus/contract/spec/rules.md +352 -0
  11. periplus/detect.py +228 -0
  12. periplus/engine/__init__.py +5 -0
  13. periplus/engine/check.py +481 -0
  14. periplus/engine/emit.py +368 -0
  15. periplus/engine/packload.py +2321 -0
  16. periplus/engine/pattern.py +27 -0
  17. periplus/engine/rules.py +1946 -0
  18. periplus/engine/select.py +491 -0
  19. periplus/engine/source.py +172 -0
  20. periplus/engine/tree.py +424 -0
  21. periplus/errors.py +332 -0
  22. periplus/init.py +358 -0
  23. periplus/layout.py +28 -0
  24. periplus/manifest.py +498 -0
  25. periplus/map.py +364 -0
  26. periplus/navigate/__init__.py +1 -0
  27. periplus/navigate/find.py +143 -0
  28. periplus/navigate/ids.py +30 -0
  29. periplus/navigate/listing.py +42 -0
  30. periplus/navigate/mapfile.py +189 -0
  31. periplus/navigate/path.py +175 -0
  32. periplus/navigate/show.py +175 -0
  33. periplus/navigate/types.py +83 -0
  34. periplus/navigate/walk.py +138 -0
  35. periplus/packs/advancedqueue_basic@0.0.1/pack.yaml +14 -0
  36. periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type.yaml +9 -0
  37. periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type_annotation.yaml +43 -0
  38. periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type_attribute.yaml +43 -0
  39. periplus/packs/ai_basic@0.0.1/ai_agent/ai_agent.yaml +8 -0
  40. periplus/packs/ai_basic@0.0.1/ai_agent/ai_agent_attribute.yaml +43 -0
  41. periplus/packs/ai_basic@0.0.1/ai_function_call/ai_function_call.yaml +8 -0
  42. periplus/packs/ai_basic@0.0.1/ai_function_call/ai_function_call_attribute.yaml +43 -0
  43. periplus/packs/ai_basic@0.0.1/pack.yaml +15 -0
  44. periplus/packs/config_pages_basic@0.0.1/bundle/config_pages_bundle_config.yaml +54 -0
  45. periplus/packs/config_pages_basic@0.0.1/pack.yaml +11 -0
  46. periplus/packs/crop_basic@0.0.1/bundle/crop_bundle_config.yaml +54 -0
  47. periplus/packs/crop_basic@0.0.1/pack.yaml +11 -0
  48. periplus/packs/drupal@0.1.0/block/block_placement.yaml +25 -0
  49. periplus/packs/drupal@0.1.0/block/block_placement_code.yaml +58 -0
  50. periplus/packs/drupal@0.1.0/block/block_placement_config.yaml +53 -0
  51. periplus/packs/drupal@0.1.0/block/block_plugin.yaml +52 -0
  52. periplus/packs/drupal@0.1.0/block/block_plugin_altered.yaml +61 -0
  53. periplus/packs/drupal@0.1.0/block/block_plugin_annotation.yaml +69 -0
  54. periplus/packs/drupal@0.1.0/block/block_plugin_attribute.yaml +56 -0
  55. periplus/packs/drupal@0.1.0/block/block_plugin_content.yaml +46 -0
  56. periplus/packs/drupal@0.1.0/block/block_plugin_derived.yaml +110 -0
  57. periplus/packs/drupal@0.1.0/block/block_plugin_deriver.yaml +30 -0
  58. periplus/packs/drupal@0.1.0/block/block_type.yaml +14 -0
  59. periplus/packs/drupal@0.1.0/block/block_type_code.yaml +43 -0
  60. periplus/packs/drupal@0.1.0/block/block_type_config.yaml +45 -0
  61. periplus/packs/drupal@0.1.0/cache/cache.yaml +18 -0
  62. periplus/packs/drupal@0.1.0/cache/cache_backend.yaml +31 -0
  63. periplus/packs/drupal@0.1.0/component/sdc.yaml +19 -0
  64. periplus/packs/drupal@0.1.0/component/sdc_definition.yaml +25 -0
  65. periplus/packs/drupal@0.1.0/component/sdc_use.yaml +25 -0
  66. periplus/packs/drupal@0.1.0/config/config_object.yaml +65 -0
  67. periplus/packs/drupal@0.1.0/display/display.yaml +32 -0
  68. periplus/packs/drupal@0.1.0/display/display_code.yaml +53 -0
  69. periplus/packs/drupal@0.1.0/display/display_config.yaml +95 -0
  70. periplus/packs/drupal@0.1.0/entity/entity.yaml +33 -0
  71. periplus/packs/drupal@0.1.0/entity/entity_class.yaml +60 -0
  72. periplus/packs/drupal@0.1.0/entity/entity_save.yaml +27 -0
  73. periplus/packs/drupal@0.1.0/entity/entity_type_annotation.yaml +85 -0
  74. periplus/packs/drupal@0.1.0/entity/entity_type_attribute.yaml +76 -0
  75. periplus/packs/drupal@0.1.0/field/field_instance_code.yaml +40 -0
  76. periplus/packs/drupal@0.1.0/field/field_instance_config.yaml +58 -0
  77. periplus/packs/drupal@0.1.0/field/field_storage.yaml +37 -0
  78. periplus/packs/drupal@0.1.0/field/field_storage_code.yaml +39 -0
  79. periplus/packs/drupal@0.1.0/field/field_storage_config.yaml +24 -0
  80. periplus/packs/drupal@0.1.0/form/form.yaml +48 -0
  81. periplus/packs/drupal@0.1.0/form/form_class.yaml +50 -0
  82. periplus/packs/drupal@0.1.0/form/form_id.yaml +48 -0
  83. periplus/packs/drupal@0.1.0/form/form_methods.yaml +39 -0
  84. periplus/packs/drupal@0.1.0/hook/hook.yaml +8 -0
  85. periplus/packs/drupal@0.1.0/hook/hook_attribute.yaml +28 -0
  86. periplus/packs/drupal@0.1.0/module/module.yaml +8 -0
  87. periplus/packs/drupal@0.1.0/module/module_info.yaml +50 -0
  88. periplus/packs/drupal@0.1.0/pack.yaml +184 -0
  89. periplus/packs/drupal@0.1.0/plugin/plugin.yaml +17 -0
  90. periplus/packs/drupal@0.1.0/plugin/plugin_annotation.yaml +120 -0
  91. periplus/packs/drupal@0.1.0/recommendations/recommendations.yaml +14 -0
  92. periplus/packs/drupal@0.1.0/service/service.yaml +19 -0
  93. periplus/packs/drupal@0.1.0/service/service_autowire.yaml +28 -0
  94. periplus/packs/drupal@0.1.0/service/service_definition.yaml +24 -0
  95. periplus/packs/drupal@0.1.0/service/service_use.yaml +43 -0
  96. periplus/packs/drupal_basic@0.3.0/action/action.yaml +9 -0
  97. periplus/packs/drupal_basic@0.3.0/action/action_annotation.yaml +43 -0
  98. periplus/packs/drupal_basic@0.3.0/action/action_attribute.yaml +43 -0
  99. periplus/packs/drupal_basic@0.3.0/block/block_placement.yaml +16 -0
  100. periplus/packs/drupal_basic@0.3.0/block/block_placement_config.yaml +51 -0
  101. periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin.yaml +14 -0
  102. periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin_annotation.yaml +47 -0
  103. periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin_attribute.yaml +43 -0
  104. periplus/packs/drupal_basic@0.3.0/block_type/block_type.yaml +8 -0
  105. periplus/packs/drupal_basic@0.3.0/block_type/block_type_config.yaml +54 -0
  106. periplus/packs/drupal_basic@0.3.0/boundary/boundary_types.yaml +19 -0
  107. periplus/packs/drupal_basic@0.3.0/bundle/bundle.yaml +20 -0
  108. periplus/packs/drupal_basic@0.3.0/bundle/bundle_config.yaml +393 -0
  109. periplus/packs/drupal_basic@0.3.0/call/call.yaml +349 -0
  110. periplus/packs/drupal_basic@0.3.0/component/component.yaml +30 -0
  111. periplus/packs/drupal_basic@0.3.0/config/config_object.yaml +119 -0
  112. periplus/packs/drupal_basic@0.3.0/constraint/constraint.yaml +8 -0
  113. periplus/packs/drupal_basic@0.3.0/constraint/constraint_annotation.yaml +43 -0
  114. periplus/packs/drupal_basic@0.3.0/constraint/constraint_attribute.yaml +43 -0
  115. periplus/packs/drupal_basic@0.3.0/display/display.yaml +30 -0
  116. periplus/packs/drupal_basic@0.3.0/display/display_config.yaml +203 -0
  117. periplus/packs/drupal_basic@0.3.0/display/display_mode_config.yaml +60 -0
  118. periplus/packs/drupal_basic@0.3.0/entity_type/config_entity_type_annotation.yaml +46 -0
  119. periplus/packs/drupal_basic@0.3.0/entity_type/config_entity_type_attribute.yaml +43 -0
  120. periplus/packs/drupal_basic@0.3.0/entity_type/content_entity_type_annotation.yaml +46 -0
  121. periplus/packs/drupal_basic@0.3.0/entity_type/content_entity_type_attribute.yaml +43 -0
  122. periplus/packs/drupal_basic@0.3.0/entity_type/entity_type.yaml +16 -0
  123. periplus/packs/drupal_basic@0.3.0/field/field_instance_config.yaml +116 -0
  124. periplus/packs/drupal_basic@0.3.0/field/field_storage.yaml +21 -0
  125. periplus/packs/drupal_basic@0.3.0/field/field_storage_config.yaml +45 -0
  126. periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter.yaml +8 -0
  127. periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter_annotation.yaml +46 -0
  128. periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter_attribute.yaml +43 -0
  129. periplus/packs/drupal_basic@0.3.0/field_type/field_type_annotation.yaml +46 -0
  130. periplus/packs/drupal_basic@0.3.0/field_type/field_type_attribute.yaml +50 -0
  131. periplus/packs/drupal_basic@0.3.0/field_widget/field_widget_annotation.yaml +46 -0
  132. periplus/packs/drupal_basic@0.3.0/field_widget/field_widget_attribute.yaml +50 -0
  133. periplus/packs/drupal_basic@0.3.0/form/form_id.yaml +80 -0
  134. periplus/packs/drupal_basic@0.3.0/hook/hook.yaml +100 -0
  135. periplus/packs/drupal_basic@0.3.0/hook/hook_attribute.yaml +64 -0
  136. periplus/packs/drupal_basic@0.3.0/hook/hook_theme_include.yaml +34 -0
  137. periplus/packs/drupal_basic@0.3.0/library/library.yaml +12 -0
  138. periplus/packs/drupal_basic@0.3.0/library/library_definition.yaml +58 -0
  139. periplus/packs/drupal_basic@0.3.0/local_action/local_action.yaml +12 -0
  140. periplus/packs/drupal_basic@0.3.0/local_action/local_action_definition.yaml +49 -0
  141. periplus/packs/drupal_basic@0.3.0/mail/mail.yaml +8 -0
  142. periplus/packs/drupal_basic@0.3.0/mail/mail_attribute.yaml +43 -0
  143. periplus/packs/drupal_basic@0.3.0/media_source/media_source.yaml +8 -0
  144. periplus/packs/drupal_basic@0.3.0/media_source/media_source_annotation.yaml +43 -0
  145. periplus/packs/drupal_basic@0.3.0/media_source/media_source_attribute.yaml +79 -0
  146. periplus/packs/drupal_basic@0.3.0/menu_link/menu_link.yaml +18 -0
  147. periplus/packs/drupal_basic@0.3.0/menu_link/menu_link_definition.yaml +48 -0
  148. periplus/packs/drupal_basic@0.3.0/module/module.yaml +7 -0
  149. periplus/packs/drupal_basic@0.3.0/module/module_info.yaml +58 -0
  150. periplus/packs/drupal_basic@0.3.0/pack.yaml +528 -0
  151. periplus/packs/drupal_basic@0.3.0/permission/permission.yaml +7 -0
  152. periplus/packs/drupal_basic@0.3.0/permission/permission_definition.yaml +27 -0
  153. periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker.yaml +9 -0
  154. periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker_annotation.yaml +43 -0
  155. periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker_attribute.yaml +43 -0
  156. periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource.yaml +8 -0
  157. periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource_annotation.yaml +43 -0
  158. periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource_attribute.yaml +43 -0
  159. periplus/packs/drupal_basic@0.3.0/role/role.yaml +14 -0
  160. periplus/packs/drupal_basic@0.3.0/role/role_config.yaml +44 -0
  161. periplus/packs/drupal_basic@0.3.0/route/route.yaml +42 -0
  162. periplus/packs/drupal_basic@0.3.0/route/route_definition.yaml +113 -0
  163. periplus/packs/drupal_basic@0.3.0/route/route_options.yaml +55 -0
  164. periplus/packs/drupal_basic@0.3.0/route/route_requirements.yaml +108 -0
  165. periplus/packs/drupal_basic@0.3.0/service/service.yaml +56 -0
  166. periplus/packs/drupal_basic@0.3.0/service/service_definition.yaml +181 -0
  167. periplus/packs/drupal_basic@0.3.0/template/template.yaml +86 -0
  168. periplus/packs/drupal_basic@0.3.0/template/template_route_link.yaml +30 -0
  169. periplus/packs/drupal_basic@0.3.0/theme/theme.yaml +15 -0
  170. periplus/packs/drupal_basic@0.3.0/theme/theme_info.yaml +61 -0
  171. periplus/packs/drupal_basic@0.3.0/theme_hook/template_suggestion.yaml +29 -0
  172. periplus/packs/drupal_basic@0.3.0/theme_hook/theme_hook.yaml +87 -0
  173. periplus/packs/drupal_basic@0.3.0/theme_hook/theme_hook_render.yaml +20 -0
  174. periplus/packs/drupal_basic@0.3.0/url/url.yaml +88 -0
  175. periplus/packs/drupal_basic@0.3.0/views_plugin/views_field_attribute.yaml +80 -0
  176. periplus/packs/drupal_basic@0.3.0/views_plugin/views_filter_attribute.yaml +80 -0
  177. periplus/packs/drupal_basic@0.3.0/views_plugin/views_plugin.yaml +15 -0
  178. periplus/packs/drupal_basic@0.3.0/views_plugin/views_style_attribute.yaml +80 -0
  179. periplus/packs/drupal_js_basic@0.0.1/attachment/attachment.yaml +12 -0
  180. periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_php.yaml +15 -0
  181. periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_php_array.yaml +52 -0
  182. periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_twig.yaml +26 -0
  183. periplus/packs/drupal_js_basic@0.0.1/attachment/php_function_holder.yaml +13 -0
  184. periplus/packs/drupal_js_basic@0.0.1/behavior/behavior.yaml +198 -0
  185. periplus/packs/drupal_js_basic@0.0.1/pack.yaml +153 -0
  186. periplus/packs/drupal_js_basic@0.0.1/setting/setting.yaml +13 -0
  187. periplus/packs/drupal_js_basic@0.0.1/setting/setting_in_js.yaml +15 -0
  188. periplus/packs/drupal_js_basic@0.0.1/setting/setting_in_php.yaml +92 -0
  189. periplus/packs/drush_basic@0.0.1/command/command.yaml +15 -0
  190. periplus/packs/drush_basic@0.0.1/command/command_annotation.yaml +60 -0
  191. periplus/packs/drush_basic@0.0.1/command/command_attribute.yaml +88 -0
  192. periplus/packs/drush_basic@0.0.1/pack.yaml +29 -0
  193. periplus/packs/eck_basic@0.0.1/bundle/eck_bundle_config.yaml +56 -0
  194. periplus/packs/eck_basic@0.0.1/entity_type/eck_entity_type_config.yaml +26 -0
  195. periplus/packs/eck_basic@0.0.1/pack.yaml +28 -0
  196. periplus/packs/go@0.0.2/interfaces/satisfies.yaml +47 -0
  197. periplus/packs/go@0.0.2/pack.yaml +51 -0
  198. periplus/packs/go@0.0.2/types/find_declarations.yaml +137 -0
  199. periplus/packs/go@0.0.2/types/node_types.yaml +36 -0
  200. periplus/packs/go_basic@0.0.3/pack.yaml +104 -0
  201. periplus/packs/go_basic@0.0.3/rules/calls.yaml +25 -0
  202. periplus/packs/go_basic@0.0.3/rules/declarations.yaml +40 -0
  203. periplus/packs/go_basic@0.0.3/rules/imports.yaml +16 -0
  204. periplus/packs/go_basic@0.0.3/types/node_types.yaml +15 -0
  205. periplus/packs/js_basic@0.0.1/function/function.yaml +68 -0
  206. periplus/packs/js_basic@0.0.1/pack.yaml +50 -0
  207. periplus/packs/laravel_basic@0.0.5/pack.yaml +313 -0
  208. periplus/packs/laravel_basic@0.0.5/rules/classes.yaml +42 -0
  209. periplus/packs/laravel_basic@0.0.5/rules/config.yaml +229 -0
  210. periplus/packs/laravel_basic@0.0.5/rules/routes.yaml +210 -0
  211. periplus/packs/laravel_basic@0.0.5/rules/types.yaml +50 -0
  212. periplus/packs/laravel_basic@0.0.5/rules/views.yaml +100 -0
  213. periplus/packs/paragraphs_basic@0.0.1/bundle/paragraph_bundle_config.yaml +54 -0
  214. periplus/packs/paragraphs_basic@0.0.1/pack.yaml +12 -0
  215. periplus/packs/php@0.1.0/edges/edge_kinds.yaml +64 -0
  216. periplus/packs/php@0.1.0/edges/find_calls.yaml +34 -0
  217. periplus/packs/php@0.1.0/edges/find_imports.yaml +18 -0
  218. periplus/packs/php@0.1.0/edges/find_member_access.yaml +22 -0
  219. periplus/packs/php@0.1.0/edges/find_structure.yaml +38 -0
  220. periplus/packs/php@0.1.0/embedded/embedded_regions.yaml +19 -0
  221. periplus/packs/php@0.1.0/imports/imports.yaml +20 -0
  222. periplus/packs/php@0.1.0/pack.yaml +117 -0
  223. periplus/packs/php@0.1.0/resolution/excluded_tokens.yaml +23 -0
  224. periplus/packs/php@0.1.0/resolution/resolution.yaml +35 -0
  225. periplus/packs/php@0.1.0/types/find_declarations.yaml +102 -0
  226. periplus/packs/php@0.1.0/types/node_types.yaml +55 -0
  227. periplus/packs/php_basic@0.2.0/edges/edge_kinds.yaml +48 -0
  228. periplus/packs/php_basic@0.2.0/edges/find_structure.yaml +750 -0
  229. periplus/packs/php_basic@0.2.0/imports/imports.yaml +19 -0
  230. periplus/packs/php_basic@0.2.0/pack.yaml +94 -0
  231. periplus/packs/php_basic@0.2.0/types/find_declarations.yaml +267 -0
  232. periplus/packs/php_basic@0.2.0/types/node_types.yaml +55 -0
  233. periplus/packs/profile_basic@0.0.1/bundle/profile_bundle_config.yaml +54 -0
  234. periplus/packs/profile_basic@0.0.1/pack.yaml +12 -0
  235. periplus/packs/salesforce_basic@0.0.1/event/salesforce_event.yaml +36 -0
  236. periplus/packs/salesforce_basic@0.0.1/pack.yaml +31 -0
  237. periplus/packs/salesforce_basic@0.0.1/salesforce_mapping/salesforce_mapping.yaml +19 -0
  238. periplus/packs/salesforce_basic@0.0.1/salesforce_mapping/salesforce_mapping_config.yaml +83 -0
  239. periplus/packs/salesforce_basic@0.0.1/salesforce_mapping_field/salesforce_mapping_field.yaml +8 -0
  240. periplus/packs/salesforce_basic@0.0.1/salesforce_mapping_field/salesforce_mapping_field_annotation.yaml +43 -0
  241. periplus/packs/twig@0.0.1/pack.yaml +26 -0
  242. periplus/packs/twig@0.0.1/types/template.yaml +57 -0
  243. periplus/packs/twig_basic@0.1.0/pack.yaml +11 -0
  244. periplus/packs/twig_tweak_basic@0.0.1/pack.yaml +27 -0
  245. periplus/packs/twig_tweak_basic@0.0.1/rules/block.yaml +20 -0
  246. periplus/packs/twig_tweak_basic@0.0.1/rules/entity.yaml +35 -0
  247. periplus/packs/twig_tweak_basic@0.0.1/rules/field.yaml +21 -0
  248. periplus/packs/twig_tweak_basic@0.0.1/rules/menu.yaml +21 -0
  249. periplus/packs/webform_basic@0.0.1/bundle/webform_submission_bundle_config.yaml +54 -0
  250. periplus/packs/webform_basic@0.0.1/pack.yaml +20 -0
  251. periplus/packs/webform_basic@0.0.1/webform_handler/webform_handler.yaml +8 -0
  252. periplus/packs/webform_basic@0.0.1/webform_handler/webform_handler_annotation.yaml +43 -0
  253. periplus/packs/yaml@0.0.1/pack.yaml +72 -0
  254. periplus/packs/yaml@0.0.1/types/find_mapping_entry.yaml +21 -0
  255. periplus/packs/yaml@0.0.1/types/yaml_types.yaml +44 -0
  256. periplus/packs/yaml_basic@0.1.0/pack.yaml +10 -0
  257. periplus/packs.py +447 -0
  258. periplus/preflight.py +102 -0
  259. periplus/py.typed +0 -0
  260. periplus/report.py +596 -0
  261. periplus/resolution.py +355 -0
  262. periplus/settings.py +617 -0
  263. periplus/settings.stub.yml +99 -0
  264. periplus/spec.py +774 -0
  265. periplus/update.py +197 -0
  266. periplus/validate.py +655 -0
  267. periplus_map-0.2.1.dist-info/METADATA +566 -0
  268. periplus_map-0.2.1.dist-info/RECORD +272 -0
  269. periplus_map-0.2.1.dist-info/WHEEL +4 -0
  270. periplus_map-0.2.1.dist-info/entry_points.txt +2 -0
  271. periplus_map-0.2.1.dist-info/licenses/LICENSE +202 -0
  272. periplus_map-0.2.1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,566 @@
1
+ Metadata-Version: 2.5
2
+ Name: periplus-map
3
+ Version: 0.2.1
4
+ Summary: A schema-first, deterministic codebase mapper, configurable to any stack.
5
+ Project-URL: Homepage, https://github.com/camoa/periplus
6
+ Project-URL: Repository, https://github.com/camoa/periplus
7
+ Project-URL: Changelog, https://github.com/camoa/periplus/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/camoa/periplus/issues
9
+ Author: camoa
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ License-File: NOTICE
13
+ Keywords: codebase,deterministic,map,static-analysis
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: jsonschema==4.26.0
25
+ Requires-Dist: platformdirs==4.11.5
26
+ Requires-Dist: ruamel-yaml==0.19.1
27
+ Requires-Dist: tree-sitter-php==0.24.1
28
+ Requires-Dist: tree-sitter==0.26.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: mypy==2.3.1; extra == 'dev'
31
+ Requires-Dist: pytest==9.1.1; extra == 'dev'
32
+ Requires-Dist: ruff==0.15.0; extra == 'dev'
33
+ Requires-Dist: types-jsonschema==4.26.0.20260518; extra == 'dev'
34
+ Provides-Extra: go
35
+ Requires-Dist: tree-sitter-go==0.25.0; extra == 'go'
36
+ Provides-Extra: js
37
+ Requires-Dist: tree-sitter-javascript==0.25.0; extra == 'js'
38
+ Description-Content-Type: text/markdown
39
+
40
+ <div align="center">
41
+
42
+ # Periplus
43
+
44
+ **A map of a code base in one JSON file · rutters that say what to find · the same map every run**
45
+
46
+ [![checks](https://img.shields.io/github/actions/workflow/status/camoa/periplus/checks.yml?branch=main)](https://github.com/camoa/periplus/actions/workflows/checks.yml)
47
+ [![License](https://img.shields.io/github/license/camoa/periplus)](LICENSE)
48
+ [![Latest tag](https://img.shields.io/github/v/tag/camoa/periplus)](https://github.com/camoa/periplus/tags)
49
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](pyproject.toml)
50
+ <sub>The CI workflow, `.github/workflows/checks.yml`, installs the package with its `dev`
51
+ and `js` extras for the tests. It runs pytest on Python 3.11 to 3.14, ruff and mypy, and validates
52
+ every bundled pack. It does not run the end-to-end checks, and it never maps a Go project.</sub>
53
+
54
+ Status: **beta**, version 0.2.1, 592 tests.
55
+
56
+ </div>
57
+
58
+ > *periplus* (n.): an ancient coastal record of the ports and the distances between them.
59
+ >
60
+ > *rutter* (n.): a mariner's book of written sailing directions for one coast, from the French
61
+ > *routier*; it told a pilot what to look for before charts did.
62
+
63
+ Periplus makes a map of a code base: a JSON file of nodes and edges. Configuration files called
64
+ rutters tell it what to find. The same input always gives the same map, byte for byte. Periplus
65
+ never guesses. A thing that no rutter explains appears in the map as a marked unknown.
66
+
67
+ A **rutter** is a folder of YAML files for one stack, such as PHP, Drupal or Python. It declares
68
+ node types and edge kinds. It also says where to look, which files to read and how to name what it
69
+ finds. A new stack needs a new rutter, never a change to the engine. File names and settings call
70
+ a rutter a *pack*: a rutter lives in a folder named `<pack>@<version>`.
71
+
72
+ How it is meant to work: a person writes a rutter, often with help from an AI. Periplus then runs
73
+ it with no AI. A person or an AI reads the map, where every node and edge carries its file and
74
+ line. To improve a map, improve the rutters. The engine stays the same.
75
+
76
+ ## Contents
77
+
78
+ - [Get started](#get-started)
79
+ - [Why Periplus](#why-periplus)
80
+ - [What the map holds](#what-the-map-holds)
81
+ - [Write a rutter](#write-a-rutter)
82
+ - [Read the map](#read-the-map)
83
+ - [CLI reference](#cli-reference)
84
+ - [Troubleshooting](#troubleshooting)
85
+ - [Tests and checks](#tests-and-checks)
86
+ - [Known limitations](#known-limitations)
87
+ - [License](#license)
88
+
89
+ ## Get started
90
+
91
+ Periplus needs Python 3.11 or later. It declares support for 3.11 to 3.14.
92
+
93
+ ### 1. Install Periplus
94
+
95
+ Install it from PyPI:
96
+
97
+ ```
98
+ uv tool install periplus-map # or: pip install periplus-map
99
+ uv tool install 'periplus-map[go,js]' # adds the grammars that go_basic and js_basic need
100
+ pip install 'periplus-map[go,js]' # the same with pip
101
+ periplus --version
102
+ ```
103
+
104
+ To install a tagged version from the repository:
105
+
106
+ ```
107
+ uv tool install git+https://github.com/camoa/periplus@v0.2.1
108
+ ```
109
+
110
+ To install from a local copy of this repository:
111
+
112
+ ```
113
+ uv tool install /path/to/periplus # or: pip install /path/to/periplus
114
+ ```
115
+
116
+ To update a local install, pull the repository and run `uv tool install /path/to/periplus` again. A
117
+ local directory is rebuilt when `pyproject.toml` changes or a file under `src/periplus` does. Run
118
+ `uv tool install --reinstall /path/to/periplus` for the form that always rebuilds. The command
119
+ `periplus status` prints a `rutters:` digest of the bundled rutters, so you can compare two installs.
120
+
121
+ The distribution name is `periplus-map`. Its five runtime dependencies are pinned exactly. One
122
+ package that `jsonschema` brings is compiled, so your platform needs a prebuilt wheel or Rust.
123
+
124
+ To work on Periplus itself, run `uv sync --frozen --all-extras` in the repository. This installs
125
+ the development tools and the Go grammar into `.venv`.
126
+
127
+ ### 2. Pin a rutter
128
+
129
+ Run `periplus init` in the project folder. It creates `.periplus/`, a pack folder and a commented
130
+ settings file, and pins a rutter when it recognises the stack:
131
+
132
+ ```
133
+ cd /path/to/project
134
+ periplus init
135
+ ```
136
+
137
+ The settings file, `.periplus/settings.yml`, names the rutters to load, each pinned to an exact
138
+ version:
139
+
140
+ ```yaml
141
+ periplus_version: 0
142
+ packs:
143
+ - drupal_basic@0.3.0
144
+ folders:
145
+ config: ./config/default
146
+ ```
147
+
148
+ The `folders` line moves the config folder from `drupal_basic`'s default, `./config/sync`. Leave
149
+ it out when your site uses the default.
150
+
151
+ In a Drupal project, `periplus init` writes the `drupal_basic@0.3.0` pin for you. It looks for
152
+ `drupal/core` in `composer.json`, or for `web/core/lib/Drupal.php`. The rutters that
153
+ `drupal_basic` depends on load without a pin of their own. After you update Periplus, run
154
+ `periplus update` to bring the pins to the installed versions.
155
+
156
+ ### 3. Map the project
157
+
158
+ ```
159
+ periplus map --output map.json
160
+ ```
161
+
162
+ `periplus map` also prints a run report. It names each rule and how often it fired, and the files
163
+ no rule read. It also names folders it could not list, files that did not parse, and rules it
164
+ could not run. The [map report guide](docs/map-report-guide.md) reads each part in turn.
165
+
166
+ ### 4. Ask the map
167
+
168
+ ```
169
+ periplus types
170
+ periplus find TEXT
171
+ periplus show ID
172
+ ```
173
+
174
+ [Read the map](#read-the-map) lists the six commands that answer a question about the map.
175
+
176
+ ## Why Periplus
177
+
178
+ **The map is one file you commit, it is the same on every run, and nothing serves it.**
179
+
180
+ [`docs/principles.md`](docs/principles.md) gives the reasons for this design: why a new mapper,
181
+ and why rutters and not built-in support for each stack. The table compares Periplus with the
182
+ kinds of tool that page describes.
183
+
184
+ | | Periplus | A source indexer | A server-backed tool | An AI summary |
185
+ |---|---|---|---|---|
186
+ | Framework configuration and templates | Read by rutter rules, as the source files are | Little or nothing | Little or nothing | What the model happens to read |
187
+ | What you get | One JSON file to commit, compare and review | An index for the tool's own queries | A database behind a server or viewer | Ranked text made for one prompt, never saved |
188
+ | What it needs to run | The command line | The tool | A database, a server or a viewer | A model |
189
+ | A new stack | A new rutter, with no engine change | Code in the tool | Code in the tool | A new prompt |
190
+
191
+ ## What the map holds
192
+
193
+ The map is one JSON file. It holds the nodes, the edges, counts by type and by kind, the packs
194
+ loaded and the settings file used. Each node has an `id`, a `type`, a `state` and its `locations`
195
+ (file and line). Its `provenance` names the pack, the rule and the rule's declared confidence.
196
+
197
+ The engine writes three states today:
198
+
199
+ - `mapped`: a rule found the thing in a file the project holds.
200
+ - `referenced`: a file names the thing, but no rule read where it is defined. Drupal core is an
201
+ example when only custom code is mapped.
202
+ - `unresolved`: a rule looked for a name and no rule maps it. The node records what was searched.
203
+
204
+ An unresolved node from a map of a Drupal site, shortened:
205
+
206
+ ```json
207
+ {"id": "php.function::Drupal\\my_module\\Plugin\\Action\\json_decode",
208
+ "state": "unresolved", "type": "php.function",
209
+ "unresolved_detail": {"rule": "php_basic/calls_function", "found": "no mapped node of this name",
210
+ "searched_for": "Drupal\\my_module\\Plugin\\Action\\json_decode",
211
+ "types_tried": ["php.function"]}}
212
+ ```
213
+
214
+ Each edge has `from`, `to`, `kind`, its locations, an `occurrences` count and its provenance.
215
+
216
+ **Determinism.** The check `scripts/demo.d/99j-readme-example.sh` maps the example under
217
+ [Write a rutter](#write-a-rutter) twice in one folder, and the two maps are byte-identical. This
218
+ ran on one Linux machine. The design aims for identical maps across machines, but no test has
219
+ compared two machines or two operating systems yet.
220
+
221
+ ### The rutters provided
222
+
223
+ What the map holds depends on the rutters loaded. Bundled rutters load by name. Example rutters
224
+ are copied into a project's `.periplus/packs`. The tables group them by language and by framework.
225
+
226
+ <details>
227
+ <summary><strong>Languages and file types</strong></summary>
228
+
229
+ | Rutter | Version | What it finds |
230
+ |---|---|---|
231
+ | `php_basic` | 0.2.0 | PHP classes, interfaces, traits, enums, functions and methods. Inheritance, interfaces, traits, containment, and plain and static calls; a call to a PHP builtin function makes nothing. Reads a parse tree. |
232
+ | `js_basic` | 0.0.1 | JavaScript functions that have a name of their own, each under its script's path. Reads a parse tree. Needs the `js` extra. |
233
+ | `go_basic` | 0.0.3 | Go functions, methods, structs and interfaces, each under its package's import path. Calls written `f(x)` inside one package, and `pkg.F(x)` through an import, to another package of the module or to an unresolved function outside it. Reads a parse tree. Needs the `go` extra. |
234
+ | `yaml_basic` | 0.1.0 | Claims `.yml` and `.yaml` files for rutters that depend on it. No rules of its own. |
235
+ | `twig_basic` | 0.1.0 | Claims `.twig` files for rutters that depend on it. No rules of its own. |
236
+
237
+ </details>
238
+
239
+ <details>
240
+ <summary><strong>Drupal: 2 core rutters and 11 contributed-module rutters</strong></summary>
241
+
242
+ The two core rutters come first. Each contributed module has a rutter of its own, pinned
243
+ beside `drupal_basic` when the site uses that module.
244
+
245
+ | Rutter | Version | What it finds |
246
+ |---|---|---|
247
+ | `drupal_basic` | 0.3.0 | Configuration, modules, themes, services, routes, permissions, libraries, templates, hooks, entity types, fields, displays and plugins. The services and entity types that `\Drupal::service('x')`, `$container->get('x')`, `\Drupal::entityQuery('x')` and an entity type manager's `getStorage('x')` name in PHP functions and methods. Hooks from `Hook` attribute methods, and from procedural functions named by their file's name, such as `mymod_cron` in `mymod.module`. Depends on `php_basic`, `yaml_basic` and `twig_basic`. |
248
+ | `drupal_js_basic` | 0.0.1 | Drupal behaviors and the behaviors they attach, `drupalSettings` keys that PHP attaches and behaviors read, and the libraries that PHP and templates attach. Depends on `js_basic` and `drupal_basic`. |
249
+ | `ai_basic` | 0.0.1 | The AI module's agent plugins and function call plugins, by attribute, each joined to its class. |
250
+ | `advancedqueue_basic` | 0.0.1 | The Advanced Queue module's job type plugins, by attribute or annotation, each joined to its class. |
251
+ | `config_pages_basic` | 0.0.1 | Each Config Pages type, from its config file, as a bundle of the entity type `config_pages`. |
252
+ | `crop_basic` | 0.0.1 | Each Crop API crop type, from its config file, as a bundle of the entity type `crop`. |
253
+ | `drush_basic` | 0.0.1 | Drush commands in the custom modules, from the `Command` attribute or the `@command` tag, each joined to its method and class. |
254
+ | `eck_basic` | 0.0.1 | Each Entity Construction Kit entity type, and each of its bundles, from their config files. |
255
+ | `paragraphs_basic` | 0.0.1 | Each paragraph type, from its config file, as a bundle of the entity type `paragraph`. |
256
+ | `profile_basic` | 0.0.1 | Each Profile type, from its config file, as a bundle of the entity type `profile`. |
257
+ | `salesforce_basic` | 0.0.1 | Salesforce mapping field plugins; each mapping with its Salesforce object and its edges to the bundle, entity type and fields it maps; the Salesforce events that subscribers name. |
258
+ | `twig_tweak_basic` | 0.0.1 | The block plugins, menus, entity types and field storages that Twig Tweak functions such as `drupal_block('x')` name in templates. |
259
+ | `webform_basic` | 0.0.1 | Webform handler plugins, by annotation, and each webform, from its config file, as a bundle of `webform_submission`. |
260
+
261
+ </details>
262
+
263
+ <details>
264
+ <summary><strong>Laravel</strong></summary>
265
+
266
+ | Rutter | Version | What it finds |
267
+ |---|---|---|
268
+ | `laravel_basic` | 0.0.5 | Laravel routes with the prefixes and names of `Route::prefix()->name()->group()` groups, their controller methods, controllers, models, migrations, Blade views with their includes and component tags, and configuration files with the text keys of the array each returns. The views and keys that `view('x')` and `config('x.y')` calls name in PHP functions and methods. Depends on `php_basic`. Reads text, and a parse tree for the calls. |
269
+
270
+ </details>
271
+
272
+ <details>
273
+ <summary><strong>Examples</strong></summary>
274
+
275
+ | Rutter | Version | What it finds |
276
+ |---|---|---|
277
+ | `python_text` | 0.1.0 | Python modules, top-level classes and functions, imports and calls. Reads text. |
278
+ | `typer_text` | 0.1.0 | Typer apps, commands and mounts. Depends on `python_text`. |
279
+ | `speckitty` | 0.1.0 | spec-kitty missions and their templates. |
280
+
281
+ </details>
282
+
283
+ <details>
284
+ <summary><strong>Reference packs</strong></summary>
285
+
286
+ The bundle also holds five older reference packs. They pass `periplus validate`, but they hold
287
+ vocabulary only and cannot make a map.
288
+
289
+ | Pack | Version |
290
+ |---|---|
291
+ | `php` | 0.1.0 |
292
+ | `drupal` | 0.1.0 |
293
+ | `yaml` | 0.0.1 |
294
+ | `twig` | 0.0.1 |
295
+ | `go` | 0.0.2 |
296
+
297
+ </details>
298
+
299
+ The bundled rutters are in `src/periplus/packs`. The examples are in
300
+ `examples/second-stack/packs`. The manifest of every rutter in the tables above, except
301
+ `yaml_basic`, `twig_basic`, the examples and the reference packs, opens with a list of its known
302
+ gaps.
303
+
304
+ To map a Go module, install the `go` extra and run Periplus at the module's root. Give the module
305
+ path in the settings, because Periplus does not read `go.mod`:
306
+
307
+ ```yaml
308
+ periplus_version: 0
309
+ packs:
310
+ - go_basic@0.0.3
311
+ values:
312
+ module: example.com/app
313
+ ```
314
+
315
+ To map a Laravel application, pin `php_basic@0.2.0` and `laravel_basic@0.0.5` and run Periplus
316
+ at the application's root. The default Laravel folders need no settings. To move the templates,
317
+ set `views_root` under `folders`; a template is named by its path below `views_root`.
318
+
319
+ New in this release: `drupal_basic` 0.3.0, the 11 contributed-module rutters listed after it in
320
+ the Drupal table, and the two JavaScript rutters, `js_basic` and `drupal_js_basic`. The AI and
321
+ Advanced Queue plugins moved out of `drupal_basic` into their own rutters. Three new rutter
322
+ shapes: a resolution step that looks a written name up in a table the rutter declares under
323
+ `tables`, a call argument written as a class constant read through the grammar's
324
+ `constant_access` and `constant_declaration` keys, and a rule narrowed to one file ending by
325
+ `match.ending`. A rutter's `boundary` lists the framework classes the map never reads. Each one
326
+ an edge reaches becomes a `declared` node, and a class that descends from one may take the type
327
+ the list gives it. Settings `values` pass project facts, such as a Go module path, to a rutter.
328
+
329
+ ## Write a rutter
330
+
331
+ A rutter for a new stack is a folder of YAML files in the project. You write it, map the
332
+ project, read what the map cannot explain, and write the next rule.
333
+
334
+ ### The loop
335
+
336
+ 1. Run `periplus init` in the project folder. It creates `.periplus/packs/` and the settings file
337
+ `.periplus/settings.yml`. It pins a bundled rutter when it recognises the stack. For a new
338
+ stack, write a first rutter and pin it, as in the example below.
339
+ 2. Run `periplus map --output map.json --format json >report.json`. A map needs at least one
340
+ pinned rutter, and with none it stops at exit 17.
341
+ 3. Read the unknowns. In the report, `unread` lists the files in the rules' folders that no rule
342
+ read. `skipped` lists each match whose rule could not build a node or edge it emits, with the
343
+ reason. `not_executed` lists the rules the engine cannot run, and why. In the map,
344
+ `counts.nodes_by_state` counts the `referenced` nodes, which a file names but no rule maps. It
345
+ also counts the `unresolved` nodes, which a rule looked for and did not find.
346
+ 4. Write a rule for those names in a rutter under `.periplus/packs/`. Pin a new rutter under
347
+ `packs` in the settings. To build on a bundled rutter, name it in `depends`, as
348
+ `depends: [php_basic]`. Your rutter may then use its folders, file types and types, and add
349
+ rules. The bundled rutter's own files stay as installed.
350
+ 5. Run `periplus validate <pack>`. It names each file and place that disagrees with the schemas. A
351
+ rutter that does not validate stops the map.
352
+ 6. Map again. The unknowns you covered disappear. Go back to step 3.
353
+
354
+ ### Where a rutter and its settings live
355
+
356
+ A project's own rutters sit in `.periplus/packs/`, one folder per rutter, named
357
+ `<pack>@<version>`. Each holds a manifest, `pack.yaml`, and pack files: every other `.yaml` or
358
+ `.yml` file, in any subfolder.
359
+
360
+ The settings file, `.periplus/settings.yml`, names:
361
+
362
+ - `packs`: the rutters to load, each pinned as `<pack>@<version>`. A pinned rutter's dependencies
363
+ load without a pin of their own.
364
+ - `folders`: a new place for a folder that a rutter names. A project that keeps the example's
365
+ lists in `todo` sets `folders: {lists: ./todo/**}`. The rules then read `todo` and every folder
366
+ below it.
367
+ - `values`: named text values that any template of any rutter may name, such as `{module}`.
368
+ - `pack_paths`: more folders to find rutters in, relative to the project folder.
369
+ - `re_include`: globs that take back the files a rutter's `files.exclude` leaves out.
370
+
371
+ `periplus status` lists where Periplus looks for a pinned rutter: the project's
372
+ `.periplus/packs`, the user's configuration folder, the bundled rutters and each `pack_paths`
373
+ folder. A bundled rutter, such as `php_basic`, loads by its pin alone.
374
+
375
+ ### A complete small rutter
376
+
377
+ This rutter reads a line format invented for this example, a list of chores. A line
378
+ `chore <name>` declares a chore. `after <name>` at its end names the chore it waits for. Write
379
+ these four files in an empty folder.
380
+
381
+ <!-- example: .periplus/settings.yml -->
382
+ ```yaml
383
+ periplus_version: 0
384
+ packs:
385
+ - chores@0.0.1
386
+ ```
387
+
388
+ <!-- example: .periplus/packs/chores@0.0.1/pack.yaml -->
389
+ ```yaml
390
+ pack: chores
391
+ version: 0.0.1
392
+ depends: []
393
+ folders:
394
+ lists: ./lists
395
+ files:
396
+ types:
397
+ chores:
398
+ endings: [chores]
399
+ reader: text
400
+ ```
401
+
402
+ <!-- example: .periplus/packs/chores@0.0.1/rules/chores.yaml -->
403
+ ```yaml
404
+ node_types:
405
+ - name: chores.chore
406
+ id_namespace: chores.chore
407
+ edge_kinds:
408
+ - kind: waits_for
409
+ from: chores.chore
410
+ to: chores.chore
411
+ rules:
412
+ - rule: chore
413
+ reads: file
414
+ in: [lists]
415
+ match: {file: '*', filetype: chores, text: '(?m)^chore (?<name>\w+)'}
416
+ id: {from: {capture: name}}
417
+ emits: [{node: {type: chores.chore}}]
418
+ confidence: declared
419
+ - rule: chore_waits
420
+ reads: file
421
+ in: [lists]
422
+ match: {file: '*', filetype: chores, text: '(?m)^chore (?<name>\w+) after (?<after>\w+)'}
423
+ id: {from: {capture: name}}
424
+ emits: [{edge: {kind: waits_for, from: this_node, to: {from: {capture: after}}}}]
425
+ confidence: declared
426
+ ```
427
+
428
+ <!-- example: lists/home.chores -->
429
+ ```text
430
+ # the week
431
+ chore dishes
432
+ chore laundry after dishes
433
+ chore floors after sweeping
434
+ ```
435
+
436
+ `pack.yaml` gives the rutter's name and version, a folder `lists`, and a file type `chores` for
437
+ files ending in `.chores`, read as text. The pack file declares the node type `chores.chore` and
438
+ the edge kind `waits_for`. The rule `chore` fires once per match of its `text` pattern, and the
439
+ capture `name` gives the node's id. The rule `chore_waits` adds a `waits_for` edge from that node
440
+ to the name after `after`. `periplus validate` refuses one rule that emits both the node and the
441
+ edge, so the edge has its own rule. `confidence: declared` says the file states the thing.
442
+
443
+ Run `periplus validate chores`, then `periplus map --output map.json`. The map holds exactly these
444
+ nodes and edges, each with its state or kind and its file and line:
445
+
446
+ <!-- example-map -->
447
+ ```text
448
+ node chores.chore::dishes mapped lists/home.chores:2
449
+ node chores.chore::laundry mapped lists/home.chores:3
450
+ node chores.chore::floors mapped lists/home.chores:4
451
+ node chores.chore::sweeping referenced
452
+ edge chores.chore::laundry waits_for chores.chore::dishes lists/home.chores:3
453
+ edge chores.chore::floors waits_for chores.chore::sweeping lists/home.chores:4
454
+ ```
455
+
456
+ `sweeping` is `referenced`: line 4 names it, and no rule maps it. That is the kind of unknown
457
+ that step 3 of the loop finds. The check `scripts/demo.d/99j-readme-example.sh` runs this example
458
+ from this file and compares the map with the lines above.
459
+
460
+ ### What to read next
461
+
462
+ | Read | For |
463
+ |---|---|
464
+ | `periplus spec` | The contract: the schemas, the rule format, the engine, and what the format cannot say yet. `periplus spec <pack>` adds what one rutter declares and inherits. |
465
+ | [`docs/rutter-author-guide.md`](docs/rutter-author-guide.md) | Start here: the files of a rutter, one rule of each shape, and how to check a rutter. |
466
+ | [`docs/rutter-author-guide-text.md`](docs/rutter-author-guide-text.md) | Text files: spans, values from enclosing spans, the line of a node, and a path below a folder. |
467
+ | [`docs/rutter-author-guide-drupal.md`](docs/rutter-author-guide-drupal.md) | Parse-tree rules, read through the PHP and Drupal rutters. |
468
+ | [`docs/rutter-author-guide-references.md`](docs/rutter-author-guide-references.md) | Calls read from a parse tree, and every key the schemas accept that the engine does not run. |
469
+ | [`docs/rutter-author-guide-imports.md`](docs/rutter-author-guide-imports.md) | Import paths and a call's arguments. |
470
+ | [`docs/rutter-author-guide-abilities.md`](docs/rutter-author-guide-abilities.md) | A condition on a second child, an id that must fit a pattern, a declared end, and a class constant as a call's argument. |
471
+ | [`docs/rutter-author-guide-map-rules.md`](docs/rutter-author-guide-map-rules.md) | Map rules: rules that read the map the file rules made, run in passes, one hop each. |
472
+ | `examples/second-stack/packs` | Three working text rutters to copy: `python_text`, `typer_text` and `speckitty`. |
473
+ | `periplus validate <pack>` | Checks one rutter's files. It names each disagreement with the schemas, by file and place. |
474
+ | [`docs/map-report-guide.md`](docs/map-report-guide.md) | Reading the map report: each part in the order to read it, what it means, and what to do when it is not empty. |
475
+
476
+ A parse-tree rutter pins its grammar under `grammar` in `pack.yaml`, as `php_basic` does.
477
+ Periplus installs the PHP grammar, and the Go grammar with the `go` extra. Another language needs
478
+ its `tree-sitter-<language>` package installed beside Periplus, or the map stops at exit 28.
479
+
480
+ An AI that writes a rutter reads `periplus spec` first, then the guide pages in the order above.
481
+ Then it runs the loop. The map's `referenced` and `unresolved` nodes say which rule to write next.
482
+
483
+ ## Read the map
484
+
485
+ Six commands answer a question about a written map, and stop: `periplus types` counts the node
486
+ types and edge kinds, `periplus find TEXT` searches node ids, `periplus show ID` prints a node
487
+ and its edges, `periplus neighbors ID` and `periplus affected ID` follow edges out of and into a
488
+ node, and `periplus path FROM TO` prints the shortest chains between two. They read the ids the
489
+ map holds and nothing more. `periplus check-map` runs the full schema check that these commands
490
+ skip. The [navigation guide](docs/navigation.md) gives each command's options, output and exit
491
+ codes, with an example you can run.
492
+
493
+ ## CLI reference
494
+
495
+ Run each command in the project folder.
496
+
497
+ ```
498
+ periplus init # create .periplus/, a pack folder and a commented settings file
499
+ periplus status # report the settings, where each value came from, and the pack search path
500
+ periplus spec [PACK] [--summary] # print the rutter contract, and what one rutter declares and inherits
501
+ periplus validate PACK # check one rutter's files against the shipped schemas and name what disagrees
502
+ periplus update # move each pin to the installed version of that rutter
503
+ periplus map --output map.json # map this project and write the map, with a run report
504
+ periplus types # count the node types and edge kinds in the written map
505
+ periplus check-map # check every node and edge of the map against the map schema
506
+ periplus find TEXT # find the nodes whose id holds a text
507
+ periplus show ID # show one node and the edges at it
508
+ periplus neighbors ID # list the items an item's edges lead to
509
+ periplus affected ID # list the items whose edges lead to an item
510
+ periplus path FROM TO # show every shortest chain of edges from one item to another
511
+ ```
512
+
513
+ `periplus validate` takes a bare `name`, or `name@version` to pick one of several copies.
514
+ `periplus update` leaves project-local and unknown pins as they are. Every command but `init`
515
+ takes `--format json`. The seven commands that read a map take `--map PATH`; the
516
+ [navigation guide](docs/navigation.md) gives their other options. `periplus --version` prints the
517
+ installed version, and `periplus <command> --help` gives each command's options.
518
+
519
+ ## Troubleshooting
520
+
521
+ - **`periplus map` stops at exit 17.** The project pins no rutter, so there is nothing to map.
522
+ Run `periplus init`, or pin a rutter under `packs` in `.periplus/settings.yml`. Exit 17 also
523
+ marks a map that fails its own schema, which is an engine defect. Nothing is written in
524
+ either case.
525
+ - **`periplus map` stops at exit 28: a grammar mismatch.** A rutter's `grammar` in `pack.yaml`
526
+ pins a parser runtime or a grammar version other than the one installed, or one that is not
527
+ installed. The message names the rutter, the package, the version pinned and the version
528
+ found. Install the `go` or `js` extra for `go_basic` or `js_basic`. For another language,
529
+ install its `tree-sitter-<language>` package at the pinned version. The check runs before any
530
+ source file is opened.
531
+ - **A navigation command stops at exit 29, 30 or 31.** It found no map, the file is not a map,
532
+ or the id names no node. The [navigation guide](docs/navigation.md) gives the remedy for each.
533
+
534
+ ## Tests and checks
535
+
536
+ The pytest suite holds 592 tests. `scripts/demo.sh` runs the 76 end-to-end checks in
537
+ `scripts/demo.d` and stops at the first failure. Each check maps a small project and compares the
538
+ result with what it expects. The CI workflow behind the checks badge does not run them.
539
+
540
+ To propose a change, read [`CONTRIBUTING.md`](CONTRIBUTING.md).
541
+
542
+ ## Known limitations
543
+
544
+ These are the limits a new user meets first. [`docs/known-limitations.md`](docs/known-limitations.md)
545
+ lists the rest, each rutter's among them.
546
+
547
+ - **Keys accepted but not executed.** The schemas accept more than the engine runs, for example
548
+ `resolution` and `excluded_tokens`. A rule with such a key does
549
+ not run. Such a key outside a rule stops the map. `rutter-author-guide-references.md` lists every key.
550
+ - **No order among rutters.** Two rules in one rutter on one parse-tree node: the last one wins.
551
+ Nothing settles two rutters that give one attribute two values.
552
+ - **One edge kind name for all rutters.** `php_basic`, `go_basic` and `python_text` each declare
553
+ `calls` with different ends. So one project cannot load `php_basic` beside either of the others.
554
+ - **Patterns run on Python's `re`, not RE2.** Nothing checks for the RE2 subset, and
555
+ `laravel_basic` uses backreferences and lookarounds. Python `re` refuses only what it cannot
556
+ compile, such as a variable-width lookbehind.
557
+ - **An unresolved end takes its first candidate type.** For example, `pathlib.Path` becomes a
558
+ `python.function`. The map states a type it cannot know.
559
+ - **One JSON file, and every run is a full run.** There is no split map and no incremental run.
560
+ - **No export.** There is no GraphML, Mermaid or DOT output yet.
561
+
562
+ ## License
563
+
564
+ Apache License 2.0. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).
565
+
566
+ Built with AI assistance. Periplus uses no AI to make a map.