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,220 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * token-logger.js
4
- *
5
- * Claude Code Stop hook — fires after every AI response turn.
6
- * Reads the session transcript, extracts token usage, accumulates weekly totals,
7
- * and warns when approaching the project's budget allocation.
8
- *
9
- * Wired in .claude/settings.json:
10
- * "Stop": [{ "type": "command", "command": "node .ai-assistance/scripts/token-logger.js" }]
11
- *
12
- * Reads: stdin (Stop event JSON with session_id, transcript_path, cwd)
13
- * .ai-assistance/token-budget.json
14
- * .ai-assistance/local/kpi/session-<id>.json (running session state)
15
- * .ai-assistance/local/kpi/weekly-<YYYY-WNN>.json (weekly totals)
16
- *
17
- * Writes: .ai-assistance/local/kpi/session-<id>.json (updated state)
18
- * .ai-assistance/local/kpi/weekly-<YYYY-WNN>.json (updated totals)
19
- * .ai-assistance/local/kpi/sessions-<YYYY-WNN>.jsonl (completed turns)
20
- */
21
-
22
- const fs = require("fs");
23
- const path = require("path");
24
- const os = require("os");
25
-
26
- // ── Helpers ────────────────────────────────────────────────────────────────
27
-
28
- function isoWeek(d) {
29
- const jan4 = new Date(d.getFullYear(), 0, 4);
30
- const startOfWeek = new Date(jan4);
31
- startOfWeek.setDate(jan4.getDate() - ((jan4.getDay() + 6) % 7));
32
- const weekNum = Math.ceil(((d - startOfWeek) / 86400000 + 1) / 7);
33
- return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
34
- }
35
-
36
- function loadJson(p, fallback) {
37
- try { return JSON.parse(fs.readFileSync(p, "utf8")); }
38
- catch { return fallback; }
39
- }
40
-
41
- function saveJson(p, data) {
42
- fs.mkdirSync(path.dirname(p), { recursive: true });
43
- fs.writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf8");
44
- }
45
-
46
- function appendJsonl(p, obj) {
47
- fs.mkdirSync(path.dirname(p), { recursive: true });
48
- fs.appendFileSync(p, JSON.stringify(obj) + "\n", "utf8");
49
- }
50
-
51
- // ── Extract token usage from transcript JSONL ──────────────────────────────
52
-
53
- function extractUsageFromTranscript(transcriptPath) {
54
- if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;
55
-
56
- let inputTokens = 0, outputTokens = 0, cacheReadTokens = 0, cacheCreateTokens = 0;
57
- let toolCalls = 0, turns = 0, found = false;
58
-
59
- try {
60
- const lines = fs.readFileSync(transcriptPath, "utf8").split("\n").filter(Boolean);
61
- for (const line of lines) {
62
- try {
63
- const entry = JSON.parse(line);
64
- // Extract usage from any entry that has it
65
- const usage = entry.usage || entry.message?.usage;
66
- if (usage) {
67
- inputTokens += usage.input_tokens || 0;
68
- outputTokens += usage.output_tokens || 0;
69
- cacheReadTokens += usage.cache_read_input_tokens || 0;
70
- cacheCreateTokens+= usage.cache_creation_input_tokens|| 0;
71
- found = true;
72
- }
73
- // Count tool uses
74
- if (entry.type === "tool_use" || entry.tool_name) toolCalls++;
75
- // Count assistant turns
76
- if (entry.role === "assistant" || entry.type === "assistant") turns++;
77
- } catch { /* skip malformed lines */ }
78
- }
79
- } catch { return null; }
80
-
81
- if (!found) return null;
82
- return { inputTokens, outputTokens, cacheReadTokens, cacheCreateTokens, toolCalls, turns };
83
- }
84
-
85
- // ── Estimate tokens when transcript doesn't have usage data ───────────────
86
-
87
- function estimateFromTranscript(transcriptPath) {
88
- if (!transcriptPath || !fs.existsSync(transcriptPath)) {
89
- return { inputTokens: 0, outputTokens: 0, toolCalls: 0, turns: 0, estimated: true };
90
- }
91
- let inputChars = 0, outputChars = 0, toolCalls = 0, turns = 0;
92
- try {
93
- const lines = fs.readFileSync(transcriptPath, "utf8").split("\n").filter(Boolean);
94
- for (const line of lines) {
95
- try {
96
- const entry = JSON.parse(line);
97
- const content = JSON.stringify(entry.content || entry.text || "");
98
- if (entry.role === "user" || entry.type === "user") { inputChars += content.length; }
99
- if (entry.role === "assistant" || entry.type === "assistant") { outputChars += content.length; turns++; }
100
- if (entry.type === "tool_use" || entry.tool_name) { toolCalls++; inputChars += 500 * 4; }
101
- } catch { /* skip */ }
102
- }
103
- } catch {}
104
- return {
105
- inputTokens: Math.round(inputChars / 4),
106
- outputTokens: Math.round(outputChars / 4),
107
- cacheReadTokens: 0, cacheCreateTokens: 0,
108
- toolCalls, turns, estimated: true
109
- };
110
- }
111
-
112
- // ── Main ───────────────────────────────────────────────────────────────────
113
-
114
- async function main() {
115
- let event = {};
116
- try {
117
- const raw = fs.readFileSync("/dev/stdin", "utf8");
118
- event = JSON.parse(raw);
119
- } catch { /* no stdin or parse error — use empty event */ }
120
-
121
- const cwd = event.cwd || process.cwd();
122
- const sessionId = event.session_id || `unknown-${Date.now()}`;
123
- const transcriptPath = event.transcript_path;
124
-
125
- const budgetFile = path.join(cwd, ".ai-assistance", "token-budget.json");
126
- const kpiDir = path.join(cwd, ".ai-assistance", "local", "kpi");
127
- const weekId = isoWeek(new Date());
128
- const sessionFile = path.join(kpiDir, `session-${sessionId}.json`);
129
- const weeklyFile = path.join(kpiDir, `weekly-${weekId}.json`);
130
- const turnLogFile = path.join(kpiDir, `sessions-${weekId}.jsonl`);
131
-
132
- const budget = loadJson(budgetFile, {});
133
- const project = (budget.project?.name || path.basename(cwd));
134
- const priority= (budget.project?.priority || "medium");
135
- const targetPct = (budget.weekly_quota?.target_utilization_pct || 75) / 100;
136
- const warnAt = (budget.session_logging?.warn_at_pct || 80) / 100;
137
-
138
- // Extract usage from transcript
139
- const transcriptUsage = extractUsageFromTranscript(transcriptPath)
140
- || estimateFromTranscript(transcriptPath);
141
-
142
- // Load previous session state (accumulate across turns in a session)
143
- const prevSession = loadJson(sessionFile, {
144
- session_id: sessionId, project, priority,
145
- started_at: new Date().toISOString(),
146
- week_id: weekId,
147
- input_tokens: 0, output_tokens: 0,
148
- cache_read_tokens: 0, cache_create_tokens: 0,
149
- tool_calls: 0, turns: 0, estimated: false,
150
- });
151
-
152
- // Use transcript totals (they accumulate naturally) not deltas
153
- const sessionNow = {
154
- ...prevSession,
155
- input_tokens: transcriptUsage.inputTokens,
156
- output_tokens: transcriptUsage.outputTokens,
157
- cache_read_tokens: transcriptUsage.cacheReadTokens || 0,
158
- cache_create_tokens:transcriptUsage.cacheCreateTokens || 0,
159
- tool_calls: transcriptUsage.toolCalls,
160
- turns: transcriptUsage.turns,
161
- estimated: transcriptUsage.estimated || false,
162
- last_updated_at: new Date().toISOString(),
163
- };
164
-
165
- saveJson(sessionFile, sessionNow);
166
-
167
- // Update weekly totals — replace session contribution (re-compute from sessions)
168
- const weekly = loadJson(weeklyFile, { week_id: weekId, projects: {}, total_tokens: 0 });
169
- const sessionTotal = sessionNow.input_tokens + sessionNow.output_tokens;
170
- const prevContrib = (weekly.projects[sessionId]?.tokens || 0);
171
- weekly.projects[sessionId] = {
172
- project, priority, tokens: sessionTotal,
173
- tool_calls: sessionNow.tool_calls, turns: sessionNow.turns,
174
- updated_at: new Date().toISOString()
175
- };
176
- weekly.total_tokens = Object.values(weekly.projects).reduce((s, p) => s + p.tokens, 0);
177
- saveJson(weeklyFile, weekly);
178
-
179
- // Log the turn to the weekly JSONL (for cross-session analysis)
180
- appendJsonl(turnLogFile, {
181
- ts: new Date().toISOString(), session_id: sessionId, project, priority, week_id: weekId,
182
- turn_tokens: sessionTotal - prevContrib,
183
- session_total_tokens: sessionTotal,
184
- tool_calls: sessionNow.tool_calls,
185
- estimated: sessionNow.estimated,
186
- });
187
-
188
- // ── Budget warnings ────────────────────────────────────────────────────
189
-
190
- const totalTokens = budget.weekly_quota?.total_tokens;
191
- if (totalTokens) {
192
- const usedPct = weekly.total_tokens / totalTokens;
193
- const targetTokens = totalTokens * targetPct;
194
-
195
- // Priority-based soft allocation
196
- const weights = budget.project_allocation?.priority_weights || { high: 50, medium: 30, low: 20 };
197
- const myWeight = (weights[priority] || 30) / 100;
198
- const myBudget = totalTokens * targetPct * myWeight;
199
- const myUsed = Object.values(weekly.projects)
200
- .filter(p => p.project === project)
201
- .reduce((s, p) => s + p.tokens, 0);
202
- const myPct = myBudget > 0 ? myUsed / myBudget : 0;
203
-
204
- if (usedPct >= warnAt) {
205
- process.stderr.write(
206
- `\n[token-budget] ⚠ Week ${weekId}: ${Math.round(usedPct * 100)}% of quota used` +
207
- ` (${weekly.total_tokens.toLocaleString()}/${totalTokens.toLocaleString()} tokens)` +
208
- ` — target was ${Math.round(targetPct * 100)}%\n`
209
- );
210
- }
211
- if (myPct >= warnAt) {
212
- process.stderr.write(
213
- `[token-budget] ⚠ Project "${project}" (${priority}): ${Math.round(myPct * 100)}% of allocation` +
214
- ` (${myUsed.toLocaleString()}/${Math.round(myBudget).toLocaleString()} tokens)\n`
215
- );
216
- }
217
- }
218
- }
219
-
220
- main().catch(() => { /* never crash the hook */ });
@@ -1,158 +0,0 @@
1
- /**
2
- * token-report.ts
3
- *
4
- * Cross-project token usage report. Reads weekly session logs from all aligned
5
- * projects (via local/paths.md) and produces a management-ready summary.
6
- *
7
- * Usage:
8
- * npx ts-node .ai-assistance/scripts/token-report.ts
9
- * npx ts-node .ai-assistance/scripts/token-report.ts --week 2026-W24
10
- * npx ts-node .ai-assistance/scripts/token-report.ts --format json
11
- *
12
- * Output: Markdown table (default) or JSON (--format json)
13
- * The report is printed to stdout — redirect to a file or pipe to your reporting tool.
14
- */
15
-
16
- import * as fs from "fs";
17
- import * as path from "path";
18
-
19
- const SCRIPTS_DIR = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
20
- const AI_DIR = path.join(SCRIPTS_DIR, "..");
21
- const CWD = path.join(AI_DIR, "..", "..");
22
-
23
- // ── Helpers ────────────────────────────────────────────────────────────────
24
-
25
- function isoWeek(d: Date): string {
26
- const jan4 = new Date(d.getFullYear(), 0, 4);
27
- const s = new Date(jan4); s.setDate(jan4.getDate() - ((jan4.getDay() + 6) % 7));
28
- return `${d.getFullYear()}-W${String(Math.ceil(((d.getTime() - s.getTime()) / 86400000 + 1) / 7)).padStart(2, "0")}`;
29
- }
30
-
31
- function loadJson<T>(p: string, fb: T): T {
32
- try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return fb; }
33
- }
34
-
35
- interface ProjectPath { alias: string; localPath: string; }
36
-
37
- function loadProjectPaths(): ProjectPath[] {
38
- const pathsFile = path.join(AI_DIR, "local", "paths.md");
39
- if (!fs.existsSync(pathsFile)) return [{ alias: path.basename(CWD), localPath: CWD }];
40
- const projects: ProjectPath[] = [];
41
- const seen = new Set<string>();
42
- for (const line of fs.readFileSync(pathsFile, "utf8").split("\n")) {
43
- const m = line.match(/\|\s*`([^`]+)`\s*\|\s*`([^`]+)`/);
44
- if (!m) continue;
45
- const [, alias, rawPath] = m;
46
- const localPath = rawPath.replace(/\//g, path.sep);
47
- const real = fs.existsSync(localPath) ? fs.realpathSync(localPath) : localPath;
48
- if (!seen.has(real) && fs.existsSync(localPath)) { projects.push({ alias, localPath }); seen.add(real); }
49
- }
50
- return projects.length ? projects : [{ alias: path.basename(CWD), localPath: CWD }];
51
- }
52
-
53
- interface WeeklyData {
54
- week_id: string;
55
- total_tokens: number;
56
- projects: Record<string, { project: string; priority: string; tokens: number; tool_calls: number; turns: number; }>;
57
- }
58
-
59
- interface SessionTurn {
60
- ts: string; session_id: string; project: string; priority: string;
61
- week_id: string; turn_tokens: number; session_total_tokens: number;
62
- tool_calls: number; estimated: boolean;
63
- }
64
-
65
- // ── Main ───────────────────────────────────────────────────────────────────
66
-
67
- const args = process.argv.slice(2);
68
- const weekArg = args.includes("--week") ? args[args.indexOf("--week") + 1] : null;
69
- const format = args.includes("--format") ? args[args.indexOf("--format") + 1] : "text";
70
- const weekId = weekArg || isoWeek(new Date());
71
-
72
- const projects = loadProjectPaths();
73
-
74
- // Aggregate across all projects
75
- const byProject: Record<string, {
76
- tokens: number; tool_calls: number; turns: number;
77
- priority: string; sessions: number; estimated: number;
78
- }> = {};
79
-
80
- let grandTotal = 0;
81
-
82
- for (const { localPath } of projects) {
83
- const kpiDir = path.join(localPath, ".ai-assistance", "local", "kpi");
84
- const weeklyFile = path.join(kpiDir, `weekly-${weekId}.json`);
85
- const budget = loadJson<any>(path.join(localPath, ".ai-assistance", "token-budget.json"), {});
86
- const projectName= budget.project?.name || path.basename(localPath);
87
- const priority = budget.project?.priority || "medium";
88
-
89
- if (!fs.existsSync(weeklyFile)) continue;
90
-
91
- const weekly = loadJson<WeeklyData>(weeklyFile, { week_id: weekId, total_tokens: 0, projects: {} });
92
-
93
- // Sum sessions for this project
94
- let tokens = 0, toolCalls = 0, turns = 0, sessions = 0, estimated = 0;
95
- for (const s of Object.values(weekly.projects)) {
96
- if (s.project === projectName) {
97
- tokens += s.tokens;
98
- toolCalls += s.tool_calls || 0;
99
- turns += s.turns || 0;
100
- sessions++;
101
- }
102
- }
103
-
104
- // Count estimated sessions from JSONL
105
- const jsonl = path.join(kpiDir, `sessions-${weekId}.jsonl`);
106
- if (fs.existsSync(jsonl)) {
107
- const lines = fs.readFileSync(jsonl, "utf8").split("\n").filter(Boolean);
108
- estimated = lines.filter(l => { try { return JSON.parse(l).estimated; } catch { return false; } }).length;
109
- }
110
-
111
- if (tokens > 0) {
112
- byProject[projectName] = { tokens, tool_calls: toolCalls, turns, priority, sessions, estimated };
113
- grandTotal += tokens;
114
- }
115
- }
116
-
117
- if (format === "json") {
118
- console.log(JSON.stringify({ week_id: weekId, grand_total_tokens: grandTotal, projects: byProject }, null, 2));
119
- process.exit(0);
120
- }
121
-
122
- // ── Text report ────────────────────────────────────────────────────────────
123
-
124
- const sortedProjects = Object.entries(byProject)
125
- .sort(([, a], [, b]) => b.tokens - a.tokens);
126
-
127
- console.log(`\n${"=".repeat(70)}`);
128
- console.log(` Token Usage Report — ${weekId}`);
129
- console.log(` Generated: ${new Date().toISOString().slice(0, 16)}`);
130
- console.log(`${"=".repeat(70)}\n`);
131
- console.log(` Grand total: ${grandTotal.toLocaleString()} tokens across ${sortedProjects.length} project(s)\n`);
132
-
133
- console.log(` ${"Project".padEnd(30)} ${"Priority".padEnd(10)} ${"Tokens".padStart(10)} ${"Share".padStart(7)} ${"Tool calls".padStart(12)} ${"Turns".padStart(6)}`);
134
- console.log(` ${"-".repeat(78)}`);
135
-
136
- for (const [name, data] of sortedProjects) {
137
- const share = grandTotal > 0 ? Math.round((data.tokens / grandTotal) * 100) : 0;
138
- const est = data.estimated > 0 ? " ~" : " ";
139
- console.log(` ${name.padEnd(30)} ${data.priority.padEnd(10)} ${est}${data.tokens.toLocaleString().padStart(8)} ${`${share}%`.padStart(7)} ${data.tool_calls.toString().padStart(12)} ${data.turns.toString().padStart(6)}`);
140
- }
141
-
142
- console.log(`\n ~ = some sessions used token estimation (transcript had no usage data)`);
143
-
144
- // Priority allocation analysis
145
- const targetPct = 75;
146
- const weights = { high: 50, medium: 30, low: 20 };
147
- console.log(`\n Priority allocation vs actuals (target utilization: ${targetPct}%):`);
148
- for (const priority of ["high", "medium", "low"]) {
149
- const w = (weights as any)[priority];
150
- const actual = Object.values(byProject)
151
- .filter(p => p.priority === priority)
152
- .reduce((s, p) => s + p.tokens, 0);
153
- const actualPct = grandTotal > 0 ? Math.round((actual / grandTotal) * 100) : 0;
154
- const projects = Object.entries(byProject).filter(([, p]) => p.priority === priority).map(([n]) => n).join(", ") || "(none)";
155
- console.log(` ${priority.padEnd(8)} target ~${w}% actual ${`${actualPct}%`.padStart(4)} — ${projects}`);
156
- }
157
-
158
- console.log(`\n${"=".repeat(70)}\n`);
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * token-session-start.js
4
- *
5
- * Claude Code SessionStart hook.
6
- * Checks current week's token usage, warns if over allocation,
7
- * and shows the budget status for this project.
8
- *
9
- * Wired in .claude/settings.json:
10
- * "SessionStart": [{ "type": "command", "command": "node .ai-assistance/scripts/token-session-start.js" }]
11
- */
12
-
13
- const fs = require("fs");
14
- const path = require("path");
15
-
16
- function isoWeek(d) {
17
- const jan4 = new Date(d.getFullYear(), 0, 4);
18
- const s = new Date(jan4); s.setDate(jan4.getDate() - ((jan4.getDay() + 6) % 7));
19
- return `${d.getFullYear()}-W${String(Math.ceil(((d - s) / 86400000 + 1) / 7)).padStart(2, "0")}`;
20
- }
21
- function loadJson(p, fb) { try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return fb; } }
22
-
23
- async function main() {
24
- let event = {};
25
- try { event = JSON.parse(fs.readFileSync("/dev/stdin", "utf8")); } catch {}
26
-
27
- const cwd = event.cwd || process.cwd();
28
- const weekId = isoWeek(new Date());
29
- const budget = loadJson(path.join(cwd, ".ai-assistance", "token-budget.json"), {});
30
- const weekly = loadJson(path.join(cwd, ".ai-assistance", "local", "kpi", `weekly-${weekId}.json`), { total_tokens: 0, projects: {} });
31
-
32
- const project = budget.project?.name || path.basename(cwd);
33
- const priority = budget.project?.priority || "medium";
34
- const total = budget.weekly_quota?.total_tokens;
35
- const targetPct= (budget.weekly_quota?.target_utilization_pct || 75);
36
-
37
- // My project's tokens this week across all sessions
38
- const myTokens = Object.values(weekly.projects)
39
- .filter(p => p.project === project)
40
- .reduce((s, p) => s + p.tokens, 0);
41
-
42
- const lines = [`\n[token-budget] Week ${weekId} | Project: ${project} (${priority})`];
43
- lines.push(` All projects this week: ${weekly.total_tokens.toLocaleString()} tokens`);
44
- lines.push(` This project this week: ${myTokens.toLocaleString()} tokens`);
45
-
46
- if (total) {
47
- const usedPct = Math.round((weekly.total_tokens / total) * 100);
48
- const weights = budget.project_allocation?.priority_weights || { high: 50, medium: 30, low: 20 };
49
- const myAlloc = Math.round(total * (targetPct / 100) * ((weights[priority] || 30) / 100));
50
- const myPct = myAlloc > 0 ? Math.round((myTokens / myAlloc) * 100) : 0;
51
- lines.push(` Week quota: ${usedPct}% used (target ${targetPct}%) | This project: ${myPct}% of ${myAlloc.toLocaleString()} alloc`);
52
- if (usedPct >= targetPct) lines.push(` !! Over weekly target — consider deferring low-priority work`);
53
- if (myPct >= 90) lines.push(` !! This project near allocation limit`);
54
- }
55
-
56
- process.stderr.write(lines.join("\n") + "\n");
57
-
58
- // Also run bridge-init if it exists
59
- const bridgeInit = path.join(cwd, ".ai-assistance", "scripts", "bridge-init.sh");
60
- if (fs.existsSync(bridgeInit)) {
61
- const { execSync } = require("child_process");
62
- try { execSync(`bash "${bridgeInit}"`, { stdio: "inherit" }); } catch {}
63
- }
64
- }
65
-
66
- main().catch(() => {});
@@ -1,48 +0,0 @@
1
- # SKILL: AI Instructions Generator, Reviewer & Updater
2
-
3
- **Purpose:** Keep CLAUDE.md, skill files, and conventions accurate, tidy, and placed correctly as the project evolves.
4
-
5
- ---
6
-
7
- ## When to Use
8
-
9
- - A developer makes an observation that reveals a gap, mistake, or new convention.
10
- - A new pattern emerges in the code that future AI agents should know about.
11
- - A skill needs to be created, updated, or split.
12
- - CLAUDE.md needs to reflect a structural change to the repo.
13
-
14
- ---
15
-
16
- ## Decision: Practice/Convention vs. CLAUDE.md Instruction vs. Skill
17
-
18
- When an observation is made, scope it:
19
-
20
- | Type | Where it goes |
21
- |------|--------------|
22
- | One-off note or context | Inline comment in the relevant `.ai-assistance/code/*.md` doc |
23
- | Convention (how we do things here) | `CLAUDE.md` → Conventions section |
24
- | Always-do behaviour for AI agents | `CLAUDE.md` → Before Starting Any Task, or a new section |
25
- | Reusable, invocable capability | New or updated `SKILL.md` in `.ai-assistance/skills/<skill-name>/` |
26
-
27
- ---
28
-
29
- ## File Placement Rules
30
-
31
- - **CLAUDE.md** lives at repo root — one file, always.
32
- - **Skills** live at `.ai-assistance/skills/<skill-name>/SKILL.md`.
33
- - Additional assets (scripts, prompts) live alongside the SKILL.md in the same folder.
34
- - **Code specs** live at `.ai-assistance/code/` (see `code-specs` skill).
35
- - **Project files** live at `.ai-assistance/projects/<project-name>/` (see `project-cycle` skill).
36
- - **Refactoring logs** live at `.ai-assistance/code/refactoring/` (see `refactor` skill).
37
-
38
- **Never scatter instruction files in code directories.** All AI tooling lives under `.ai-assistance/`.
39
-
40
- ---
41
-
42
- ## Workflow
43
-
44
- 1. Identify the type of observation (see table above).
45
- 2. Locate the correct file. Create it if it doesn't exist.
46
- 3. Make the minimal change — don't rewrite documents unnecessarily.
47
- 4. Update the skill index table in CLAUDE.md if a new skill was added.
48
- 5. Confirm with the developer if the change affects an existing convention that other developers rely on.
@@ -1,69 +0,0 @@
1
- # SKILL: Code Specs for AI
2
-
3
- **Purpose:** Maintain AI-readable markdown documentation in `.ai-assistance/code/` so that future AI agents don't need to re-analyse code from scratch. These docs are shortcuts, not replacements — when real analysis is needed, always update them.
4
-
5
- ---
6
-
7
- ## When to Use
8
-
9
- - After analysing a new area of the codebase.
10
- - Before starting work in an area — check if a doc exists and whether it's stale.
11
- - After making changes that alter the understanding of an area.
12
- - Periodically (especially at project cycle-end) — use Gemini Assist to spot stale sections.
13
-
14
- ---
15
-
16
- ## Document Structure
17
-
18
- Each code spec document (`.ai-assistance/code/<area>.md`) must include:
19
-
20
- ```markdown
21
- # Code Spec: <Area Name>
22
-
23
- **Scope:** What this document covers. What it explicitly does NOT cover.
24
- **Last updated:** YYYY-MM-DD
25
- **Key files:** List of the most important files in this area.
26
-
27
- ---
28
-
29
- ## Overview
30
- Brief description of what this area does and its role in the gem.
31
-
32
- ## Key Classes / Modules
33
- For each significant class: one-line purpose, key public methods/interface.
34
-
35
- ## Patterns & Conventions
36
- Design patterns, inheritance chains, naming conventions specific to this area.
37
-
38
- ## Dependencies
39
- What this area depends on (internal and external). What depends on it.
40
-
41
- ## Gotchas / Non-Obvious Behaviours
42
- Things that would cause confusion or bugs if not known upfront.
43
-
44
- ## TODOs / Open Questions
45
- Pending items specific to this area. Link to project TODO if applicable.
46
- ```
47
-
48
- ---
49
-
50
- ## Naming & Placement
51
-
52
- Mirror the `lib/` structure:
53
-
54
- | Code area | Doc path |
55
- |-----------|---------|
56
- | `lib/ecoportal/api/graphql/model/` | `.ai-assistance/code/model.md` |
57
- | `lib/ecoportal/api/graphql/input/` | `.ai-assistance/code/input.md` |
58
- | `lib/ecoportal/api/graphql/logic/` | `.ai-assistance/code/logic.md` |
59
- | Whole gem overview | `.ai-assistance/code/OVERVIEW.md` |
60
-
61
- For deeply nested areas, use subdirectories: `.ai-assistance/code/model/location_structure.md`
62
-
63
- ---
64
-
65
- ## Keeping Docs Fresh
66
-
67
- - When you read a file and find something the doc doesn't mention — add it.
68
- - When a refactor changes a class — update the corresponding doc.
69
- - Use the `gemini-assist` skill for batch reviews of multiple docs vs. current code.