ecoportal-api-graphql 1.3.5 → 1.3.9
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.
- checksums.yaml +4 -4
- data/.ai-assistance/bridge/CLAUDE.md +338 -0
- data/.ai-assistance/bridge/archive/.gitkeep +0 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +29 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +18 -0
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +42 -0
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +115 -0
- data/.ai-assistance/bridge/context/gemini-review-prompt.txt +48 -0
- data/.ai-assistance/bridge/context/gemini-review-response.md +104 -0
- data/.ai-assistance/bridge/context/project.md +42 -0
- data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +48 -0
- data/.ai-assistance/bridge/queue/.gitkeep +1 -0
- data/.ai-assistance/capabilities/CLAUDE.md +27 -0
- data/.ai-assistance/capabilities/assumptions-log.md +80 -0
- data/.ai-assistance/capabilities/code.md +47 -0
- data/.ai-assistance/capabilities/connectors.md +37 -0
- data/.ai-assistance/capabilities/cowork.md +55 -0
- data/.ai-assistance/code/OVERVIEW.md +155 -0
- data/.ai-assistance/code/data_fields.md +242 -0
- data/.ai-assistance/code/dependencies.md +151 -0
- data/.ai-assistance/code/diff_as_input.md +234 -0
- data/.ai-assistance/code/diff_service_deep_dive.md +192 -0
- data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +55 -0
- data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +181 -0
- data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +192 -0
- data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +147 -0
- data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +277 -0
- data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +200 -0
- data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +228 -0
- data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +197 -0
- data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +243 -0
- data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +196 -0
- data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +132 -0
- data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +187 -0
- data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +236 -0
- data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +183 -0
- data/.ai-assistance/code/ecoportal_schema_reference.md +240 -0
- data/.ai-assistance/code/graphql_domain_knowledge.md +230 -0
- data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +71 -0
- data/.ai-assistance/code/refactoring/opportunities.md +251 -0
- data/.ai-assistance/code/schema_analysis.md +321 -0
- data/.ai-assistance/code/search_filters.md +868 -0
- data/.ai-assistance/code/spec_coverage.md +73 -0
- data/.ai-assistance/code/workflow-command-guide.md +438 -0
- data/.ai-assistance/code/workflow-space.md +353 -0
- data/.ai-assistance/conventions/CLAUDE.md +30 -0
- data/.ai-assistance/conventions/code-working-tree-protocol.md +199 -0
- data/.ai-assistance/conventions/gitignore-rules.md +42 -0
- data/.ai-assistance/conventions/permission-guidance.md +120 -0
- data/.ai-assistance/integrations/README.md +70 -0
- data/.ai-assistance/integrations/gitkraken-mcp.md +107 -0
- data/.ai-assistance/integrations/gitlab-mcp.md +123 -0
- data/.ai-assistance/integrations/local-git.md +60 -0
- data/.ai-assistance/local_paths.example.md +17 -0
- data/.ai-assistance/projects/TODO.md +97 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +168 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +60 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +267 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +53 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +102 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +139 -0
- data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +93 -0
- data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +244 -0
- data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +266 -0
- data/.ai-assistance/projects/eco-helpers-compat/TODO.md +86 -0
- data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +101 -0
- data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +177 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +161 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +125 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +126 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +256 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +122 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +148 -0
- data/.ai-assistance/projects/page-model/DECISIONS.md +245 -0
- data/.ai-assistance/projects/page-model/TODO.md +190 -0
- data/.ai-assistance/projects/search-filter-builder/INTENT.md +107 -0
- data/.ai-assistance/projects/search-filter-builder/TODO.md +131 -0
- data/.ai-assistance/projects/template-maintenance/DESIGN.md +134 -0
- data/.ai-assistance/projects/workflow-space/TODO.md +213 -0
- data/.ai-assistance/reinstall-claude-desktop-windows.md +136 -0
- data/.ai-assistance/scripts/CLAUDE.md +150 -0
- data/.ai-assistance/scripts/bridge-init.sh +86 -0
- data/.ai-assistance/scripts/bridge-status.sh +44 -0
- data/.ai-assistance/scripts/capabilities-check.ts +104 -0
- data/.ai-assistance/scripts/check-outbox.sh +43 -0
- data/.ai-assistance/scripts/dep_graph.rb +91 -0
- data/.ai-assistance/scripts/lock-acquire.sh +103 -0
- data/.ai-assistance/scripts/lock-multi.sh +124 -0
- data/.ai-assistance/scripts/lock-queue.sh +94 -0
- data/.ai-assistance/scripts/setup-mcps.test.ts +188 -0
- data/.ai-assistance/scripts/setup-mcps.ts +234 -0
- data/.ai-assistance/scripts/task-complete.ts +74 -0
- data/.ai-assistance/scripts/task-create.ts +75 -0
- data/.ai-assistance/scripts/task-read.ts +125 -0
- data/.ai-assistance/scripts/token-logger.js +220 -0
- data/.ai-assistance/scripts/token-report.ts +158 -0
- data/.ai-assistance/scripts/token-session-start.js +66 -0
- data/.ai-assistance/skills/ai-instructions/SKILL.md +48 -0
- data/.ai-assistance/skills/code-specs/SKILL.md +69 -0
- data/.ai-assistance/skills/corporate-policies/SKILL.md +201 -0
- data/.ai-assistance/skills/dep-graph/SKILL.md +139 -0
- data/.ai-assistance/skills/ep-ai-manager/SKILL.md +417 -0
- data/.ai-assistance/skills/gemini-assist/SKILL.md +63 -0
- data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +205 -0
- data/.ai-assistance/skills/gemini-assist/gemini_ask.py +1 -0
- data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +240 -0
- data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +25 -0
- data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +261 -0
- data/.ai-assistance/skills/project-cycle/SKILL.md +177 -0
- data/.ai-assistance/skills/refactor/SKILL.md +62 -0
- data/.ai-assistance/skills/rubocop/SKILL.md +93 -0
- data/.ai-assistance/skills/ruby-scripting/SKILL.md +215 -0
- data/.ai-assistance/skills/spec-generation/SKILL.md +72 -0
- data/.ai-assistance/standards-version.json +21 -0
- data/.ai-assistance/token-budget.json +32 -0
- data/.ai-assistance/version.json +39 -0
- data/.claude/settings.json +146 -0
- data/.env.example +18 -0
- data/.gitattributes +15 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +121 -97
- data/CHANGELOG.md +673 -477
- data/CLAUDE.md +232 -0
- data/Gemfile +30 -6
- data/Rakefile +90 -38
- data/docs/worklog.md +574 -0
- data/ecoportal-api-graphql.gemspec +40 -40
- data/lib/ecoportal/api/common/graphql/CLAUDE.md +36 -0
- data/lib/ecoportal/api/common/graphql/client.rb +1 -1
- data/lib/ecoportal/api/common/graphql/http_client.rb +35 -3
- data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
- data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
- data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
- data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
- data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
- data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
- data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
- data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
- data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
- data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
- data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
- data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
- data/lib/ecoportal/api/graphql/base/force.rb +65 -0
- data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
- data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
- data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
- data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
- data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -12
- data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -14
- data/lib/ecoportal/api/graphql/base/page/phased.rb +1 -0
- data/lib/ecoportal/api/graphql/base/page/section.rb +36 -0
- data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
- data/lib/ecoportal/api/graphql/base/page.rb +2 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
- data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
- data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
- data/lib/ecoportal/api/graphql/base/register.rb +18 -0
- data/lib/ecoportal/api/graphql/base.rb +11 -0
- data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
- data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
- data/lib/ecoportal/api/graphql/builder/page.rb +210 -41
- data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
- data/lib/ecoportal/api/graphql/builder/register.rb +27 -19
- data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
- data/lib/ecoportal/api/graphql/builder.rb +2 -0
- data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
- data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
- data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
- data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
- data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
- data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
- data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
- data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
- data/lib/ecoportal/api/graphql/compat.rb +17 -0
- data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
- data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
- data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +21 -28
- data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
- data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
- data/lib/ecoportal/api/graphql/concerns.rb +4 -0
- data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
- data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
- data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
- data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
- data/lib/ecoportal/api/graphql/connection.rb +4 -0
- data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
- data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
- data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
- data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
- data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
- data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
- data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
- data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
- data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment.rb +37 -27
- data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
- data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
- data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
- data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
- data/lib/ecoportal/api/graphql/input/page.rb +26 -0
- data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
- data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
- data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
- data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
- data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
- data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
- data/lib/ecoportal/api/graphql/input/register.rb +13 -0
- data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
- data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
- data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
- data/lib/ecoportal/api/graphql/input.rb +8 -0
- data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
- data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
- data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
- data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
- data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
- data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
- data/lib/ecoportal/api/graphql/model/organization.rb +65 -55
- data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
- data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -20
- data/lib/ecoportal/api/graphql/model/page.rb +1 -0
- data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
- data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
- data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
- data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
- data/lib/ecoportal/api/graphql/model/register.rb +10 -0
- data/lib/ecoportal/api/graphql/model.rb +8 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
- data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
- data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
- data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
- data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
- data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
- data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
- data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
- data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
- data/lib/ecoportal/api/graphql/mutation.rb +8 -0
- data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
- data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
- data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
- data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
- data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
- data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
- data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
- data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
- data/lib/ecoportal/api/graphql/payload.rb +11 -0
- data/lib/ecoportal/api/graphql/query/action.rb +1 -1
- data/lib/ecoportal/api/graphql/query/action_categories.rb +1 -1
- data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
- data/lib/ecoportal/api/graphql/query/contractor_entities.rb +1 -1
- data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
- data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
- data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
- data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
- data/lib/ecoportal/api/graphql/query/page.rb +45 -0
- data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
- data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
- data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
- data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
- data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
- data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
- data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
- data/lib/ecoportal/api/graphql/query.rb +11 -0
- data/lib/ecoportal/api/graphql.rb +60 -2
- data/lib/ecoportal/api/graphql_version.rb +5 -5
- data/scripts/auto-worker-scheduler.sh +386 -0
- data/tests/contractor_entity_create.rb +19 -19
- data/tests/contractor_entity_udpate.rb +20 -20
- data/tests/dump_page_model.rb +74 -0
- data/tests/loc_structure_get.rb +1 -2
- data/tests/loc_structure_update.rb +51 -51
- data/tests/loc_structures_get.rb +15 -15
- metadata +436 -5
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lock-queue.sh
|
|
3
|
+
# Drop a priority turn-request into the bridge queue of one or more repos.
|
|
4
|
+
# Used when a lock is held and this agent wants to wait with declared priority.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# bash lock-queue.sh \
|
|
8
|
+
# --agent cowork \
|
|
9
|
+
# --user oscar \
|
|
10
|
+
# --intent "Review auth changes" \
|
|
11
|
+
# --priority high \
|
|
12
|
+
# --target-project ep-claude-aws-platform \
|
|
13
|
+
# [--repo /path/to/repo1 --repo /path/to/repo2 ...] \
|
|
14
|
+
# [--expiry-minutes 90]
|
|
15
|
+
#
|
|
16
|
+
# Writes: .ai-assistance/bridge/queue/<priority>-<ts>-<id>.json in each repo
|
|
17
|
+
# Exit 0: request(s) queued. Prints JSON with queue file paths.
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
AGENT="" USER_SLUG="" INTENT="" PRIORITY="medium" TARGET_PROJECT=""
|
|
22
|
+
EXPIRY_MINUTES=90 ESTIMATED_WAIT_MINUTES=""
|
|
23
|
+
REPOS=()
|
|
24
|
+
|
|
25
|
+
while [[ $# -gt 0 ]]; do
|
|
26
|
+
case "$1" in
|
|
27
|
+
--agent) AGENT="$2"; shift 2 ;;
|
|
28
|
+
--user) USER_SLUG="$2"; shift 2 ;;
|
|
29
|
+
--intent) INTENT="$2"; shift 2 ;;
|
|
30
|
+
--priority) PRIORITY="$2"; shift 2 ;;
|
|
31
|
+
--target-project) TARGET_PROJECT="$2"; shift 2 ;;
|
|
32
|
+
--estimated-wait) ESTIMATED_WAIT_MINUTES="$2"; shift 2 ;;
|
|
33
|
+
--repo) REPOS+=("$2"); shift 2 ;;
|
|
34
|
+
--expiry-minutes) EXPIRY_MINUTES="$2"; shift 2 ;;
|
|
35
|
+
*) echo "{\"queued\":false,\"reason\":\"unknown argument: $1\"}"; exit 1 ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
[[ -z "$AGENT" || -z "$USER_SLUG" || -z "$INTENT" ]] && {
|
|
40
|
+
echo '{"queued":false,"reason":"missing --agent, --user, or --intent"}'; exit 1; }
|
|
41
|
+
|
|
42
|
+
# Default: current repo
|
|
43
|
+
[[ ${#REPOS[@]} -eq 0 ]] && REPOS=("$(pwd)")
|
|
44
|
+
|
|
45
|
+
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
46
|
+
EXPIRES=$(date -u -d "+${EXPIRY_MINUTES} minutes" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
|
|
47
|
+
|| date -u -v +${EXPIRY_MINUTES}M +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
|
|
48
|
+
|| echo "unknown")
|
|
49
|
+
REQUEST_ID="req-$(date +%s)-$$"
|
|
50
|
+
|
|
51
|
+
# Priority sort prefix: 1=critical 2=high 3=medium 4=low
|
|
52
|
+
case "$PRIORITY" in
|
|
53
|
+
critical) PFX="1" ;; high) PFX="2" ;; medium) PFX="3" ;; low) PFX="4" ;; *) PFX="3" ;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
QUEUED_FILES=()
|
|
57
|
+
|
|
58
|
+
# estimated_wait: read from the current LOCK file if present (it may contain estimated_release)
|
|
59
|
+
get_estimated_release() {
|
|
60
|
+
local lock="$1/.ai-assistance/bridge/LOCK"
|
|
61
|
+
[[ -f "$lock" ]] || { echo "unknown"; return; }
|
|
62
|
+
grep "estimated_release:" "$lock" 2>/dev/null | sed 's/.*estimated_release://' | tr -d ' ' | head -1 || echo "unknown"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for repo in "${REPOS[@]}"; do
|
|
66
|
+
QUEUE_DIR="$repo/.ai-assistance/bridge/queue"
|
|
67
|
+
[[ -d "$QUEUE_DIR" ]] || mkdir -p "$QUEUE_DIR"
|
|
68
|
+
QFILE="$QUEUE_DIR/${PFX}-${PRIORITY}-$(date +%s%3N)-${REQUEST_ID}.json"
|
|
69
|
+
|
|
70
|
+
# Read current lock's estimated release so queue entry shows the expected wait
|
|
71
|
+
LOCK_ESTIMATED_RELEASE=$(get_estimated_release "$repo")
|
|
72
|
+
EFFECTIVE_WAIT="${ESTIMATED_WAIT_MINUTES:-unknown}"
|
|
73
|
+
|
|
74
|
+
cat > "$QFILE" << JSON
|
|
75
|
+
{
|
|
76
|
+
"request_id": "$REQUEST_ID",
|
|
77
|
+
"agent": "$AGENT",
|
|
78
|
+
"user": "$USER_SLUG",
|
|
79
|
+
"intent": "$INTENT",
|
|
80
|
+
"priority": "$PRIORITY",
|
|
81
|
+
"target_project": "$TARGET_PROJECT",
|
|
82
|
+
"repo": "$repo",
|
|
83
|
+
"requested_at": "$NOW",
|
|
84
|
+
"expires_at": "$EXPIRES",
|
|
85
|
+
"estimated_wait_minutes": "$EFFECTIVE_WAIT",
|
|
86
|
+
"current_lock_estimated_release": "$LOCK_ESTIMATED_RELEASE"
|
|
87
|
+
}
|
|
88
|
+
JSON
|
|
89
|
+
QUEUED_FILES+=("$QFILE")
|
|
90
|
+
done
|
|
91
|
+
|
|
92
|
+
# Report
|
|
93
|
+
FILES_JSON=$(printf '"%s",' "${QUEUED_FILES[@]}" | sed 's/,$//')
|
|
94
|
+
echo "{\"queued\":true,\"request_id\":\"$REQUEST_ID\",\"priority\":\"$PRIORITY\",\"files\":[$FILES_JSON]}"
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env npx ts-node
|
|
2
|
+
/**
|
|
3
|
+
* setup-mcps.test.ts
|
|
4
|
+
* Unit tests for the pure logic in setup-mcps.ts.
|
|
5
|
+
* No external dependencies — uses Node's assert module.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx ts-node .ai-assistance/scripts/setup-mcps.test.ts
|
|
9
|
+
*
|
|
10
|
+
* Tests cover:
|
|
11
|
+
* - parseEnvFile: key=value parsing, comments, blank lines, edge cases
|
|
12
|
+
* - buildMcpEntries: correct shape for GitKraken + GitLab entries
|
|
13
|
+
* - mergeConfig: merges, preserves unrelated keys, overwrites same-named entries
|
|
14
|
+
* - getConfigPath: returns a string containing "Claude" on current platform
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import assert from "assert";
|
|
18
|
+
import { parseEnvFile, buildMcpEntries, mergeConfig, getConfigPath } from "./setup-mcps";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Test runner
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
let passed = 0;
|
|
25
|
+
let failed = 0;
|
|
26
|
+
|
|
27
|
+
function test(name: string, fn: () => void) {
|
|
28
|
+
try {
|
|
29
|
+
fn();
|
|
30
|
+
console.log(` ✓ ${name}`);
|
|
31
|
+
passed++;
|
|
32
|
+
} catch (err: unknown) {
|
|
33
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34
|
+
console.log(` ✗ ${name}`);
|
|
35
|
+
console.log(` ${message}`);
|
|
36
|
+
failed++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// parseEnvFile
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
console.log("\nparseEnvFile");
|
|
45
|
+
|
|
46
|
+
test("parses simple key=value pairs", () => {
|
|
47
|
+
const result = parseEnvFile("FOO=bar\nBAZ=qux");
|
|
48
|
+
assert.strictEqual(result["FOO"], "bar");
|
|
49
|
+
assert.strictEqual(result["BAZ"], "qux");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("ignores comment lines", () => {
|
|
53
|
+
const result = parseEnvFile("# this is a comment\nFOO=bar");
|
|
54
|
+
assert.strictEqual(Object.keys(result).length, 1);
|
|
55
|
+
assert.strictEqual(result["FOO"], "bar");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("ignores blank lines", () => {
|
|
59
|
+
const result = parseEnvFile("\n\nFOO=bar\n\n");
|
|
60
|
+
assert.strictEqual(Object.keys(result).length, 1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("handles values containing = signs", () => {
|
|
64
|
+
const result = parseEnvFile("URL=https://example.com?foo=bar");
|
|
65
|
+
assert.strictEqual(result["URL"], "https://example.com?foo=bar");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("returns empty object for empty input", () => {
|
|
69
|
+
const result = parseEnvFile("");
|
|
70
|
+
assert.deepStrictEqual(result, {});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("returns empty object for comment-only input", () => {
|
|
74
|
+
const result = parseEnvFile("# just a comment\n# another");
|
|
75
|
+
assert.deepStrictEqual(result, {});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// buildMcpEntries
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
console.log("\nbuildMcpEntries");
|
|
83
|
+
|
|
84
|
+
test("includes gitkraken entry with correct command", () => {
|
|
85
|
+
const entries = buildMcpEntries("tok", "https://gitlab.example.com");
|
|
86
|
+
const gk = entries["gitkraken"] as Record<string, unknown>;
|
|
87
|
+
assert.strictEqual(gk.command, "gk");
|
|
88
|
+
assert.deepStrictEqual(gk.args, ["mcp"]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("includes gitlab-mr entry with correct command", () => {
|
|
92
|
+
const entries = buildMcpEntries("tok", "https://gitlab.example.com");
|
|
93
|
+
const gl = entries["gitlab-mr"] as Record<string, unknown>;
|
|
94
|
+
assert.strictEqual(gl.command, "npx");
|
|
95
|
+
assert.deepStrictEqual(gl.args, ["-y", "@kopfrechner/gitlab-mr-mcp"]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("injects GITLAB_TOKEN into gitlab-mr env", () => {
|
|
99
|
+
const entries = buildMcpEntries("my-token", "https://gitlab.example.com");
|
|
100
|
+
const gl = entries["gitlab-mr"] as { env: Record<string, string> };
|
|
101
|
+
assert.strictEqual(gl.env["GITLAB_TOKEN"], "my-token");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("injects GITLAB_URL into gitlab-mr env", () => {
|
|
105
|
+
const entries = buildMcpEntries("tok", "https://gitlab.example.com");
|
|
106
|
+
const gl = entries["gitlab-mr"] as { env: Record<string, string> };
|
|
107
|
+
assert.strictEqual(gl.env["GITLAB_URL"], "https://gitlab.example.com");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("returns exactly two top-level keys", () => {
|
|
111
|
+
const entries = buildMcpEntries("tok", "https://gitlab.example.com");
|
|
112
|
+
assert.strictEqual(Object.keys(entries).length, 2);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// mergeConfig
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
console.log("\nmergeConfig");
|
|
120
|
+
|
|
121
|
+
test("adds mcpServers to empty config", () => {
|
|
122
|
+
const result = mergeConfig({}, { foo: { command: "foo" } });
|
|
123
|
+
assert.ok(result.mcpServers);
|
|
124
|
+
assert.ok((result.mcpServers as Record<string, unknown>)["foo"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("preserves existing top-level keys", () => {
|
|
128
|
+
const result = mergeConfig({ theme: "dark" }, { foo: {} });
|
|
129
|
+
assert.strictEqual(result.theme, "dark");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("preserves existing mcpServers entries not in newEntries", () => {
|
|
133
|
+
const existing = { mcpServers: { existing: { command: "old" } } };
|
|
134
|
+
const result = mergeConfig(existing, { newone: { command: "new" } });
|
|
135
|
+
const servers = result.mcpServers as Record<string, unknown>;
|
|
136
|
+
assert.ok(servers["existing"]);
|
|
137
|
+
assert.ok(servers["newone"]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("overwrites same-named mcpServers entries", () => {
|
|
141
|
+
const existing = {
|
|
142
|
+
mcpServers: { "gitlab-mr": { command: "old", env: { GITLAB_TOKEN: "old-tok" } } },
|
|
143
|
+
};
|
|
144
|
+
const result = mergeConfig(existing, {
|
|
145
|
+
"gitlab-mr": { command: "npx", env: { GITLAB_TOKEN: "new-tok" } },
|
|
146
|
+
});
|
|
147
|
+
const gl = (result.mcpServers as Record<string, unknown>)["gitlab-mr"] as {
|
|
148
|
+
env: Record<string, string>;
|
|
149
|
+
};
|
|
150
|
+
assert.strictEqual(gl.env["GITLAB_TOKEN"], "new-tok");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("handles existing config with no mcpServers key", () => {
|
|
154
|
+
const result = mergeConfig({ theme: "light" }, { foo: {} });
|
|
155
|
+
assert.ok((result.mcpServers as Record<string, unknown>)["foo"]);
|
|
156
|
+
assert.strictEqual(result.theme, "light");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// getConfigPath
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
console.log("\ngetConfigPath");
|
|
164
|
+
|
|
165
|
+
test("returns a non-empty string", () => {
|
|
166
|
+
const p = getConfigPath();
|
|
167
|
+
assert.ok(typeof p === "string" && p.length > 0);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("path contains 'Claude'", () => {
|
|
171
|
+
const p = getConfigPath();
|
|
172
|
+
assert.ok(p.includes("Claude"), `Expected path to contain 'Claude', got: ${p}`);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("path ends with claude_desktop_config.json", () => {
|
|
176
|
+
const p = getConfigPath();
|
|
177
|
+
assert.ok(
|
|
178
|
+
p.endsWith("claude_desktop_config.json"),
|
|
179
|
+
`Expected path to end with filename, got: ${p}`
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Summary
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed\n`);
|
|
188
|
+
if (failed > 0) process.exit(1);
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/usr/bin/env npx ts-node
|
|
2
|
+
/**
|
|
3
|
+
* setup-mcps.ts
|
|
4
|
+
* One-time setup: writes GitKraken and GitLab MCP entries into Claude Desktop's
|
|
5
|
+
* global config (claude_desktop_config.json). Safe to re-run — merges into any
|
|
6
|
+
* existing config without overwriting unrelated entries.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx ts-node .ai-assistance/scripts/setup-mcps.ts
|
|
10
|
+
*
|
|
11
|
+
* What it does:
|
|
12
|
+
* 1. Detects OS and locates claude_desktop_config.json
|
|
13
|
+
* 2. Reads GitLab credentials from .env as defaults (no re-entry needed)
|
|
14
|
+
* 3. Prompts to confirm or override each value
|
|
15
|
+
* 4. Backs up the existing config before writing
|
|
16
|
+
* 5. Merges GitKraken + GitLab MCP entries and writes the config
|
|
17
|
+
* 6. Prints verification steps
|
|
18
|
+
*
|
|
19
|
+
* Requirements:
|
|
20
|
+
* - GitKraken CLI (gk) installed + authenticated:
|
|
21
|
+
* https://www.gitkraken.com/cli
|
|
22
|
+
* Run: gk auth login
|
|
23
|
+
* - GitLab PAT with scopes: read_api, read_repository, create_runner, ai_features
|
|
24
|
+
* https://gitlab.ecoportal.co.nz/-/user_settings/personal_access_tokens
|
|
25
|
+
* - Node.js + ts-node:
|
|
26
|
+
* npm install -g ts-node typescript
|
|
27
|
+
*
|
|
28
|
+
* Expiry note:
|
|
29
|
+
* Current PAT expires April 2027. Token has rotation enabled — rotate before
|
|
30
|
+
* expiry and re-run this script with the new token.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
readFileSync,
|
|
35
|
+
writeFileSync,
|
|
36
|
+
existsSync,
|
|
37
|
+
copyFileSync,
|
|
38
|
+
mkdirSync,
|
|
39
|
+
} from "fs";
|
|
40
|
+
import { join, dirname } from "path";
|
|
41
|
+
import { createInterface } from "readline";
|
|
42
|
+
import { homedir, platform } from "os";
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Pure logic (exported for testing)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/** Detect OS-appropriate path to claude_desktop_config.json */
|
|
49
|
+
export function getConfigPath(): string {
|
|
50
|
+
const p = platform();
|
|
51
|
+
if (p === "win32") {
|
|
52
|
+
const appData =
|
|
53
|
+
process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
|
|
54
|
+
return join(appData, "Claude", "claude_desktop_config.json");
|
|
55
|
+
} else if (p === "darwin") {
|
|
56
|
+
return join(
|
|
57
|
+
homedir(),
|
|
58
|
+
"Library",
|
|
59
|
+
"Application Support",
|
|
60
|
+
"Claude",
|
|
61
|
+
"claude_desktop_config.json"
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
// Linux fallback
|
|
65
|
+
return join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Parse KEY=VALUE lines from a .env file. Ignores comments and blank lines. */
|
|
70
|
+
export function parseEnvFile(content: string): Record<string, string> {
|
|
71
|
+
const env: Record<string, string> = {};
|
|
72
|
+
for (const line of content.split("\n")) {
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
75
|
+
const eq = trimmed.indexOf("=");
|
|
76
|
+
if (eq === -1) continue;
|
|
77
|
+
const key = trimmed.slice(0, eq).trim();
|
|
78
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
79
|
+
if (key) env[key] = value;
|
|
80
|
+
}
|
|
81
|
+
return env;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Build the mcpServers block for GitKraken + GitLab. */
|
|
85
|
+
export function buildMcpEntries(
|
|
86
|
+
gitlabToken: string,
|
|
87
|
+
gitlabUrl: string
|
|
88
|
+
): Record<string, unknown> {
|
|
89
|
+
return {
|
|
90
|
+
gitkraken: {
|
|
91
|
+
command: "gk",
|
|
92
|
+
args: ["mcp"],
|
|
93
|
+
},
|
|
94
|
+
"gitlab-mr": {
|
|
95
|
+
command: "npx",
|
|
96
|
+
args: ["-y", "@kopfrechner/gitlab-mr-mcp"],
|
|
97
|
+
env: {
|
|
98
|
+
GITLAB_TOKEN: gitlabToken,
|
|
99
|
+
GITLAB_URL: gitlabUrl,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Merge new MCP entries into an existing config object.
|
|
107
|
+
* Existing mcpServers entries not in newEntries are preserved.
|
|
108
|
+
* Entries in newEntries overwrite same-named existing entries.
|
|
109
|
+
*/
|
|
110
|
+
export function mergeConfig(
|
|
111
|
+
existing: Record<string, unknown>,
|
|
112
|
+
newEntries: Record<string, unknown>
|
|
113
|
+
): Record<string, unknown> {
|
|
114
|
+
return {
|
|
115
|
+
...existing,
|
|
116
|
+
mcpServers: {
|
|
117
|
+
...((existing.mcpServers as Record<string, unknown>) ?? {}),
|
|
118
|
+
...newEntries,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// I/O helpers
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
function ask(question: string): Promise<string> {
|
|
128
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
rl.question(question, (answer) => {
|
|
131
|
+
rl.close();
|
|
132
|
+
resolve(answer.trim());
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function loadEnvDefaults(repoRoot: string): Record<string, string> {
|
|
138
|
+
const envPath = join(repoRoot, ".env");
|
|
139
|
+
if (!existsSync(envPath)) return {};
|
|
140
|
+
return parseEnvFile(readFileSync(envPath, "utf8"));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Main
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
async function main() {
|
|
148
|
+
// Resolve repo root relative to this script's location
|
|
149
|
+
const scriptDir = dirname(
|
|
150
|
+
process.argv[1]
|
|
151
|
+
);
|
|
152
|
+
const repoRoot = join(scriptDir, "../..");
|
|
153
|
+
const configPath = getConfigPath();
|
|
154
|
+
const env = loadEnvDefaults(repoRoot);
|
|
155
|
+
|
|
156
|
+
console.log("\n🔧 Claude Desktop MCP Setup");
|
|
157
|
+
console.log("============================");
|
|
158
|
+
console.log(`Config: ${configPath}\n`);
|
|
159
|
+
|
|
160
|
+
// Load existing config
|
|
161
|
+
let existing: Record<string, unknown> = {};
|
|
162
|
+
if (existsSync(configPath)) {
|
|
163
|
+
existing = JSON.parse(readFileSync(configPath, "utf8"));
|
|
164
|
+
const count = Object.keys(
|
|
165
|
+
(existing.mcpServers as Record<string, unknown>) ?? {}
|
|
166
|
+
).length;
|
|
167
|
+
console.log(`Found existing config with ${count} MCP server(s) — will merge.\n`);
|
|
168
|
+
} else {
|
|
169
|
+
console.log("No existing config — will create fresh.\n");
|
|
170
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// --- GitLab PAT ---
|
|
174
|
+
const defaultToken = env["GITLAB_TOKEN"] ?? "";
|
|
175
|
+
const tokenHint = defaultToken
|
|
176
|
+
? `from .env, ends …${defaultToken.slice(-6)}`
|
|
177
|
+
: "required";
|
|
178
|
+
const tokenInput = await ask(
|
|
179
|
+
`GitLab PAT [${tokenHint}] (press Enter to keep): `
|
|
180
|
+
);
|
|
181
|
+
const gitlabToken = tokenInput || defaultToken;
|
|
182
|
+
|
|
183
|
+
if (!gitlabToken) {
|
|
184
|
+
console.error(
|
|
185
|
+
"\n❌ GitLab PAT is required.\n" +
|
|
186
|
+
" Create one at: https://gitlab.ecoportal.co.nz/-/user_settings/personal_access_tokens\n" +
|
|
187
|
+
" Required scopes: read_api, read_repository, create_runner, ai_features\n" +
|
|
188
|
+
" Enable token rotation. Current expiry: April 2027.\n"
|
|
189
|
+
);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- GitLab URL ---
|
|
194
|
+
const defaultUrl =
|
|
195
|
+
env["GITLAB_URL"] ?? "https://gitlab.ecoportal.co.nz";
|
|
196
|
+
const urlInput = await ask(`GitLab URL [${defaultUrl}]: `);
|
|
197
|
+
const gitlabUrl = urlInput || defaultUrl;
|
|
198
|
+
|
|
199
|
+
// --- Build + merge ---
|
|
200
|
+
const newEntries = buildMcpEntries(gitlabToken, gitlabUrl);
|
|
201
|
+
const merged = mergeConfig(existing, newEntries);
|
|
202
|
+
|
|
203
|
+
// --- Backup ---
|
|
204
|
+
if (existsSync(configPath)) {
|
|
205
|
+
const backupPath = configPath + ".backup";
|
|
206
|
+
copyFileSync(configPath, backupPath);
|
|
207
|
+
console.log(`\n✓ Backed up to ${backupPath}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// --- Write ---
|
|
211
|
+
writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
212
|
+
console.log(`✓ Written to ${configPath}`);
|
|
213
|
+
|
|
214
|
+
console.log(`
|
|
215
|
+
✅ Done! Next steps:
|
|
216
|
+
|
|
217
|
+
1. Restart Claude Desktop (fully quit and reopen)
|
|
218
|
+
|
|
219
|
+
2. Verify GitKraken — ask Claude:
|
|
220
|
+
"What branch am I on in ecoportal-api-graphql?"
|
|
221
|
+
First use opens a browser for GitKraken OAuth — approve it.
|
|
222
|
+
|
|
223
|
+
3. Verify GitLab — ask Claude:
|
|
224
|
+
"List open merge requests in ecoportal-api-graphql"
|
|
225
|
+
|
|
226
|
+
Note: if GitKraken (gk) isn't installed yet:
|
|
227
|
+
https://www.gitkraken.com/cli → gk auth login
|
|
228
|
+
`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
main().catch((err) => {
|
|
232
|
+
console.error("\n❌ Setup failed:", err.message);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env npx ts-node
|
|
2
|
+
/**
|
|
3
|
+
* task-complete.ts
|
|
4
|
+
* Writes an outbox file and archives the inbox+outbox pair.
|
|
5
|
+
* Called by Code after finishing a bridge task.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx ts-node .ai-assistance/scripts/task-complete.ts \
|
|
9
|
+
* --task oscar-a3f2b1c-list-open-mrs.md \
|
|
10
|
+
* --status done \
|
|
11
|
+
* --result "Branch: feature/auth. Open MRs: 3 ..." \
|
|
12
|
+
* --notes "GitLab plugin authenticated OK"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { writeFileSync, renameSync, readFileSync, mkdirSync, existsSync } from "fs";
|
|
16
|
+
import { join, dirname } from "path";
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const get = (flag: string, required = true): string | undefined => {
|
|
20
|
+
const i = args.indexOf(flag);
|
|
21
|
+
if (i === -1 || i + 1 >= args.length) {
|
|
22
|
+
if (required) { console.error(`Missing required argument: ${flag}`); process.exit(1); }
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return args[i + 1];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const taskFile = get("--task")!;
|
|
29
|
+
const status = (get("--status")! as string).toUpperCase(); // DONE or FAILED
|
|
30
|
+
const result = get("--result")!;
|
|
31
|
+
const notes = get("--notes", false) ?? "";
|
|
32
|
+
|
|
33
|
+
const scriptDir = dirname(process.argv[1]);
|
|
34
|
+
const bridgeDir = join(scriptDir, "../bridge");
|
|
35
|
+
const inboxPath = join(bridgeDir, "inbox", taskFile);
|
|
36
|
+
const outboxDir = join(bridgeDir, "outbox");
|
|
37
|
+
const archiveDir = join(bridgeDir, "archive");
|
|
38
|
+
|
|
39
|
+
if (!existsSync(inboxPath)) {
|
|
40
|
+
console.error(`Inbox task not found: ${inboxPath}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
mkdirSync(outboxDir, { recursive: true });
|
|
45
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
46
|
+
|
|
47
|
+
// Read title from inbox
|
|
48
|
+
const inboxContent = readFileSync(inboxPath, "utf8");
|
|
49
|
+
const titleMatch = inboxContent.match(/^# TASK: (.+)$/m);
|
|
50
|
+
const title = titleMatch?.[1] ?? taskFile;
|
|
51
|
+
|
|
52
|
+
// Write outbox
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
const outboxContent = `# TASK: ${title}
|
|
55
|
+
STATUS: ${status}
|
|
56
|
+
COMPLETED: ${timestamp}
|
|
57
|
+
FROM: code
|
|
58
|
+
TO: cowork
|
|
59
|
+
|
|
60
|
+
## Result
|
|
61
|
+
${result}
|
|
62
|
+
|
|
63
|
+
## Notes
|
|
64
|
+
${notes || "(none)"}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const outboxPath = join(outboxDir, taskFile);
|
|
68
|
+
writeFileSync(outboxPath, outboxContent, "utf8");
|
|
69
|
+
|
|
70
|
+
// Archive both files
|
|
71
|
+
renameSync(inboxPath, join(archiveDir, `inbox-${taskFile}`));
|
|
72
|
+
renameSync(outboxPath, join(archiveDir, `outbox-${taskFile}`));
|
|
73
|
+
|
|
74
|
+
console.log(JSON.stringify({ archived: taskFile, status, timestamp }));
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env npx ts-node
|
|
2
|
+
/**
|
|
3
|
+
* task-create.ts
|
|
4
|
+
* Creates a new bridge inbox task file with correct naming and format.
|
|
5
|
+
* Keeps token usage low — agents call this instead of composing files manually.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx ts-node .ai-assistance/scripts/task-create.ts \
|
|
9
|
+
* --title "List open MRs" \
|
|
10
|
+
* --request "Run git branch --show-current, then list open MRs via GitLab plugin" \
|
|
11
|
+
* --expected "Branch name + MR table" \
|
|
12
|
+
* --from cowork \
|
|
13
|
+
* --to code \
|
|
14
|
+
* --user oscar
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
18
|
+
import { join, dirname } from "path";
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
|
|
21
|
+
// --- Parse args ---
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const get = (flag: string): string => {
|
|
24
|
+
const i = args.indexOf(flag);
|
|
25
|
+
if (i === -1 || i + 1 >= args.length) {
|
|
26
|
+
console.error(`Missing required argument: ${flag}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
return args[i + 1];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const title = get("--title");
|
|
33
|
+
const request = get("--request");
|
|
34
|
+
const expected = get("--expected");
|
|
35
|
+
const from = get("--from");
|
|
36
|
+
const to = get("--to");
|
|
37
|
+
const user = get("--user");
|
|
38
|
+
const context = args.includes("--context") ? get("--context") : "";
|
|
39
|
+
|
|
40
|
+
// --- Generate filename ---
|
|
41
|
+
const uuid7 = randomUUID().replace(/-/g, "").slice(0, 7);
|
|
42
|
+
const slug = title
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
45
|
+
.trim()
|
|
46
|
+
.replace(/\s+/g, "-")
|
|
47
|
+
.slice(0, 40);
|
|
48
|
+
const filename = `${user}-${uuid7}-${slug}.md`;
|
|
49
|
+
|
|
50
|
+
// --- Build file content ---
|
|
51
|
+
const timestamp = new Date().toISOString();
|
|
52
|
+
const content = `# TASK: ${title}
|
|
53
|
+
STATUS: PENDING
|
|
54
|
+
CREATED: ${timestamp}
|
|
55
|
+
FROM: ${from}
|
|
56
|
+
TO: ${to}
|
|
57
|
+
|
|
58
|
+
## Context
|
|
59
|
+
${context || "(none provided)"}
|
|
60
|
+
|
|
61
|
+
## Request
|
|
62
|
+
${request}
|
|
63
|
+
|
|
64
|
+
## Expected output
|
|
65
|
+
${expected}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
// --- Write file ---
|
|
69
|
+
const scriptDir = dirname(process.argv[1]);
|
|
70
|
+
const inboxDir = join(scriptDir, "../bridge/inbox");
|
|
71
|
+
mkdirSync(inboxDir, { recursive: true });
|
|
72
|
+
const filePath = join(inboxDir, filename);
|
|
73
|
+
writeFileSync(filePath, content, "utf8");
|
|
74
|
+
|
|
75
|
+
console.log(JSON.stringify({ created: filename, path: filePath }));
|