ecoportal-api-graphql 1.3.13 → 1.3.16

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 (275) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -0
  3. data/lib/ecoportal/api/common/graphql/client.rb +0 -2
  4. data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
  5. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
  6. data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
  7. data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
  8. data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
  9. data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
  10. data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
  11. data/lib/ecoportal/api/graphql/base/page.rb +17 -16
  12. data/lib/ecoportal/api/graphql/base/template.rb +35 -0
  13. data/lib/ecoportal/api/graphql/base.rb +37 -35
  14. data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
  15. data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
  16. data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
  17. data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
  18. data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
  19. data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
  20. data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
  21. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
  22. data/lib/ecoportal/api/graphql/connection.rb +17 -17
  23. data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
  24. data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
  25. data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
  26. data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
  27. data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
  28. data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
  29. data/lib/ecoportal/api/graphql/fragment.rb +3 -0
  30. data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
  31. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
  32. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
  33. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
  34. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
  35. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
  36. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
  37. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
  38. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
  39. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
  40. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
  41. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
  42. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
  43. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
  44. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
  45. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
  46. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
  47. data/lib/ecoportal/api/graphql/input.rb +24 -24
  48. data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
  49. data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
  50. data/lib/ecoportal/api/graphql/model/page.rb +16 -15
  51. data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
  52. data/lib/ecoportal/api/graphql/model/template.rb +15 -0
  53. data/lib/ecoportal/api/graphql/model.rb +32 -30
  54. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
  55. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
  56. data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
  57. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
  58. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
  59. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
  60. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
  61. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
  62. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
  63. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
  64. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
  65. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
  66. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
  67. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
  68. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
  69. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
  70. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
  71. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
  72. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
  73. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
  74. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
  75. data/lib/ecoportal/api/graphql/mutation.rb +20 -20
  76. data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
  77. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
  78. data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
  79. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
  80. data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
  81. data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
  82. data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
  83. data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
  84. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
  85. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
  86. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
  87. data/lib/ecoportal/api/graphql/payload.rb +23 -23
  88. data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
  89. data/lib/ecoportal/api/graphql/query/page.rb +45 -45
  90. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
  91. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
  92. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
  93. data/lib/ecoportal/api/graphql/query.rb +27 -27
  94. data/lib/ecoportal/api/graphql.rb +9 -5
  95. data/lib/ecoportal/api/graphql_version.rb +1 -1
  96. metadata +9 -180
  97. data/.ai-assistance/bridge/CLAUDE.md +0 -338
  98. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  99. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
  100. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
  101. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
  102. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
  103. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
  104. data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
  105. data/.ai-assistance/bridge/context/project.md +0 -42
  106. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  107. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  108. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
  109. data/.ai-assistance/bridge/queue/.gitkeep +0 -1
  110. data/.ai-assistance/capabilities/CLAUDE.md +0 -27
  111. data/.ai-assistance/capabilities/assumptions-log.md +0 -80
  112. data/.ai-assistance/capabilities/code.md +0 -47
  113. data/.ai-assistance/capabilities/connectors.md +0 -37
  114. data/.ai-assistance/capabilities/cowork.md +0 -55
  115. data/.ai-assistance/code/OVERVIEW.md +0 -155
  116. data/.ai-assistance/code/data_fields.md +0 -242
  117. data/.ai-assistance/code/dependencies.md +0 -151
  118. data/.ai-assistance/code/diff_as_input.md +0 -234
  119. data/.ai-assistance/code/diff_pairing_engine.md +0 -243
  120. data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
  121. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
  122. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
  123. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
  124. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
  125. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
  126. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
  127. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
  128. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
  129. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
  130. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
  131. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
  132. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
  133. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
  134. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
  135. data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
  136. data/.ai-assistance/code/filter_contract_matrix.md +0 -177
  137. data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
  138. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
  139. data/.ai-assistance/code/refactoring/opportunities.md +0 -251
  140. data/.ai-assistance/code/schema_analysis.md +0 -321
  141. data/.ai-assistance/code/search_filters.md +0 -868
  142. data/.ai-assistance/code/spec_coverage.md +0 -73
  143. data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
  144. data/.ai-assistance/code/workflow-command-guide.md +0 -466
  145. data/.ai-assistance/code/workflow-space.md +0 -353
  146. data/.ai-assistance/conventions/CLAUDE.md +0 -30
  147. data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
  148. data/.ai-assistance/conventions/gitignore-rules.md +0 -42
  149. data/.ai-assistance/conventions/permission-guidance.md +0 -120
  150. data/.ai-assistance/integrations/README.md +0 -70
  151. data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
  152. data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
  153. data/.ai-assistance/integrations/local-git.md +0 -60
  154. data/.ai-assistance/local_paths.example.md +0 -17
  155. data/.ai-assistance/projects/TODO.md +0 -118
  156. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
  157. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
  158. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
  159. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
  160. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
  161. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
  162. data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
  163. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
  164. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
  165. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
  166. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
  167. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
  168. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
  169. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
  170. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
  171. data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
  172. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
  173. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
  174. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
  175. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
  176. data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
  177. data/.ai-assistance/projects/page-model/TODO.md +0 -190
  178. data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
  179. data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
  180. data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
  181. data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
  182. data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
  183. data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
  184. data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
  185. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
  186. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
  187. data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
  188. data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
  189. data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
  190. data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
  191. data/.ai-assistance/projects/template-maintenance/README.md +0 -14
  192. data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
  193. data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
  194. data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
  195. data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
  196. data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
  197. data/.ai-assistance/scripts/CLAUDE.md +0 -150
  198. data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
  199. data/.ai-assistance/scripts/bridge-init.sh +0 -86
  200. data/.ai-assistance/scripts/bridge-status.sh +0 -44
  201. data/.ai-assistance/scripts/capabilities-check.ts +0 -104
  202. data/.ai-assistance/scripts/check-outbox.sh +0 -43
  203. data/.ai-assistance/scripts/dep_graph.rb +0 -91
  204. data/.ai-assistance/scripts/lock-acquire.sh +0 -103
  205. data/.ai-assistance/scripts/lock-multi.sh +0 -124
  206. data/.ai-assistance/scripts/lock-queue.sh +0 -94
  207. data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
  208. data/.ai-assistance/scripts/setup-mcps.ts +0 -234
  209. data/.ai-assistance/scripts/task-complete.ts +0 -74
  210. data/.ai-assistance/scripts/task-create.ts +0 -75
  211. data/.ai-assistance/scripts/task-read.ts +0 -125
  212. data/.ai-assistance/scripts/token-logger.js +0 -220
  213. data/.ai-assistance/scripts/token-report.ts +0 -158
  214. data/.ai-assistance/scripts/token-session-start.js +0 -66
  215. data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
  216. data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
  217. data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
  218. data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
  219. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
  220. data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
  221. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
  222. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
  223. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
  224. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
  225. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
  226. data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
  227. data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
  228. data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
  229. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
  230. data/.ai-assistance/skills/refactor/SKILL.md +0 -62
  231. data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
  232. data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
  233. data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
  234. data/.ai-assistance/standards-version.json +0 -22
  235. data/.ai-assistance/token-budget.json +0 -32
  236. data/.ai-assistance/version.json +0 -39
  237. data/.claude/settings.json +0 -150
  238. data/.env.example +0 -18
  239. data/.gitattributes +0 -15
  240. data/.gitignore +0 -37
  241. data/.gitlab-ci.yml +0 -45
  242. data/.markdownlint.json +0 -4
  243. data/.rspec +0 -3
  244. data/.rubocop.yml +0 -121
  245. data/.ruby-version +0 -1
  246. data/.yardopts +0 -10
  247. data/CLAUDE.md +0 -243
  248. data/Gemfile +0 -30
  249. data/Rakefile +0 -90
  250. data/bin/console +0 -14
  251. data/bin/setup +0 -8
  252. data/bin/setup.ps1 +0 -6
  253. data/docs/self-docs/ARCHITECTURE.md +0 -88
  254. data/docs/self-docs/CHANGES.jsonl +0 -12
  255. data/docs/self-docs/COMPLIANCE.md +0 -79
  256. data/docs/self-docs/CONVENTIONS.md +0 -74
  257. data/docs/self-docs/INTEGRATIONS.md +0 -65
  258. data/docs/self-docs/OPERATIONS.md +0 -76
  259. data/docs/self-docs/OVERVIEW.md +0 -64
  260. data/docs/self-docs/STATUS.md +0 -73
  261. data/docs/self-docs/self-docs-index.json +0 -51
  262. data/docs/worklog.md +0 -977
  263. data/ecoportal-api-graphql.gemspec +0 -40
  264. data/scripts/auto-worker-scheduler.sh +0 -386
  265. data/tests/actions_get.rb +0 -7
  266. data/tests/contractor_entities_get.rb +0 -20
  267. data/tests/contractor_entity_create.rb +0 -19
  268. data/tests/contractor_entity_udpate.rb +0 -20
  269. data/tests/dump_page_model.rb +0 -74
  270. data/tests/dump_template_model.rb +0 -90
  271. data/tests/loc_structure_get.rb +0 -10
  272. data/tests/loc_structure_update.rb +0 -51
  273. data/tests/loc_structures_get.rb +0 -15
  274. data/tests/local_libs.rb +0 -20
  275. data/tests/validate_queries.rb +0 -127
@@ -1,378 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- self_docs_scan.py -- Project self-docs OBSERVE stage (deterministic, zero-LLM core).
4
-
5
- Skill (semantic refresh + Act): skills-library/project-self-docs/SKILL.md.
6
-
7
- Runs FROM A TARGET REPO (the repo documenting itself), not from ep-ai-standards. It does the
8
- DETERMINISTIC half of self-documentation so no interactive Claude tokens are spent on facts the
9
- filesystem already knows:
10
- - detect the repo type using the same signals as skills-library/discovery-manifest.yaml
11
- - collect a structural fingerprint (top-level dirs, key config files, git head/branch/remote,
12
- commit-activity clusters, existing .ai-assistance/code specs, CI files, doc files)
13
- - compute a stable content hash per self-doc so refreshes are idempotent and the changelog
14
- only records REAL changes
15
- - emit a structured JSON "observation" the skill consumes to fill the self-doc set
16
-
17
- It does NOT write project prose, summarise purpose, or judge architecture -- that semantic work is
18
- the LLM skill's job (batched to Gemini per the token-frugality rule), gated by human review before
19
- any egress. This scanner is the thermometer; the skill is the (human-gated) actuator.
20
-
21
- HARD CONSTRAINTS:
22
- - READ-ONLY by default. It never writes into the target repo except the self-doc index/changelog
23
- under --write, and even then only to the self-docs directory (default docs/self-docs/).
24
- - NO absolute paths are emitted into any committed artefact: paths are repo-relative.
25
- - NO Confluence / network egress. Publish target is UNDECIDED (see SKILL.md); this script only
26
- ever touches the local filesystem.
27
-
28
- Usage:
29
- python self_docs_scan.py # human summary of the observation (target = cwd)
30
- python self_docs_scan.py --json # structured observation to stdout
31
- python self_docs_scan.py --target <path> # observe a different LOCAL repo root (convenience
32
- # only -- NEVER to generate another repo's self-docs
33
- # from here; run the skill in that repo's own session)
34
- python self_docs_scan.py --out docs/self-docs # self-docs dir (default docs/self-docs)
35
- python self_docs_scan.py --write # (re)write the machine index + changelog only
36
- """
37
- import argparse
38
- import hashlib
39
- import json
40
- import os
41
- import re
42
- import subprocess
43
- import sys
44
- from datetime import datetime, timezone
45
-
46
- SCHEMA_VERSION = "1.1"
47
-
48
- # Self-doc set: stable filenames the skill fills in. Keeping this list here (not only in the
49
- # template) lets the deterministic scanner report which docs already exist and which are missing,
50
- # and lets the index/changelog be generated without the LLM.
51
- SELF_DOC_SET = [
52
- ("OVERVIEW.md", "What the project is, who it serves, current priority, discovery & access"),
53
- ("ARCHITECTURE.md", "Top-level structure, key components, how they fit"),
54
- ("CONVENTIONS.md", "Coding style, branch naming, commit style, test conventions"),
55
- ("INTEGRATIONS.md", "External services + data + permissions, upstream/downstream repos"),
56
- ("STATUS.md", "Active work, migration state, known risks, open questions"),
57
- ("COMPLIANCE.md", "ISO-27001/audit, data classes + PII, vendors, AI content, leak controls"),
58
- ("OPERATIONS.md", "Infrastructure, public exposure + Cloudflare, DevOps audit, usage KPIs"),
59
- ]
60
-
61
- # Detection signals mirror skills-library/discovery-manifest.yaml. Order matters: first match wins.
62
- # (file, ...) => all must exist; file_any => any of the globs; not_file => must be absent.
63
- REPO_TYPE_RULES = [
64
- ("aws-cdk", {"file": ["cdk.json"]}),
65
- ("browser-extension", {"file": ["manifest.json"], "file_any": ["package.json", "Gruntfile.js", "webpack.config.js"]}),
66
- ("ruby-gem", {"file_any": ["*.gemspec"]}),
67
- ("rails-app", {"file": ["config/application.rb"]}),
68
- ("ruby-scripts", {"file": ["Gemfile"], "not_file": ["*.gemspec", "config/application.rb"]}),
69
- ("typescript-lib", {"file": ["package.json"], "not_file": ["cdk.json", "manifest.json"]}),
70
- ]
71
-
72
- KEY_CONFIG_FILES = [
73
- "package.json", "Gemfile", "cdk.json", "manifest.json", "go.mod", "Cargo.toml",
74
- "pyproject.toml", "requirements.txt", "tsconfig.json", ".eslintrc", ".eslintrc.json",
75
- ".eslintrc.js", ".prettierrc", ".rubocop.yml", ".editorconfig",
76
- ]
77
- CI_FILES = [".gitlab-ci.yml", "Jenkinsfile", "Makefile"]
78
-
79
-
80
- def _iso_now():
81
- return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
82
-
83
-
84
- def _exists(root, rel):
85
- return os.path.exists(os.path.join(root, rel))
86
-
87
-
88
- def _glob_any(root, patterns):
89
- import fnmatch
90
- try:
91
- top = os.listdir(root)
92
- except OSError:
93
- return False
94
- for pat in patterns:
95
- if any(fnmatch.fnmatch(name, pat) for name in top):
96
- return True
97
- return False
98
-
99
-
100
- def detect_repo_type(root):
101
- for type_name, rule in REPO_TYPE_RULES:
102
- ok = True
103
- for f in rule.get("file", []):
104
- if not _exists(root, f):
105
- ok = False
106
- break
107
- if ok and rule.get("file_any"):
108
- ok = _glob_any(root, rule["file_any"])
109
- if ok and rule.get("not_file"):
110
- for f in rule["not_file"]:
111
- if "*" in f:
112
- if _glob_any(root, [f]):
113
- ok = False
114
- break
115
- elif _exists(root, f):
116
- ok = False
117
- break
118
- if ok:
119
- return type_name
120
- return "unknown"
121
-
122
-
123
- def _git(root, args):
124
- try:
125
- out = subprocess.run(
126
- ["git", "-C", root] + args,
127
- capture_output=True, text=True, timeout=15,
128
- )
129
- return out.stdout.strip() if out.returncode == 0 else ""
130
- except (OSError, subprocess.SubprocessError):
131
- return ""
132
-
133
-
134
- def git_facts(root):
135
- branch = _git(root, ["rev-parse", "--abbrev-ref", "HEAD"])
136
- head = _git(root, ["rev-parse", "--short", "HEAD"])
137
- remote = _git(root, ["config", "--get", "remote.origin.url"])
138
- # Real repo name from the remote, never a local folder name that may be renamed.
139
- repo_name = ""
140
- if remote and "/" in remote:
141
- repo_name = re.sub(r"\.git$", "", remote.rstrip("/").split("/")[-1])
142
- # Activity clusters: which top-level dirs churn most in the last 50 commits.
143
- log = _git(root, ["log", "-50", "--name-only", "--pretty=format:"])
144
- clusters = {}
145
- for line in log.splitlines():
146
- line = line.strip()
147
- if not line:
148
- continue
149
- top = line.split("/")[0] if "/" in line else line
150
- clusters[top] = clusters.get(top, 0) + 1
151
- top_clusters = sorted(clusters.items(), key=lambda kv: kv[1], reverse=True)[:6]
152
- return {
153
- "branch": branch,
154
- "head": head,
155
- "repo_name": repo_name,
156
- "has_remote": bool(remote),
157
- "activity_clusters": [{"path": k, "touches": v} for k, v in top_clusters],
158
- }
159
-
160
-
161
- def structure(root):
162
- dirs, files = [], []
163
- try:
164
- for name in sorted(os.listdir(root)):
165
- if name.startswith(".git"):
166
- continue
167
- full = os.path.join(root, name)
168
- if os.path.isdir(full):
169
- dirs.append(name)
170
- else:
171
- files.append(name)
172
- except OSError:
173
- pass
174
- return {
175
- "top_level_dirs": dirs,
176
- "key_config_files": [f for f in KEY_CONFIG_FILES if _exists(root, f)],
177
- "ci_files": [f for f in CI_FILES if _exists(root, f)],
178
- "has_readme": _exists(root, "README.md") or _exists(root, "README"),
179
- "has_changelog": _exists(root, "CHANGELOG.md"),
180
- }
181
-
182
-
183
- def existing_context(root):
184
- """Reusable inputs that already exist -- the skill composes from these, not from scratch."""
185
- ctx = {"code_specs": [], "repo_context": None, "conventions": None}
186
- code_dir = os.path.join(root, ".ai-assistance", "code")
187
- if os.path.isdir(code_dir):
188
- for dirpath, _, filenames in os.walk(code_dir):
189
- for fn in filenames:
190
- if fn.endswith(".md"):
191
- rel = os.path.relpath(os.path.join(dirpath, fn), root).replace("\\", "/")
192
- ctx["code_specs"].append(rel)
193
- ctx["code_specs"].sort()
194
- local = os.path.join(root, ".ai-assistance", "local")
195
- if os.path.isfile(os.path.join(local, "repo-context.md")):
196
- ctx["repo_context"] = ".ai-assistance/local/repo-context.md"
197
- if os.path.isfile(os.path.join(local, "conventions.md")):
198
- ctx["conventions"] = ".ai-assistance/local/conventions.md"
199
- return ctx
200
-
201
-
202
- def _content_hash(path):
203
- """Hash the self-doc BODY (below the frontmatter) so index/changelog record real changes,
204
- not just a re-run timestamp. Missing file -> None."""
205
- if not os.path.isfile(path):
206
- return None
207
- with open(path, encoding="utf-8", errors="replace") as fh:
208
- text = fh.read()
209
- body = text
210
- if text.startswith("---"):
211
- end = text.find("\n---", 3)
212
- if end != -1:
213
- body = text[end + 4:]
214
- # Normalise line endings + trailing whitespace so cross-platform re-runs are stable.
215
- norm = "\n".join(line.rstrip() for line in body.replace("\r\n", "\n").split("\n")).strip()
216
- return hashlib.sha256(norm.encode("utf-8")).hexdigest()[:16]
217
-
218
-
219
- def self_doc_status(root, out_rel):
220
- out_dir = os.path.join(root, out_rel)
221
- docs = []
222
- for fname, purpose in SELF_DOC_SET:
223
- path = os.path.join(out_dir, fname)
224
- docs.append({
225
- "file": f"{out_rel}/{fname}".replace("\\", "/"),
226
- "purpose": purpose,
227
- "exists": os.path.isfile(path),
228
- "content_hash": _content_hash(path),
229
- })
230
- return docs
231
-
232
-
233
- def observe(root, out_rel):
234
- return {
235
- "schema_version": SCHEMA_VERSION,
236
- "observed_at": _iso_now(),
237
- "repo_type": detect_repo_type(root),
238
- "git": git_facts(root),
239
- "structure": structure(root),
240
- "existing_context": existing_context(root),
241
- "self_doc_set": self_doc_status(root, out_rel),
242
- "out_dir": out_rel.replace("\\", "/"),
243
- }
244
-
245
-
246
- # == index + changelog (deterministic writers) ======================================
247
-
248
- def _read_json(path, default):
249
- if not os.path.isfile(path):
250
- return default
251
- try:
252
- with open(path, encoding="utf-8") as fh:
253
- return json.load(fh)
254
- except (OSError, ValueError):
255
- return default
256
-
257
-
258
- def write_index_and_changelog(root, out_rel, obs):
259
- """Rewrite the machine-readable index and append changelog deltas -- deterministically.
260
-
261
- The index (self-docs-index.json) is the machine surface a central hub consumes: schema
262
- version, repo identity, and a hash per doc. The changelog (CHANGES.jsonl) appends one line
263
- per doc whose content hash CHANGED since the previous index -- so the hub sees real deltas,
264
- and a no-op re-run appends nothing (idempotent)."""
265
- out_dir = os.path.join(root, out_rel)
266
- os.makedirs(out_dir, exist_ok=True)
267
- index_path = os.path.join(out_dir, "self-docs-index.json")
268
- changes_path = os.path.join(out_dir, "CHANGES.jsonl")
269
-
270
- prev = _read_json(index_path, {})
271
- prev_hashes = {d["file"]: d.get("content_hash") for d in prev.get("docs", [])}
272
-
273
- docs = obs["self_doc_set"]
274
- now = obs["observed_at"]
275
- deltas = []
276
- for d in docs:
277
- new_h = d["content_hash"]
278
- old_h = prev_hashes.get(d["file"])
279
- if new_h != old_h:
280
- deltas.append({
281
- "ts": now,
282
- "file": d["file"],
283
- "change": ("created" if old_h is None and new_h is not None
284
- else "removed" if new_h is None
285
- else "updated"),
286
- "from_hash": old_h,
287
- "to_hash": new_h,
288
- })
289
-
290
- index = {
291
- "schema_version": SCHEMA_VERSION,
292
- "generated_at": now,
293
- "repo_name": obs["git"].get("repo_name") or "",
294
- "repo_type": obs["repo_type"],
295
- "head": obs["git"].get("head", ""),
296
- "docs": [
297
- {"file": d["file"], "purpose": d["purpose"],
298
- "exists": d["exists"], "content_hash": d["content_hash"]}
299
- for d in docs
300
- ],
301
- }
302
- with open(index_path, "w", encoding="utf-8") as fh:
303
- json.dump(index, fh, indent=2)
304
- fh.write("\n")
305
-
306
- if deltas:
307
- with open(changes_path, "a", encoding="utf-8") as fh:
308
- for delta in deltas:
309
- fh.write(json.dumps(delta) + "\n")
310
-
311
- return {"index": f"{out_rel}/self-docs-index.json".replace("\\", "/"),
312
- "changelog": f"{out_rel}/CHANGES.jsonl".replace("\\", "/"),
313
- "deltas_appended": len(deltas)}
314
-
315
-
316
- def _human(obs):
317
- g = obs["git"]
318
- print(f"self-docs observation (schema {obs['schema_version']})")
319
- print(f" repo_type : {obs['repo_type']}")
320
- print(f" repo_name : {g.get('repo_name') or '(no remote)'} @ {g.get('head') or '?'} "
321
- f"on {g.get('branch') or '?'}")
322
- print(f" out_dir : {obs['out_dir']}")
323
- print(" top-level dirs:", ", ".join(obs["structure"]["top_level_dirs"]) or "(none)")
324
- if obs["structure"]["key_config_files"]:
325
- print(" config :", ", ".join(obs["structure"]["key_config_files"]))
326
- if g.get("activity_clusters"):
327
- hot = ", ".join(f"{c['path']}({c['touches']})" for c in g["activity_clusters"])
328
- print(" hot paths :", hot)
329
- ec = obs["existing_context"]
330
- reuse = []
331
- if ec["repo_context"]:
332
- reuse.append("ai-discovery repo-context.md")
333
- if ec["conventions"]:
334
- reuse.append("ai-discovery conventions.md")
335
- if ec["code_specs"]:
336
- reuse.append(f"{len(ec['code_specs'])} code-specs")
337
- print(" reuse from:", ", ".join(reuse) or "(none -- run ai-discovery/code-specs first)")
338
- print(" self-doc set:")
339
- for d in obs["self_doc_set"]:
340
- state = "present" if d["exists"] else "MISSING"
341
- print(f" [{state:7}] {d['file']} ({d['content_hash'] or '-'})")
342
-
343
-
344
- def main():
345
- ap = argparse.ArgumentParser(description="Deterministic project self-docs observation")
346
- ap.add_argument("--target", default=".",
347
- help="LOCAL repo root to observe (default: cwd). Convenience only -- do NOT use to "
348
- "generate ANOTHER repo's self-docs from here; run the skill in that repo's own "
349
- "session (self-docs are owned + generated by the repo that documents itself).")
350
- ap.add_argument("--out", default="docs/self-docs", help="self-docs dir (default: docs/self-docs)")
351
- ap.add_argument("--json", action="store_true", help="emit the structured observation")
352
- ap.add_argument("--write", action="store_true",
353
- help="(re)write the machine index + changelog deltas only")
354
- args = ap.parse_args()
355
-
356
- root = os.path.abspath(args.target)
357
- if not os.path.isdir(root):
358
- print(f"[self-docs] target not a directory: {root}", file=sys.stderr)
359
- return 2
360
-
361
- obs = observe(root, args.out)
362
-
363
- if args.write:
364
- result = write_index_and_changelog(root, args.out, obs)
365
- obs["_write_result"] = result
366
-
367
- if args.json:
368
- print(json.dumps(obs, indent=2))
369
- else:
370
- _human(obs)
371
- if args.write:
372
- r = obs["_write_result"]
373
- print(f" wrote index -> {r['index']}; changelog deltas appended: {r['deltas_appended']}")
374
- return 0
375
-
376
-
377
- if __name__ == "__main__":
378
- sys.exit(main())
@@ -1,62 +0,0 @@
1
- # SKILL: Refactor
2
-
3
- **Purpose:** Identify, log, and (with developer confirmation) apply refactoring opportunities. Never apply changes without explicit confirmation.
4
-
5
- ---
6
-
7
- ## When to Use
8
-
9
- - You notice a structural issue while working on something else — log it, don't fix it silently.
10
- - The developer asks for a refactor audit of an area.
11
- - A cycle-end review surfaces cleanup opportunities.
12
-
13
- ---
14
-
15
- ## Criteria for a Refactor Candidate
16
-
17
- Flag something when it fails one or more of:
18
-
19
- - **Understandability:** Is it clear what this does and why?
20
- - **Dependency tracking:** Are dependencies implicit or tangled?
21
- - **Separation of concerns:** Is this class/method doing more than one thing?
22
- - **Simplicity:** Could this be simpler without losing correctness?
23
- - **Alignment:** Does this match the conventions of the rest of the codebase?
24
-
25
- ---
26
-
27
- ## Log Format
28
-
29
- Refactoring opportunities are logged in `.ai-assistance/code/refactoring/<area>.md`.
30
-
31
- Each entry:
32
-
33
- ```markdown
34
- ## [SHORT-ID] <Title>
35
-
36
- - **File(s):** `lib/ecoportal/...`
37
- - **Type:** [extract_class | extract_method | rename | simplify | reorganise | other]
38
- - **Priority:** [high | medium | low]
39
- - **Status:** [open | confirmed | applied | dismissed]
40
- - **Identified:** YYYY-MM-DD
41
-
42
- **Observation:**
43
- What the issue is, concisely.
44
-
45
- **Proposed change:**
46
- What you'd do to fix it. High-level — not code unless it's very short.
47
-
48
- **Risk:**
49
- Any backwards-compat or downstream risk if applied.
50
- ```
51
-
52
- ---
53
-
54
- ## Workflow
55
-
56
- 1. Identify opportunity → log it in the appropriate refactoring file.
57
- 2. Present a summary to the developer: "I've logged N refactoring opportunities in `.ai-assistance/code/refactoring/`. Want to review them?"
58
- 3. Developer confirms which ones to apply → mark as `confirmed`.
59
- 4. Apply one at a time, update specs if needed, mark as `applied`.
60
- 5. If dismissed by developer → mark as `dismissed` with a brief reason.
61
-
62
- **Never apply a refactor without a `confirmed` status.**
@@ -1,93 +0,0 @@
1
- # SKILL: Rubocop & Spec Runner
2
-
3
- **Purpose:** Run Rubocop on in-scope files and `rake spec`, then surface only actionable
4
- offences. Use this continuously as new/modified files accumulate — not deferred to the end.
5
-
6
- ---
7
-
8
- ## When to Use
9
-
10
- - After writing or modifying any Ruby file.
11
- - Before committing a change.
12
- - Any time TODO 6.1 says "rubocop clean on all new/modified files".
13
-
14
- ---
15
-
16
- ## Running Rubocop on Specific Files
17
-
18
- ```bash
19
- bundle exec rubocop --format json <file1> [file2 ...] | \
20
- ruby -rjson -e '
21
- data = JSON.parse($stdin.read)
22
- offences = data["files"].flat_map do |f|
23
- f["offenses"].map { |o| { file: f["path"], **o.transform_keys(&:to_sym) } }
24
- end
25
- offences.reject! { |o| o[:cop_name].start_with?("Lint/RedundantCopDisableDirective") }
26
- if offences.empty?
27
- puts "No offences."
28
- else
29
- offences.each do |o|
30
- puts "#{o[:file]}:#{o[:location][:line]}:#{o[:location][:column]} " \
31
- "[#{o[:severity]}] #{o[:cop_name]} — #{o[:message]}"
32
- end
33
- puts "\n#{offences.size} offence(s)."
34
- end
35
- '
36
- ```
37
-
38
- **Key flags:**
39
- - `--format json` — machine-parseable output, avoids ANSI noise.
40
- - No `--autocorrect` here — apply corrections explicitly when needed (see below).
41
-
42
- ---
43
-
44
- ## Auto-correcting Safe Offences
45
-
46
- ```bash
47
- bundle exec rubocop --autocorrect --format json <file1> [file2 ...]
48
- ```
49
-
50
- Only use `--autocorrect` (not `--autocorrect-all`) to limit to safe cops.
51
- Always re-read the file after correction and verify the diff is sensible.
52
-
53
- ---
54
-
55
- ## Running the Full Suite
56
-
57
- ```bash
58
- bundle exec rake spec
59
- ```
60
-
61
- Run after a meaningful unit of work is complete. Failures must be resolved before moving on.
62
-
63
- ---
64
-
65
- ## Workflow
66
-
67
- 1. Write or edit a file.
68
- 2. Run rubocop on that file (command above).
69
- 3. Fix all reported offences (except those covered by existing `# rubocop:disable` comments — the
70
- disable is intentional; do not remove it).
71
- 4. Re-run rubocop to confirm zero offences.
72
- 5. When a group of files is complete, run `rake spec` to confirm no regressions.
73
-
74
- ---
75
-
76
- ## Rules
77
-
78
- - **Do not second-guess `rubocop:disable` inline comments.** They are intentional suppressions
79
- chosen by the developer or a previous agent. Leave them in place.
80
- - **Do not add new `rubocop:disable` comments** unless the developer explicitly instructs it.
81
- Fix the offence instead.
82
- - **Surface every remaining offence** — do not filter by severity. Let the developer decide
83
- what to defer.
84
- - `Lint/RedundantCopDisableDirective` offences are suppressed in the output above because
85
- they appear when a disable comment is no longer needed; rubocop handles their cleanup itself.
86
-
87
- ---
88
-
89
- ## Linked TODOs
90
-
91
- - **6.0** — This skill (done once built).
92
- - **6.1** — Run continuously on all new/modified files throughout the migration.
93
- - **6.2** — `rake spec` coverage for `HttpClient#execute`, `Fragment.assemble`, end-to-end query.