@bridge_gpt/mcp-server 0.1.1 → 0.1.3
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.
- package/README.md +37 -1
- package/build/commands.generated.js +20 -0
- package/build/index.js +193 -2
- package/build/pipeline-utils.js +93 -0
- package/build/pipelines.generated.js +14 -286
- package/package.json +3 -2
- package/pipelines/check-ci-ticket.json +35 -0
- package/pipelines/implement-ticket.json +51 -0
- package/pipelines/learn-repository.json +216 -0
- package/pipelines/pr-ticket.json +30 -0
- package/pipelines/review-ticket.json +68 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ MCP server for [Bridge API](https://bridgegpt-api.com) — exposes Jira integrat
|
|
|
10
10
|
| `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |
|
|
11
11
|
| `BAPI_API_KEY` | Yes | _(none)_ | API key obtained from the Bridge API setup UI |
|
|
12
12
|
| `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |
|
|
13
|
+
| `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |
|
|
13
14
|
|
|
14
15
|
## Configuration
|
|
15
16
|
|
|
@@ -113,6 +114,16 @@ BAPI_API_KEY = "your-api-key"
|
|
|
113
114
|
BAPI_DOCS_DIR = "docs/tmp"
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
## Slash Commands
|
|
118
|
+
|
|
119
|
+
Bridge API ships pre-built slash commands for Claude Code and Cursor. To scaffold them into your project:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx @bridge_gpt/mcp-server --init
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This writes command files to `.claude/commands/` and `.cursor/commands/` in your current directory. Re-run after upgrading the package to get updated commands.
|
|
126
|
+
|
|
116
127
|
## Available Tools
|
|
117
128
|
|
|
118
129
|
| Tool | Description |
|
|
@@ -159,7 +170,32 @@ BAPI_DOCS_DIR = "docs/tmp"
|
|
|
159
170
|
|
|
160
171
|
### Pipeline Recipes
|
|
161
172
|
|
|
162
|
-
The `list_pipelines` and `get_pipeline_recipe` tools provide access to declarative multi-step workflow recipes (e.g., `implement-ticket`, `review-ticket`, `
|
|
173
|
+
The `list_pipelines` and `get_pipeline_recipe` tools provide access to declarative multi-step workflow recipes (e.g., `implement-ticket`, `review-ticket`, `learn-repository`). These are resolved locally from bundled definitions — no additional server calls are needed.
|
|
174
|
+
|
|
175
|
+
Example pipeline JSON files are included in the published package under `pipelines/` for reference when authoring custom pipelines.
|
|
176
|
+
|
|
177
|
+
### Custom Pipelines
|
|
178
|
+
|
|
179
|
+
You can create your own pipelines by placing JSON files in `.bridge/pipelines/` (or the directory specified by `BAPI_PIPELINES_DIR`). Custom pipelines are loaded at server startup and appear alongside bundled pipelines in `list_pipelines` with `source: "user"`.
|
|
180
|
+
|
|
181
|
+
**Getting started:**
|
|
182
|
+
|
|
183
|
+
1. Run `npx @bridge_gpt/mcp-server --init` to scaffold the directory structure with a README and example pipeline.
|
|
184
|
+
2. Add pipeline JSON files to `.bridge/pipelines/`.
|
|
185
|
+
3. Optionally add instruction markdown files to `.bridge/instructions/` and reference them via `instruction_file` in your pipeline steps.
|
|
186
|
+
|
|
187
|
+
**Pipeline JSON schema:**
|
|
188
|
+
|
|
189
|
+
- `name` (string, required) — display name
|
|
190
|
+
- `description` (string, optional) — shown in `list_pipelines`
|
|
191
|
+
- `variables` (string[], optional) — variable names for `{variable_name}` substitution
|
|
192
|
+
- `steps` (array, required) — ordered list of `mcp_call` or `agent_task` steps
|
|
193
|
+
|
|
194
|
+
Each step supports `on_error` (`"halt"` or `"warn_and_continue"`) and `requires_approval` (boolean). See the generated `README.md` in `.bridge/pipelines/` for full documentation.
|
|
195
|
+
|
|
196
|
+
If a custom pipeline has the same key as a bundled pipeline, it overrides the bundled one (a warning is logged at startup).
|
|
197
|
+
|
|
198
|
+
> **Note:** The `instructions/` directory is always resolved as a sibling of the pipelines directory (i.e. one level up from `BAPI_PIPELINES_DIR`). With the default `.bridge/pipelines`, instructions are read from `.bridge/instructions/`.
|
|
163
199
|
|
|
164
200
|
## Publishing
|
|
165
201
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
|
|
2
|
+
// This file is produced by scripts/bundle-commands.js
|
|
3
|
+
export const COMMANDS = {
|
|
4
|
+
"check-ci.md": "# Check CI: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes a Jira ticket key (e.g., `BAPI-150`), discovers CI checks for the current commit, polls their status, and applies confidence-gated code corrections for failures. It is designed to run after `/create-pr` completes.\n\nIf any critical stage fails (Stage 0), stop immediately and report which stage failed and why. Non-critical stages (Stage 1, Stage 2, and Stage 3) should log a warning but not stop the pipeline.\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline to monitor and respond to CI checks for a Jira ticket. Execute all stages in sequence.\n\n## Stage 0 — Setup and Argument Parsing\n\n1. **Parse `$ARGUMENTS`**: Extract a single required `ticket_key` argument. The expected format is a Jira ticket key such as `BAPI-150` or `PROJ-123` — one or more uppercase letters, a hyphen, and one or more digits (regex: `[A-Z]+-\\d+`). If `$ARGUMENTS` is empty or the value does not match the expected format, stop immediately and display:\n\n ```\n Invalid ticket key format: '<value>'. Expected format: PROJ-123 (uppercase letters, hyphen, digits).\n Usage: /check-ci <ticket_key> (e.g., /check-ci BAPI-150)\n ```\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\n3. **Get current commit SHA**: Run `git rev-parse HEAD` in the terminal. Store the result as `commit_sha`.\n\n4. **Get current branch**: Run `git branch --show-current` in the terminal. Store the result as `current_branch`.\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Resolve CI Checks\n\n1. **Initial resolution**: Call the `resolve_ci_checks` MCP tool immediately with `commit_ref` set to `commit_sha`. Do NOT wait before calling — the cache state is unknown until the first call returns.\n\n2. **Handle the response**:\n - If the response contains `source: \"cached\"`: The checks were already resolved. Skip the wait and proceed to evaluate the check list.\n - If the response contains `source: \"new\"` and the check list is empty: CI checks have not registered yet. Run `sleep 45` in the terminal to wait for checks to appear, then call `resolve_ci_checks` again with `commit_ref` set to `commit_sha` and `force_rerun` set to `true`.\n - If the response contains `source: \"resolved\"` on the first call: Present the resolved checks to the user for approval before proceeding.\n\n3. **Evaluate the check list**:\n - If the response contains `available: false`: Warn that CI check resolution is not available and skip to Stage 3.\n - If the check list is empty or all checks have `detail_level: \"none\"`: Warn that no actionable CI checks were found and skip to Stage 3.\n - Otherwise: Store the resolved checks and proceed to Stage 2.\n\nThis stage is **non-critical** — warn on failure or empty results, skip to Stage 3.\n\n## Stage 2 — Poll CI Checks + Correction Loop\n\nInitialize `retry_count = 0` and `max_retries = 2`.\n\n1. **Polling loop**: Poll CI check status by running `sleep 30` in the terminal between each call to `poll_ci_checks` with `commit_ref` set to `commit_sha`. Continue polling until `all_complete` is `true` or 10 minutes have elapsed (approximately 20 poll cycles).\n\n2. **On poll completion — all checks passed**: If `all_passed` is `true`, log success and proceed to Stage 3.\n\n3. **On poll completion — failures detected**: Examine each failed check's `detail_level`:\n\n - **`detail_level: \"full\"`** — Apply confidence gating:\n - Read the full `failure_details` payload for all failed checks.\n - If you are confident you can fix the errors, address ALL failures across all affected files in a single batch. Do not fix one at a time.\n - After applying all fixes, perform a single `git commit` and `git push`.\n - Increment `retry_count`.\n - If `retry_count` exceeds `max_retries`, stop the correction loop and proceed to Stage 3 with a warning.\n - Otherwise, update `commit_sha` to the new HEAD (`git rev-parse HEAD`) and restart the polling loop.\n - **`detail_level: \"url_only\"`** — Report the check name and URL to the user. Do not attempt fixes. Do not consume a retry.\n - **`detail_level: \"none\"`** — Report the check name only. Do not attempt fixes. Do not consume a retry.\n\n4. **Handle `unknown_checks`**: If the poll response contains `unknown_checks`, call `resolve_ci_checks` with `commit_ref` set to `commit_sha` and `force_rerun` set to `true` at most ONCE. If `unknown_checks` persist on the next poll, warn the user that the CI check configuration is unresolvable and skip to Stage 3.\n\n5. **Timeout**: If 10 minutes elapse without `all_complete` becoming `true`, warn that polling timed out and proceed to Stage 3.\n\nThis stage is **non-critical** — warn on failure or timeout, continue to Stage 3 regardless.\n\n## Stage 3 — Summary Report\n\nDisplay a structured completion report:\n\n```\n## CI Check Report\n\n**Ticket**: <ticket_key>\n**Branch**: <current_branch>\n**Commit SHA**: <commit_sha>\n**Status**: <Passed / Failed / Timed Out / Not Available>\n\n**Per-check breakdown**:\n| Check Name | Status | Detail Level |\n|------------|--------|--------------|\n| <name> | <pass/fail> | <full/url_only/none> |\n\n**Fixes attempted**: <retry_count> of <max_retries>\n\n**Warnings**:\n<If any non-critical stages had warnings (Stage 1: resolution unavailable,\nStage 2: timeout, unfixable failures, unknown_checks),\nlist them here. If no warnings, omit this section.>\n```\n\nThis stage is **non-critical** — display the report regardless.\n\n## Final Report\n\nOn success, display the structured report from Stage 3 confirming the CI check status, including per-check breakdown, fixes attempted, and any warnings from earlier stages.\n\nOn failure at any critical stage (Stage 0), display which stage failed and the error details.\n",
|
|
5
|
+
"clarify-ticket.md": "Generate clarifying questions for a Jira ticket and save them locally.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\n`$ARGUMENTS` is a required Jira ticket key (e.g., `PROJ-123`). This command generates clarifying questions for the ticket and saves them locally.\n\nIf any step fails, stop immediately and report which step failed and why.\n\n## Step 1 — Parse Arguments\n\n1. **Parse `$ARGUMENTS`**: Extract a required `ticket_key`, an optional `--second-opinion` flag, and an optional `--provider` flag.\n - Split `$ARGUMENTS` on whitespace.\n - If `--second-opinion` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `second_opinion_value`.\n - If `--second-opinion` appears without a provider name following it (or is the last token), set `second_opinion_value = \"auto\"`.\n - If `--second-opinion` is absent, set `second_opinion_value = null`.\n - If `--provider` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `provider_value`.\n - If `--provider` appears without a valid provider name following it (or is the last token), stop immediately and report: \"Usage error: --provider requires a provider name (openai, anthropic, or gemini).\"\n - If `--provider` is absent, set `provider_value = null`.\n - If both `--second-opinion` and `--provider` are present, `--second-opinion` takes precedence (set `provider_value = null`).\n - The remaining token (after removing flags and their arguments) is the `ticket_key`.\n\n2. **Validate ticket key format**: Confirm the ticket key matches the Jira key pattern `[A-Z][A-Z0-9]+-\\d+`. If it does not match, stop immediately and report: \"Invalid ticket key format. Expected a Jira key like PROJ-123.\"\n\n## Step 2 — Connectivity Check\n\nCall the `ping` MCP tool (no parameters). If the ping fails or returns an error, stop and show:\n\n```\nConnectivity check failed. Please verify:\n- Check that the Bridge API MCP server is configured in your editor's MCP settings\n- Check that BAPI_BASE_URL is set and the server is reachable\n- Check that BAPI_API_KEY is valid\n- Check that BAPI_REPO_NAME matches a configured repository\n```\n\n## Step 3 — Resolve Docs Directory\n\nCall the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n## Step 4 — Generate Clarifying Questions\n\nCall the `request_clarifying_questions` MCP tool with:\n- `ticket_number`: the validated `ticket_key`\n- `wait_for_result`: `true`\n- `save_locally`: `true`\n- `second_opinion`: set to `second_opinion_value` if non-null; omit entirely if null\n- `provider`: set to `provider_value` if non-null; omit entirely if null\n\nIf the tool returns an error, stop immediately and report: \"Clarifying questions generation failed.\" Include the error details.\n\n## Final Report\n\nOn successful completion, display:\n\n> **Ticket Key**: {ticket_key}\n>\n> **Local File Path**: {docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md\n>\n> **Status**: The clarifying questions document has been saved locally. No changes were pushed to Jira.\n>\n> To incorporate these findings into the Jira ticket description, run: `/update-ticket {ticket_key}`\n",
|
|
6
|
+
"code-ticket.md": "# Code Ticket: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes a Jira ticket key (e.g., `BAPI-150`), downloads the AI-generated implementation plan and clarifying questions via MCP tools, then executes the plan step by step directly in the main conversation so all progress is visible.\n\nIf any critical stage fails (Stage 0, Stage 1, or Stage 3), stop immediately and report which stage failed and why. Non-critical stages (Stage 2) should log a warning but not stop the pipeline.\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline to implement a Jira ticket using an AI-generated plan. Execute all stages in sequence.\n\n## Stage 0 — Setup and Argument Parsing\n\n1. **Parse `$ARGUMENTS`**: Extract a single required `ticket_key` argument. The expected format is a Jira ticket key such as `BAPI-150` or `PROJ-123` — one or more uppercase letters, a hyphen, and one or more digits (regex: `[A-Z]+-\\d+`). If `$ARGUMENTS` is empty or the value does not match the expected format, stop immediately and display:\n\n ```\n Invalid ticket key format: '<value>'. Expected format: PROJ-123 (uppercase letters, hyphen, digits).\n Usage: /code-ticket <ticket_key> (e.g., /code-ticket BAPI-150)\n ```\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Download Implementation Plan\n\nCall the `get_plan` MCP tool with:\n- `ticket_number`: the parsed `ticket_key` from Stage 0\n- `save_locally`: `true`\n\nInspect the response for errors. If the response text contains `NOT_FOUND` or `404` or indicates the plan was not found, stop immediately and display:\n\n```\nNo implementation plan found for <ticket_key>. Run `/plan-ticket <ticket_key>` first to generate one,\nor use the `request_plan_generation` MCP tool with `wait_for_result: true`.\n```\n\nOn success, read and internalize the full plan content. This is the plan you will execute in Stage 3.\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 2.\n\n## Stage 2 — Download Clarifying Questions\n\nCall the `get_clarifying_questions` MCP tool with:\n- `ticket_number`: the parsed `ticket_key` from Stage 0\n- `save_locally`: `false`\n\nIf the response contains `NOT_FOUND` or `404` or indicates no clarifying questions were found, log a warning note:\n\n```\nWarning: No clarifying questions found for <ticket_key>. Proceeding without supplementary context.\n```\n\nDo **NOT** stop the pipeline. Clarifying questions are supplementary context, not a hard prerequisite for implementation.\n\nOn success, internalize the clarifying questions content. Reference these for additional context where relevant to implementation steps — the answers provide supplementary guidance on requirements and technical decisions.\n\nThis stage is **non-critical** — warn on failure, continue to Stage 3 regardless.\n\n## Stage 3 — Execute Implementation Plan\n\nExecute the implementation plan step by step, directly in this conversation. Work inline so the user can see all progress and approve tool calls.\n\nFollow these rules:\n\n1. **Execute the plan in order.** Do not skip any steps, especially review steps involving test execution, lint checks, and architectural verification.\n2. **Make code changes** as directed by each step in the plan.\n3. **Run tests and checks** as specified in the plan's review steps.\n4. **Do NOT run `git commit` or `git push`.** Leave all changes uncommitted for developer review.\n5. **If a step is ambiguous or blocked**, note the issue clearly and continue with the next step rather than halting entirely.\n6. **Reference clarifying questions** (if retrieved in Stage 2) when they provide relevant context for a given step.\n\nThis stage is **critical** — if a blocking error prevents further progress, stop and report the failure.\n\n## Stage 4 — Final Summary Report\n\nDisplay a structured report after all stages complete:\n\n```\n## Implementation Complete\n\n**Ticket**: <ticket_key>\n\n**Developer Action Items**:\n- All changes are uncommitted. Review the changes with `git diff` before committing.\n- Run the project's test suite to verify nothing is broken before committing.\n\n**Warnings**:\n<If any non-critical stages had warnings (Stage 2: no clarifying questions),\nlist them here so the developer has full visibility. If no warnings, omit this section.>\n```\n\n## Final Report\n\nOn success, display the structured report from Stage 4 confirming that implementation of the ticket is complete.\n\nOn failure at any critical stage (Stage 0, Stage 1, or Stage 3), display which stage failed and the error details.\n",
|
|
7
|
+
"commit-ticket.md": "# Commit Ticket: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes a Jira ticket key (e.g., `BAPI-150`), verifies the current git branch matches the ticket, identifies implementation files by cross-referencing git changes against the saved implementation plan, and commits and pushes the work. It is designed to run after `/code-ticket` completes.\n\nIf any critical stage fails (Stage 0, Stage 1, or Stage 3), stop immediately and report which stage failed and why. Non-critical stages (Stage 2, Stage 4, Stage 5, and Stage 6) should log a warning but not stop the pipeline.\n\n---\n\n# Instructions\n\nYou are executing a 7-stage pipeline to commit and push implementation work for a Jira ticket. Execute all stages in sequence.\n\n## Stage 0 — Setup and Argument Parsing\n\n1. **Parse `$ARGUMENTS`**: Extract a single required `ticket_key` argument. The expected format is a Jira ticket key such as `BAPI-150` or `PROJ-123` — one or more uppercase letters, a hyphen, and one or more digits (regex: `[A-Z]+-\\d+`). If `$ARGUMENTS` is empty or the value does not match the expected format, stop immediately and display:\n\n ```\n Invalid ticket key format: '<value>'. Expected format: PROJ-123 (uppercase letters, hyphen, digits).\n Usage: /commit-ticket <ticket_key> (e.g., /commit-ticket BAPI-150)\n ```\n\n2. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n3. **Verify uncommitted changes exist**: Run `git status --porcelain` in the terminal. If the output is empty (no modified, added, or untracked files), stop immediately and display:\n\n ```\n No uncommitted changes found. Nothing to commit for <ticket_key>.\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Branch Verification and Creation\n\n1. **Get current branch**: Run `git branch --show-current` in the terminal. Store the result as `current_branch`.\n\n2. **Check branch match**: Determine if `current_branch` contains the `ticket_key` (case-insensitive comparison). For example, if the ticket key is `BAPI-150`, branch `feature/BAPI-150-add-caching` matches, as does `feature/BAPI-150` or `bugfix/bapi-150-fix`.\n\n3. **If the branch matches**: Log a confirmation message and proceed:\n\n ```\n Branch '<current_branch>' matches ticket <ticket_key>. Proceeding.\n ```\n\n4. **If the branch does NOT match**: Create a new branch from the current HEAD in the format `feature/<ticket_key>` (e.g., `feature/BAPI-150`). Run `git checkout -b feature/<ticket_key>` in the terminal. If the branch creation fails (e.g., branch already exists), try `git checkout feature/<ticket_key>` instead. If both fail, stop immediately and display:\n\n ```\n Failed to create or switch to branch 'feature/<ticket_key>'.\n Please resolve the branch situation manually and re-run.\n ```\n\n On success, log:\n\n ```\n Created and switched to new branch 'feature/<ticket_key>'.\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 2.\n\n## Stage 2 — Identify and Stage Implementation Files\n\n1. **Collect git changes**: Run `git status --porcelain` in the terminal. Parse the output to build two lists:\n - `modified_files`: files with status `M`, `MM`, `AM`, or `A` (modified or staged)\n - `untracked_files`: files with status `??` (new untracked files)\n\n Combine into a single list `all_changed_files`.\n\n2. **Load the implementation plan**: Look for the implementation plan file at `{docs_dir}/plans/{ticket_key}-plan.md`. Read the file.\n\n - **If the plan file exists**: Extract file paths mentioned in the plan. Look for patterns like backtick-quoted paths (e.g., `src/python/foo.py`), file references in step descriptions, and any explicit file listings. Build a list `plan_files` of all file paths referenced in the plan.\n\n - **If the plan file does NOT exist**: Log a warning:\n\n ```\n Warning: No implementation plan found at {docs_dir}/plans/{ticket_key}-plan.md.\n Cannot cross-reference changes against plan. Will present all changed files for review.\n ```\n\n Set `plan_files` to an empty list.\n\n3. **Classify changed files**: For each file in `all_changed_files`, classify it into one of three categories:\n\n - **Plan-matched**: The file path appears in `plan_files` (exact match or the plan references a parent directory). These are high-confidence implementation files.\n - **Likely related**: The file is not explicitly in the plan but is a test file for a plan-matched file, a migration file, an `__init__.py` in a directory with plan-matched files, or otherwise clearly related to the implementation (e.g., `requirements.txt` if the plan mentions adding a dependency).\n - **Ambiguous**: The file does not appear related to the plan. These may be pre-existing uncommitted changes.\n\n4. **Present file list for user confirmation**: Display the classified file list to the user:\n\n ```\n ## Files to Commit for <ticket_key>\n\n ### Plan-matched files (high confidence):\n - path/to/file1.py\n - path/to/file2.py\n\n ### Likely related files:\n - tests/pytest/routes/test_file1.py\n - db/alembic/versions/xxxx_migration.py\n\n ### Ambiguous files (not referenced in plan):\n - some/other/file.py\n\n Shall I proceed with committing all listed files?\n If you want to exclude any files, please specify which ones to remove.\n ```\n\n If `plan_files` is empty (plan not found), display all files under a single \"All changed files\" heading instead.\n\n5. **Wait for user confirmation**: The user may:\n - Approve all files (proceed)\n - Specify files to exclude (remove those from the commit list)\n - Cancel entirely (stop the pipeline)\n\n If the user cancels, stop immediately and display:\n\n ```\n Commit cancelled by user. No files were staged or committed.\n ```\n\n6. **Stage the approved files**: Run `git add <file1> <file2> ...` in the terminal, listing only the approved files explicitly. Do NOT use `git add -A` or `git add .`.\n\nThis stage is **non-critical** if the plan file is not found (warn and continue with all files). It is **critical** if the user cancels or if `git add` fails — stop immediately on those failures.\n\n## Stage 3 — Commit and Push\n\n1. **Generate commit message**: Based on the staged files and the implementation plan (if available), generate a concise commit message. The message must:\n - Start with a brief summary line (under 72 characters) that references the ticket key\n - Format: `<ticket_key>: <brief description of changes>`\n - Example: `BAPI-150: Add rate limiting to LLM client`\n - If the plan was available, derive the description from the plan's title or objective\n - If the plan was not available, summarize based on the file names and `git diff --staged` output\n\n2. **Commit**: Run `git commit -m \"<message>\"` in the terminal. If the commit fails due to a pre-commit hook, report the hook output and stop:\n\n ```\n Commit failed due to pre-commit hook. Hook output:\n <hook output>\n\n Please fix the issues and re-run /commit-ticket <ticket_key>.\n ```\n\n3. **Push to remote**: Run `git push -u origin <current_branch>` in the terminal. The `-u` flag sets up upstream tracking. If the push fails, stop immediately and display:\n\n ```\n Push failed. Error:\n <error output>\n\n The commit was created locally. You can push manually with:\n git push -u origin <current_branch>\n ```\n\nThis stage is **critical** — stop immediately on failure.\n\n## Stage 4 — Final Summary Report\n\nDisplay a structured report after all stages complete:\n\n```\n## Commit Complete\n\n**Ticket**: <ticket_key>\n**Branch**: <current_branch>\n**Commit**: <commit_hash> (from `git rev-parse --short HEAD`)\n**Files committed**: <count> files\n**Remote**: Pushed to origin/<current_branch>\n\n**Committed files**:\n- path/to/file1.py\n- path/to/file2.py\n- ...\n\n**Warnings**:\n<If any non-critical warnings occurred (Stage 2: plan not found),\nlist them here. If no warnings, omit this section.>\n```\n\nThis stage is **non-critical** — display the report regardless.\n\n## Stage 5 — Jira Status Transition\n\nThis stage attempts to transition the Jira ticket to the appropriate post-PR status.\n\n1. **Resolve target status**: Call the `resolve_target_status` MCP tool with `ticket_number` set to the `ticket_key`. This returns the cached or LLM-resolved target status for the project.\n\n2. **Attempt transition**: If `resolve_target_status` returned a non-null `target_status`, call the `update_jira_status` MCP tool with `ticket_number` set to the `ticket_key` and `target_status` set to the resolved value. If the ticket is already in the target status, this is a no-op.\n\n3. **On success**: Display `\"Ticket status updated: <from_status> -> <to_status>\"`.\n\n4. **On failure or not applicable**: Display a warning but do not stop the pipeline:\n - If `resolve_target_status` returned null: `\"Ticket status transition skipped: no target status configured for this project\"`\n - If `update_jira_status` failed: `\"Ticket status transition skipped: <error message>\"`\n\nThis stage is **non-critical** — log a warning on failure but do not stop the pipeline.\n\n## Stage 6 — Smoke Test Validation Comment\n\nThis stage reviews the implementation against the ticket requirements and posts a comment if manual validation is needed.\n\n1. **Fetch ticket description**: Call the `get_ticket` MCP tool with `ticket_number` set to the `ticket_key` to retrieve the current ticket requirements.\n\n2. **Review implementation**: Compare the implementation (from the plan loaded in Stage 2 and the files committed in Stage 3) against the ticket requirements. Identify any behavior or requirements that could NOT be validated through the automated tests written during implementation or through code review alone. Consider the limitations of any tests that were written: what functionality or behavior could not be validated by those tests? Examples include: requirements involving visual UI rendering or layout checks, third-party system integrations where mock tests are insufficient, or non-deterministic behaviors.\n\n3. **If untestable requirements exist**: Compose a structured comment describing specific manual validation steps stakeholders should perform. Then call the `add_comment` MCP tool with `ticket_number` set to the `ticket_key` and the comment text. Display: `\"Smoke test validation comment posted to <ticket_key>\"`.\n\n4. **If no untestable requirements exist**: Skip silently. Display: `\"No untestable requirements identified — skipping smoke test comment\"`.\n\nThis stage is **non-critical** — log a warning on failure but do not stop the pipeline.\n\n## Final Report\n\nOn success, display the structured report from Stage 4 confirming that the commit and push are complete, including the branch name, commit hash, file list, and any warnings from earlier stages.\n\nOn failure at any critical stage (Stage 0, Stage 1, or Stage 3), display which stage failed and the error details.\n",
|
|
8
|
+
"create-pr.md": "# Create PR: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes a Jira ticket key (e.g., `BAPI-150`), fetches the ticket summary, determines the base branch, and creates a pull request on the configured VCS provider. It is designed to run after `/commit-ticket` completes.\n\nIf any critical stage fails (Stage 0), stop immediately and report which stage failed and why. Non-critical stages (Stage 1 and Stage 2) should log a warning but not stop the pipeline.\n\n---\n\n# Instructions\n\nYou are executing a 3-stage pipeline to create a pull request for a Jira ticket. Execute all stages in sequence.\n\n## Stage 0 — Setup and Argument Parsing\n\n1. **Parse `$ARGUMENTS`**: Extract a single required `ticket_key` argument. The expected format is a Jira ticket key such as `BAPI-150` or `PROJ-123` — one or more uppercase letters, a hyphen, and one or more digits (regex: `[A-Z]+-\\d+`). If `$ARGUMENTS` is empty or the value does not match the expected format, stop immediately and display:\n\n ```\n Invalid ticket key format: '<value>'. Expected format: PROJ-123 (uppercase letters, hyphen, digits).\n Usage: /create-pr <ticket_key> (e.g., /create-pr BAPI-150)\n ```\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\n3. **Get current branch**: Run `git branch --show-current` in the terminal. Store the result as `head_branch`. Verify that `head_branch` contains the `ticket_key` (case-insensitive comparison). If the branch does not contain the ticket key, stop immediately and display:\n\n ```\n Current branch '<head_branch>' does not contain ticket key <ticket_key>.\n Please switch to the correct feature branch before running /create-pr.\n ```\n\n4. **Resolve base branch**: Call the `get_config_field` MCP tool with `field_name` set to `base_branch`. If the tool returns null, or an HTTP 400 Validation Error / Invalid field name, treat it as not set and fallback to `main`. Store the resolved value as `base_branch`.\n\n5. **Fetch ticket summary**: Call the `get_ticket` MCP tool with `ticket_number` set to the parsed `ticket_key`. Extract the ticket summary from the response. If the tool returns an error, log a warning and use a generic summary based on the ticket key.\n\n6. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Create Pull Request\n\n1. **Compose PR title**: Format the title as `<ticket_key>: <ticket_summary>`. Truncate to 72 characters if needed.\n\n2. **Compose PR body**: Build a PR body that includes:\n - A brief description derived from the ticket summary\n - A plain text reference to the local implementation plan: `Implementation Plan available locally at {docs_dir}/plans/{ticket_key}-plan.md` (do not use markdown hyperlink syntax — the local path is sufficient for team members pulling the branch)\n\n3. **Create the pull request**: Call the `create_pull_request` MCP tool with:\n - `head_branch`: the current branch from Stage 0\n - `base_branch`: the resolved base branch from Stage 0\n - `title`: the composed PR title\n - `body`: the composed PR body\n\n4. **Handle the response with graceful degradation**:\n - If the response contains `available: false`: Report the reason to the user and skip to Stage 2. Do not halt the pipeline.\n - If the response contains `created: false`: Log \"PR already exists\" and store the returned PR URL. Continue to Stage 2.\n - If the response contains `created: true`: Store the PR URL. Continue to Stage 2.\n - If an HTTP error occurs: Warn the user with the error details and continue to Stage 2. Do not halt the pipeline.\n\nThis stage is **non-critical** — warn on failure, continue to Stage 2 regardless.\n\n## Stage 2 — Summary Report\n\nDisplay a structured report after all stages complete:\n\n```\n## Pull Request Report\n\n**Ticket**: <ticket_key>\n**Branch**: <head_branch>\n**Base Branch**: <base_branch>\n**PR URL**: <pr_url or \"N/A — see warnings\">\n\n**Warnings**:\n<If any non-critical stages had warnings (Stage 1: PR creation failed or unavailable),\nlist them here. If no warnings, omit this section.>\n```\n\nThis stage is **non-critical** — display the report regardless.\n\n## Final Report\n\nOn success, display the structured report from Stage 2 confirming that the pull request was created (or already existed), including the branch name, base branch, PR URL, and any warnings from earlier stages.\n\nOn failure at any critical stage (Stage 0), display which stage failed and the error details.\n",
|
|
9
|
+
"critique-ticket.md": "Generate a ticket quality critique and save it locally.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command triggers an AI-powered critique of a Jira ticket and saves the result locally. **No human confirmation gates** — the command runs end-to-end without pausing. `$ARGUMENTS` should contain a single Jira ticket key in `PROJECT-NUMBER` format (e.g., `BAPI-123`).\n\nIf any step fails, stop immediately and report which step failed and why.\n\n## Step 1 — Parse Arguments\n\n1. **Parse `$ARGUMENTS`**: Extract a required `ticket_key`, an optional `--second-opinion` flag, and an optional `--provider` flag.\n - Split `$ARGUMENTS` on whitespace.\n - If `--second-opinion` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `second_opinion_value`.\n - If `--second-opinion` appears without a provider name following it (or is the last token), set `second_opinion_value = \"auto\"`.\n - If `--second-opinion` is absent, set `second_opinion_value = null`.\n - If `--provider` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `provider_value`.\n - If `--provider` appears without a valid provider name following it (or is the last token), stop immediately and report: \"Usage error: --provider requires a provider name (openai, anthropic, or gemini).\"\n - If `--provider` is absent, set `provider_value = null`.\n - If both `--second-opinion` and `--provider` are present, `--second-opinion` takes precedence (set `provider_value = null`).\n - The remaining token (after removing flags and their arguments) is the `ticket_key`.\n\n2. **Validate the ticket key format**: Validate that `ticket_key` matches the regex pattern `^[A-Za-z][A-Za-z0-9]+-\\d+$`. If validation fails, stop immediately and report: \"The argument does not match the expected `PROJECT-NUMBER` format. Example: `BAPI-123`.\"\n\n## Step 2 — Connectivity Check\n\nCall the `ping` MCP tool (no parameters). If it fails, stop and show:\n\n```\nConnectivity check failed. Please verify:\n- Check that the Bridge API MCP server is configured in your editor's MCP settings\n- Check that BAPI_BASE_URL is set and the server is reachable\n- Check that BAPI_API_KEY is valid\n- Check that BAPI_REPO_NAME matches a configured repository\n```\n\n## Step 3 — Resolve Docs Directory\n\nCall the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n## Step 4 — Generate Critique\n\nCall the `request_ticket_critique` MCP tool with:\n- `ticket_number`: the validated `ticket_key`\n- `wait_for_result`: `true`\n- `save_locally`: `true`\n- `second_opinion`: set to `second_opinion_value` if non-null; omit entirely if null\n- `provider`: set to `provider_value` if non-null; omit entirely if null\n\nIf the tool returns an error, stop immediately and report: \"Critique generation failed.\" Include the error details.\n\n## Final Report\n\n**On success**, display a summary including:\n\n- Path to the saved critique document: `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md`\n\nNote: The critique was NOT pushed to Jira. To incorporate the critique findings into the Jira ticket description, run: `/update-ticket {ticket_key}`\n\n**On failure at any step**, stop immediately and display the step that failed and the error details.\n",
|
|
10
|
+
"explore-ticket.md": "Explore the codebase for a task and recommend implementation options or surface clarifying questions.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\n`$ARGUMENTS` is a free-form prompt describing a task you want to accomplish and your goals for it. This is **not** a Jira ticket key — it is plain text describing the work.\n\nExecute all exploration and analysis directly in the main conversation. The user should see exploration progress as it happens.\n\nIf any critical stage fails, stop immediately and report which stage failed and why.\n\n## Stage 0 — Setup and Connectivity\n\n1. **Parse prompt**: Extract the prompt text from `$ARGUMENTS`. Trim any surrounding whitespace. If the prompt is empty or whitespace-only, stop immediately and display: `Usage: /explore-ticket <prompt describing your task and goals>`\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or returns an error, stop and show:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that `BAPI_BASE_URL` is set and the server is reachable\n - Check that `BAPI_API_KEY` is valid\n - Check that `BAPI_REPO_NAME` matches a configured repository\n\n3. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n4. **Generate output filename**: Create a kebab-case slug from the prompt — take the first 6-8 meaningful words, strip non-alphanumeric characters, lowercase, and truncate to 60 characters. The output file path will be `{docs_dir}/explorations/{slug}.md`. If a file with that name already exists, append a short timestamp suffix (e.g., `-1710000000`).\n\n5. **Initialize tracking**: Prepare to track `key_files_examined` (list of files read during exploration), `web_searches` (list of topics searched), and `research_queries` (list of deep research queries).\n\nIf this stage fails, stop immediately and report the error. Do not proceed to Stage 1.\n\n## Stage 1 — Codebase Exploration\n\nThis is the core discovery stage. Take your time — thorough exploration is more valuable than speed.\n\n1. **Analyze the prompt** to identify which areas of the codebase are relevant: route files, agent flows, database models, library utilities, LLM integration, MCP server, tests, etc.\n\n2. **Search for files** matching patterns related to the task (e.g., `api/routes/**/*.py`, `src/python/llms/agents/**/*.py`, `db/models/*.py`).\n\n3. **Search for content** — relevant function names, class names, patterns, and keywords across the codebase.\n\n4. **Read the most relevant files** in detail — understand existing implementations, conventions, and patterns that relate to the task.\n\n5. **Build a mental model** of:\n - What exists today that relates to the task\n - What patterns and conventions are used in similar features\n - What dependencies, data flows, and integration points are involved\n - What gaps or unknowns remain that need external research\n\n6. **Track all significant files** examined in `key_files_examined`.\n\nDo not rush this stage. When in doubt, read more code rather than less. Continue exploring until you have a solid understanding of the relevant code.\n\nThis stage is non-blocking — always proceed to Stage 2 regardless of what you find, since the exploration informs what research is needed.\n\n## Stage 2 — Research Unknowns\n\nBased on gaps identified in Stage 1, decide what research is needed. Apply these decision rules:\n\n- **No research needed**: The codebase exploration answered all questions. Skip directly to Stage 3.\n- **Web search**: For quick factual lookups — library API signatures, configuration syntax, small \"how to\" questions. Examples: \"FastAPI dependency injection with custom headers\", \"Alembic batch migration syntax\". Do web searches inline and capture relevant findings.\n- **Deep research** (via `request_deep_research` MCP tool): For large, multi-faceted unknowns that require synthesizing information from multiple sources. Examples: \"Best practices for implementing WebSocket connection pooling in Python asyncio\", \"Tradeoffs between different approaches to real-time notification delivery in FastAPI applications\". Only use deep research when the question genuinely needs a multi-source investigation.\n\n**If deep research is needed:**\n\n1. Call `request_deep_research` with `wait_for_result` set to `true`, `save_locally` set to `true`, a descriptive `query`, and `context` describing the Bridge API tech stack and the specific task.\n2. If deep research fails, note the failure and fall back to web searches for the same topic. Do NOT halt the pipeline.\n\nTrack all research performed in `research_queries` and `web_searches`.\n\nThis stage is non-blocking — failures degrade the quality of analysis but do not stop the command. Log a warning for any failed research and continue.\n\n## Stage 3 — Analysis and Recommendation\n\nSynthesize everything from Stages 1 and 2 into a structured analysis:\n\n1. **Identify viable implementation options** — at least 2 when multiple approaches exist, or 1 if there is genuinely only one reasonable path.\n\n2. **For each option, evaluate:**\n - Implementation complexity and estimated effort\n - How well it follows existing codebase patterns and conventions\n - Risks, tradeoffs, and potential pitfalls\n - Files that would need to be created or modified\n\n3. **Decide whether to recommend or ask questions:**\n - **Recommend** if one option is clearly superior, or if the tradeoffs are well-understood and the choice is primarily technical.\n - **Ask clarifying questions** if there are significant unknowns about goals, business requirements, or constraints that would change the recommendation. For each question, explain why the answer matters and how it would affect the choice between options.\n - **When in doubt, ask rather than guess** — this command prioritizes thorough discovery over premature commitment.\n\nThis stage is inline analysis — no tool calls required. This stage is non-blocking — always proceed to Stage 4.\n\n## Stage 4 — Write Output\n\n1. Create the `explorations/` directory under `docs_dir` if it does not exist.\n\n2. Write the exploration document to the slug-based path determined in Stage 0 (`{docs_dir}/explorations/{slug}.md`) with this structure:\n\n```markdown\n# Exploration: {concise summary of the prompt}\n\n**Date**: {current date}\n**Prompt**: {original prompt text}\n\n## Context\n\n{Brief description of the task and what areas of the codebase are relevant.}\n\n## Codebase Findings\n\n{Key discoveries from Stage 1. What exists today, what patterns are used, what the relevant code paths look like. Reference specific files and functions with file_path:line_number format.}\n\n## Research Findings\n\n{Findings from web searches and deep research, if any. If no research was performed, state \"No external research was needed.\"}\n\n## Implementation Options\n\n### Option A: {name}\n\n{Description, approach, affected files, pros, cons.}\n\n### Option B: {name}\n\n{Description, approach, affected files, pros, cons.}\n\n## Recommendation\n\n{If recommending: State which option and why. Mention any caveats or risks.}\n\n{If asking questions: State \"The following questions need to be answered before a confident recommendation can be made:\" followed by numbered questions. For each question, explain why it matters and how the answer would affect the recommendation.}\n\n## Key Files\n\n{Bulleted list of the most important files examined, with one-line descriptions of their relevance.}\n```\n\nIf the file cannot be written, stop immediately and report the failure.\n\n## Final Report\n\nOn successful completion of all stages, display:\n\n> **Exploration Complete**\n>\n> **Prompt**: {first 80 characters of prompt}...\n> **Output**: {full path to the output file}\n> **Files Examined**: {count of key_files_examined}\n> **Research**: {count of web_searches} web searches, {count of research_queries} deep research queries\n>\n> **Result**: {\"Recommendation provided\" | \"Clarifying questions raised — N questions need answers\"}\n\nOn failure at any stage, stop immediately and report:\n- Which stage failed (by number and name)\n- The error details\n- Any partial results that were produced before the failure\n",
|
|
11
|
+
"implement-ticket.md": "# Implement Ticket\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n1. Parse `$ARGUMENTS` to extract a single required `ticket_key` (e.g., `BAPI-123`). If `$ARGUMENTS` is empty or does not match the Jira key pattern (`[A-Z][A-Z0-9]+-\\d+`), stop immediately and display:\n ```\n Invalid ticket key format. Expected: PROJ-123\n Usage: /implement-ticket <ticket_key>\n ```\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"implement-ticket\"`\n - `variables`: `{ \"ticket_key\": \"<ticket_key>\" }`\n\n If the tool returns an error, stop and report the failure.\n\n3. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n4. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Ticket**: <ticket_key>\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
12
|
+
"learn-repository.md": "Learn and document all configuration fields for the repository by running parallel research agents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n1. This command takes no arguments.\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"learn-repository\"`\n\n If the tool returns an error, stop and report the failure.\n\n3. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n4. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
13
|
+
"parse-repository.md": "Queue a background job to parse and index the repository for Bridge API's AI agents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nExecute all steps in this command as a simple linear sequence of MCP tool calls.\n\n## Step 1 — Parse Arguments\n\nParse `$ARGUMENTS` for an optional `directory_path` argument (a subdirectory path to scope the parse to, e.g., `src/python`). If no argument is provided, the entire repository will be parsed. If `$ARGUMENTS` is provided but invalid (e.g., contains special characters that suggest it's not a path), report an error.\n\n## Step 2 — Connectivity Check\n\nCall the `ping` MCP tool (no parameters). If the ping fails, stop immediately and display:\n\n```\nConnectivity check failed. Please verify:\n- Check that the Bridge API MCP server is configured in your editor's MCP settings\n- Check that BAPI_BASE_URL is set and the server is reachable\n- Check that BAPI_API_KEY is valid\n- Check that BAPI_REPO_NAME matches a configured repository\n```\n\n## Step 3 — Queue Parse Job\n\nCall the `parse_repository` MCP tool with:\n- `directory_path`: set to the parsed `directory_path` from Step 1 if provided, otherwise omit the parameter\n\nIf the response indicates parsing is already in progress, display:\n\n```\nRepository parsing is already in progress. A previous parse job has not yet completed.\n\nRun `/check-parse-status` to monitor progress, or wait a few minutes and try again.\n```\n\nStop and do not proceed to the summary.\n\nIf the call fails or returns an error, stop immediately and display:\n\n```\nFailed to queue parse job: <error message from the tool>\n```\n\n## Summary\n\nOn successful queuing, display:\n\n```\nRepository parse job queued successfully.\n\nScope: <entire repository or directory_path if provided>\n\nProcessing typically takes several minutes for large repositories.\nRun `/check-parse-status` to monitor progress.\n```\n\nAfter the parse completes, AI-generated plans and clarifying questions will reflect the latest code changes.\n",
|
|
14
|
+
"plan-ticket.md": "Generate an implementation plan for a Jira ticket, wait for the result, and save it locally.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nExecute all steps in this command as a simple linear sequence of MCP tool calls.\n\n## Step 1 — Parse Arguments\n\n1. **Parse `$ARGUMENTS`**: Extract a required `ticket_key`, an optional `--second-opinion` flag, and an optional `--provider` flag.\n - Split `$ARGUMENTS` on whitespace.\n - If `--second-opinion` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `second_opinion_value`.\n - If `--second-opinion` appears without a provider name following it (or is the last token), set `second_opinion_value = \"auto\"`.\n - If `--second-opinion` is absent, set `second_opinion_value = null`.\n - If `--provider` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `provider_value`.\n - If `--provider` appears without a valid provider name following it (or is the last token), stop immediately and report: \"Usage error: --provider requires a provider name (openai, anthropic, or gemini).\"\n - If `--provider` is absent, set `provider_value = null`.\n - If both `--second-opinion` and `--provider` are present, `--second-opinion` takes precedence (set `provider_value = null`).\n - The remaining token (after removing flags and their arguments) is the `ticket_key`.\n - If `ticket_key` is empty or missing, stop immediately and display:\n\n ```\n Usage: /plan-ticket <ticket_key> [--second-opinion [provider]] [--provider <name>] (e.g., /plan-ticket BAPI-150)\n ```\n\n## Step 2 — Connectivity Check\n\nCall the `ping` MCP tool (no parameters). If the ping fails, stop immediately and display:\n\n```\nConnectivity check failed. Please verify:\n- Check that the Bridge API MCP server is configured in your editor's MCP settings\n- Check that BAPI_BASE_URL is set and the server is reachable\n- Check that BAPI_API_KEY is valid\n- Check that BAPI_REPO_NAME matches a configured repository\n```\n\n## Step 3 — Resolve Docs Directory\n\nCall the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n## Step 4 — Generate Plan\n\nCall the `request_plan_generation` MCP tool with:\n- `ticket_number`: the parsed `ticket_key`\n- `wait_for_result`: `true`\n- `save_locally`: `true`\n- `second_opinion`: set to `second_opinion_value` if non-null; omit entirely if null\n- `provider`: set to `provider_value` if non-null; omit entirely if null\n\nThis step may take 1-5 minutes while the backend processes the plan.\n\nIf the tool returns an error, stop immediately and display:\n\n```\nPlan generation failed: <error message from the tool>\n```\n\n## Step 5 — Confirm Success\n\nDisplay a confirmation message:\n\n```\nPlan generated successfully for <ticket_key>\nSaved to: {docs_dir}/plans/<ticket_key>-plan.md\n```\n\n## Final Summary\n\nDisplay a summary block:\n\n```\n## Plan Generation Report\n\n- **Ticket**: <ticket_key>\n- **Plan Status**: Generated successfully\n- **Local File**: {docs_dir}/plans/<ticket_key>-plan.md\n```\n\nOn failure at any step, stop immediately, display which step failed and the error details, and do not proceed.\n",
|
|
15
|
+
"reimplement-ticket.md": "# Reimplement Ticket: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command retrieves the reimplement context for a previously-implemented Jira ticket via MCP, then implements follow-up changes inline. Use this for small follow-up requests on tickets that have already been through the plan+implement cycle.\n\nIf any critical stage fails (Stage 0 or Stage 1), stop immediately and report which stage failed and why.\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline to implement follow-up changes on a Jira ticket using assembled reimplement context. Execute all stages in sequence.\n\n## Stage 0 — Setup and Argument Parsing\n\n1. **Parse `$ARGUMENTS`**: Extract a required `ticket_key`, an optional `--second-opinion` flag, and an optional `--provider` flag.\n - Split `$ARGUMENTS` on whitespace.\n - If `--second-opinion` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `second_opinion_value`.\n - If `--second-opinion` appears without a provider name following it (or is the last token), set `second_opinion_value = \"auto\"`.\n - If `--second-opinion` is absent, set `second_opinion_value = null`.\n - If `--provider` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `provider_value`.\n - If `--provider` appears without a valid provider name following it (or is the last token), stop immediately and report: \"Usage error: --provider requires a provider name (openai, anthropic, or gemini).\"\n - If `--provider` is absent, set `provider_value = null`.\n - If both `--second-opinion` and `--provider` are present, `--second-opinion` takes precedence (set `provider_value = null`).\n - The remaining token (after removing flags and their arguments) is the `ticket_key`.\n - If `ticket_key` is empty or does not match the expected format (one or more uppercase letters, a hyphen, and one or more digits), stop immediately and display:\n\n ```\n Invalid ticket key format: '<value>'. Expected format: PROJ-123 (uppercase letters, hyphen, digits).\n Usage: /reimplement-ticket <ticket_key> [--second-opinion [provider]] [--provider <name>] (e.g., /reimplement-ticket BAPI-150)\n ```\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or does not return `\"status\": \"ok\"`, stop immediately and display:\n\n ```\n Connectivity check failed. Please verify:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that BAPI_BASE_URL is set and the server is reachable\n - Check that BAPI_API_KEY is valid\n - Check that BAPI_REPO_NAME matches a configured repository\n ```\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 1.\n\n## Stage 1 — Request and Retrieve Reimplement Context\n\nCall the `request_reimplement_context` MCP tool with:\n- `ticket_number`: the parsed `ticket_key` from Stage 0\n- `wait_for_result`: `true`\n- `save_locally`: `true`\n- `second_opinion`: set to `second_opinion_value` if it is non-null; omit the parameter entirely if `second_opinion_value` is null\n- `provider`: set to `provider_value` if it is non-null; omit the parameter entirely if `provider_value` is null\n\nIf the tool returns an error or 404 persists after polling, stop immediately and display:\n\n```\nFailed to retrieve reimplement context for <ticket_key>.\nThis may mean:\n- The ticket has not been previously processed by Bridge API\n- Background processing failed — check server logs\n- The ticket does not exist in Jira\n\nTry running /plan-ticket <ticket_key> first if this is a new ticket.\n```\n\nOn success, read and internalize the returned context markdown. This document contains:\n- A summary of changes (if applicable)\n- New/changed information since last processing (comments, description changes, attachments)\n- The original ticket description\n- The existing implementation plan (at the bottom, for reference only)\n\nThis stage is **critical** — stop immediately on failure. Do not proceed to Stage 2.\n\n## Stage 2 — Implement Follow-Up Changes\n\nExecute changes inline in this conversation. Work directly so the user can see all progress and approve tool calls.\n\nFollow these rules:\n\n1. **Focus on the new information.** The context document identifies what has changed since the last implementation. Focus your changes on addressing the new/changed requirements.\n2. **Reference the existing plan as supplementary guidance only.** The plan at the bottom of the context describes the original implementation, not the follow-up. Use it to understand the existing code structure, not as a step-by-step guide.\n3. **Make code changes** as directed by the new information.\n4. **Run tests and checks** to verify your changes don't break existing functionality.\n5. **Do NOT run `git commit` or `git push`.** Leave all changes uncommitted for developer review.\n6. **Scope guard**: If the follow-up changes are too large in scope (e.g., fundamentally restructuring the original implementation, touching more than 5-6 files, or requiring new infrastructure), stop and ask the user for guidance rather than attempting everything. Follow-up reimplementations should be small and targeted.\n7. **If a change is ambiguous or blocked**, note the issue clearly and continue with the next change rather than halting entirely.\n\nThis stage is **critical** — if a blocking error prevents further progress, stop and report the failure.\n\n## Stage 3 — Final Summary Report\n\nDisplay a structured report after all stages complete:\n\n```\n## Reimplement Complete\n\n**Ticket**: <ticket_key>\n\n**Changes Made**:\n- <brief summary of each change>\n\n**Developer Action Items**:\n- All changes are uncommitted. Review the changes with `git diff` before committing.\n- Run the project's test suite to verify nothing is broken before committing.\n\n**Warnings**:\n<If any issues arose during implementation (scope concerns, ambiguous requirements,\nfiles that couldn't be modified), list them here. If no warnings, omit this section.>\n```\n\n## Final Report\n\nOn success, display the structured report from Stage 3 confirming that the follow-up changes are complete.\n\nOn failure at any critical stage (Stage 0 or Stage 1), display which stage failed and the error details.\n",
|
|
16
|
+
"review-ticket.md": "# Review Ticket\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n1. Parse `$ARGUMENTS` to extract a single required `ticket_key` (e.g., `BAPI-123`). If `$ARGUMENTS` is empty or does not match the Jira key pattern (`[A-Z][A-Z0-9]+-\\d+`), stop immediately and display:\n ```\n Invalid ticket key format. Expected: PROJ-123\n Usage: /review-ticket <ticket_key>\n ```\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"review-ticket\"`\n - `variables`: `{ \"ticket_key\": \"<ticket_key>\" }`\n\n If the tool returns an error, stop and report the failure.\n\n3. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n4. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Ticket**: <ticket_key>\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
17
|
+
"run-tests.md": "Run the project's full test suite (unit, integration, and Playwright E2E), triage failures, fix test-code issues, and produce a structured health-check report in docs/tmp/testing/.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\n## Stage 0 — Argument Parsing and Setup\n\n1. **Parse `$ARGUMENTS`** for optional flags. Supported flags:\n - `--skip-integration` — skip integration tests (they make real LLM calls)\n - `--skip-playwright` — skip Playwright E2E tests (they require a running server)\n - `--unit-only` — shorthand that implies both `--skip-integration` and `--skip-playwright`\n\n Resolve flags to boolean variables:\n - Start with: `run_integration = true`, `run_playwright = true`\n - If `--unit-only` is present: set both to `false`\n - If `--skip-integration` is present: set `run_integration = false`\n - If `--skip-playwright` is present: set `run_playwright = false`\n - Unknown flags: note them in the final report as \"Unrecognized flag ignored\" but do not fail\n\n2. **Generate a run timestamp** using the current date and time in `YYYY-MM-DD-HH-MM` format (e.g., `2026-03-10-14-35`). Store this as `run_timestamp`. Both output documents will use this value.\n\nThis stage has no failure conditions — proceed to Stage 1.\n\n## Stage 1 — Environment Setup\n\nRun the following command in the terminal:\n```\nmkdir -p docs/tmp/testing/\n```\nIf this fails, stop immediately and report: \"Cannot create output directory docs/tmp/testing/ — check permissions.\"\n\nVerify the Python venv is available:\n```\nsource .venv/bin/activate && python --version\n```\nIf the venv is not available, note \"venv not available\" in the report and skip unit and integration tests. Continue to Playwright stage if applicable.\n\n## Stage 2 — Unit Tests\n\nRun in the terminal:\n```\nsource .venv/bin/activate && pytest tests/pytest/ -v --tb=short\n```\n\nCapture the full output. Extract the pytest summary line (e.g., `47 passed, 3 failed in 12.4s`). For each failing test, apply the triage logic below, then record the result.\n\n## Stage 3 — Integration Tests\n\nIf `run_integration` is `false`, skip this stage and record: `Integration tests: SKIPPED (--skip-integration or --unit-only flag was set)`\n\nIf `run_integration` is `true`, run each subdirectory as a separate batch. **Continue to the next batch even if the current one has failures.**\n\nRun the following batches in order in the terminal:\n```\nsource .venv/bin/activate && pytest tests/integration/code_writer/ --run-integration -v -s\nsource .venv/bin/activate && pytest tests/integration/estimator/ --run-integration -v -s\nsource .venv/bin/activate && pytest tests/integration/ticket_updater/ --run-integration -v -s\nsource .venv/bin/activate && pytest tests/integration/routes/ --run-integration -v -s\nsource .venv/bin/activate && pytest tests/integration/db/ --run-integration -v -s\n```\n\nApply triage logic to any failures. Note: integration test failures involve real LLM behavior and are more likely to be implementation issues than test-code issues — apply extra caution before fixing.\n\n## Stage 4 — Playwright E2E Tests\n\nIf `run_playwright` is `false`, skip this stage and record: `Playwright tests: SKIPPED (--skip-playwright or --unit-only flag was set)`\n\nIf `run_playwright` is `true`, first check whether the server is running:\n```\ncurl -s -o /dev/null -w \"%{http_code}\" http://localhost:8000/ping\n```\n\nIf the status code is NOT `200`, skip Playwright and record:\n```\nPlaywright tests: SKIPPED — server not running.\nStart the server with: uvicorn main:app --reload\nThen re-run this command without --skip-playwright.\n```\n\nIf the server IS running, run each spec directory as a separate batch. **Continue to the next batch even if the current one has failures.**\n\n```\nnpx playwright test tests/playwright/setup/ --config=tests/playwright/playwright.config.js --reporter=list\nnpx playwright test tests/playwright/projects/ --config=tests/playwright/playwright.config.js --reporter=list\nnpx playwright test tests/playwright/optimize/ --config=tests/playwright/playwright.config.js --reporter=list\n```\n\nApply triage logic to any failures.\n\n## Triage Logic\n\nFor every failing test, examine the test file and the code it tests. Classify as ONE of the following:\n\n### TEST-CODE ISSUE — fix it directly\n\nClassify as a test-code issue if ANY of the following applies:\n- The test asserts against a hardcoded value that no longer matches current behavior (outdated mock data)\n- The test imports or calls a function that was renamed, moved, or removed\n- The test asserts on a response field that was restructured\n- The test expects a specific error message string that has since changed\n- A fixture or conftest references a removed table column or model field\n\n**Action**: Apply a minimal, targeted fix to the test file only. Then re-run just that failing test to confirm:\n```\nsource .venv/bin/activate && pytest path/to/test_file.py::test_function_name -v --tb=short\n```\nIf the re-run **still fails** after your fix, do not make further edits — escalate to implementation-code issue instead and revert your change.\n\n### IMPLEMENTATION-CODE ISSUE (or UNCERTAIN) — document it, do not fix\n\nClassify as an implementation issue if ANY of the following applies:\n- The production function raises an unexpected exception\n- A route handler returns the wrong status code for a documented behavior\n- Business logic produces incorrect output that the test correctly asserts against\n- You are not confident the test is wrong\n\n**Action**: Do NOT touch any files in `api/`, `src/`, `main.py`, or `db/`. Record this issue in the implementation-issues document.\n\n## Stage 5 — Write Output Documents\n\n### Document 1: Test Run Report (always write this)\n\nWrite to: `docs/tmp/testing/test-run-{run_timestamp}.md`\n\n```markdown\n# Test Run: {run_timestamp}\n\n## Configuration\n- Unit tests: RUN\n- Integration tests: RUN / SKIPPED — (reason)\n- Playwright tests: RUN / SKIPPED — (reason)\n\n## Unit Tests\n**Result**: X passed, Y failed\n**Duration**: ~Ns\n**Fixes applied**:\n- `path/to/test_file.py`: brief description of what was fixed\n- (or \"none\" if no fixes were needed)\n\n**Failures escalated as implementation issues**: N\n\n## Integration Tests\n\n### tests/integration/code_writer/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/integration/estimator/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/integration/ticket_updater/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/integration/routes/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/integration/db/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n## Playwright Tests\n\n### tests/playwright/setup/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/playwright/projects/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### tests/playwright/optimize/\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n## Overall Summary\n- Total test fixes applied: N\n- Suspected implementation issues found: N\n- Implementation issues document: docs/tmp/testing/implementation-issues-{run_timestamp}.md\n (or \"not created — no issues found\")\n```\n\n### Document 2: Implementation Issues (only write if issues were found)\n\nIf at least one failure was escalated as an implementation-code issue, write to:\n`docs/tmp/testing/implementation-issues-{run_timestamp}.md`\n\n```markdown\n# Suspected Implementation Issues: {run_timestamp}\n\nThese test failures were NOT fixed. They may indicate bugs in production code.\nA developer should investigate each item before merging.\n\n## Issue 1\n- **Test**: `path/to/test_file.py::test_function_name`\n- **Tier**: unit / integration / playwright\n- **Failure message**: (paste the key assertion or exception line)\n- **Why not fixed**: (brief reasoning, e.g., \"production function raises KeyError on valid input\")\n\n## Issue 2\n...\n```\n\nIf no implementation issues were found, do NOT create this file.\n\n## Final Output\n\nAfter writing all documents, print this summary:\n\n```\nTest run complete: {run_timestamp}\nReport saved to: docs/tmp/testing/test-run-{run_timestamp}.md\nImplementation issues: docs/tmp/testing/implementation-issues-{run_timestamp}.md (if applicable)\nNo suspected implementation issues found. (if none)\n```\n",
|
|
18
|
+
"scan-tickets.md": "$ARGUMENTS\n\n---\n\n# Instructions\n\nSynchronize recently-updated Jira tickets with the local `tickets` database table and backfill missing workflow state timestamps. Perform all work directly in the main thread.\n\n## Stage 0 — Parse Arguments and Calculate Date\n\n1. Read the value of `$ARGUMENTS`. If it is empty, whitespace-only, or not a valid integer, default `months_back` to `3`. If it contains multiple tokens, extract only the first token and attempt to parse it as an integer. If parsing fails, default to `3`.\n\n2. Calculate `updated_since` by subtracting `months_back` months from today's date. Format the result as `YYYY-MM-DD`. Example: if today is 2026-03-07 and `months_back` is 3, then `updated_since` is 2025-12-07.\n\n3. Display the parsed values: \"Scanning tickets updated since {updated_since} (months_back = {months_back})\"\n\n4. Initialize the following tracking variables:\n - `tickets_scanned` = 0 (total tickets fetched from Jira)\n - `newly_tracked` = 0 (tickets inserted into database for the first time)\n - `state_updated_list` = [] (list of objects with ticket key and fields updated)\n - `warnings` = [] (list of warning strings for any per-ticket failures)\n\n## Stage 1 — Connectivity Check\n\n1. Call the `ping` MCP tool (no arguments needed — it uses the configured repo automatically).\n\n2. Check the response. If the response contains `\"status\": \"ok\"`, proceed to Stage 2.\n\n3. If the `ping` call fails or returns an error, **stop** and display the following diagnostic guidance:\n - **Connection to Bridge API failed.** Verify the following:\n - 1. Check that the Bridge API MCP server is configured in your editor's MCP settings\n - 2. The `BAPI_BASE_URL` environment variable points to a running Bridge API instance\n - 3. The `BAPI_API_KEY` environment variable contains a valid API key\n - 4. The `BAPI_REPO_NAME` environment variable is set to the correct repository name\n\n Do not proceed to subsequent stages.\n\n## Stage 2 — Fetch All Tickets from Jira\n\n1. Initialize an empty list `all_tickets` and set `offset` to `0`.\n\n2. Enter a pagination loop:\n - Call the `get_tickets` MCP tool with: `updated_since` set to the calculated date, `limit` set to `100`, and `offset` set to the current offset value.\n - Parse the JSON response. The response contains a `tickets` array of ticket objects. Each ticket object has a `ticket_number` field (the Jira key, e.g., `BAPI-42`), along with `summary`, `status`, `issue_type`, `assignee`, and `updated_at`.\n - Append all tickets from the response's `tickets` array to `all_tickets`.\n - If the number of tickets returned in this page equals `100`, increment `offset` by `100` and repeat the loop.\n - If fewer than `100` tickets are returned, exit the loop.\n\n3. Set `tickets_scanned` to the length of `all_tickets`.\n\n4. Display: \"Fetched {tickets_scanned} tickets from Jira. Processing...\"\n\n5. If the `get_tickets` call fails at any point during pagination, **stop** and report the error. Do not proceed to Stage 3.\n\n## Stage 3 — Track Each Ticket\n\n1. Iterate over each ticket in `all_tickets`. For each ticket:\n - Call the `track_ticket` MCP tool with `ticket_number` set to the ticket's `ticket_number` field. If the ticket object includes a `summary` field, pass it as the `description` parameter.\n - Inspect the response message. If the response indicates the ticket was newly created/inserted (look for words like \"created\" or \"inserted\" in the message, as opposed to \"already exists\" or \"updated\"), increment `newly_tracked` by 1.\n - If the `track_ticket` call fails for this ticket, add a warning to the `warnings` list (e.g., \"Warning: Failed to track ticket {ticket_number}: {error}\") and **continue** to the next ticket. Do not abort the scan.\n\n2. Display a brief progress indicator every 25 tickets, e.g., \"Tracked {N} of {tickets_scanned} tickets...\"\n\n## Stage 4 — Detect and Backfill Workflow State\n\nDisplay: \"Checking workflow state for {tickets_scanned} tickets...\"\n\nIterate over each ticket in `all_tickets`. For each ticket (referenced by its `ticket_number` field), perform the following sub-steps. Wrap the entire per-ticket block in error handling: if the `get_ticket_state` call or the subsequent `update_ticket_state` call fails for a ticket, add a warning to `warnings` and continue to the next ticket.\n\n**Sub-step 4a — Retrieve current state**: Call the `get_ticket_state` MCP tool with `ticket_number` set to the ticket's key. The response contains:\n\n- Five timestamp fields (each is a timestamp string or null): `clarify_called`, `clarify_answered`, `critique_called`, `critique_answered`, `plan_generated`\n- Three boolean artifact flags: `has_clarifying_questions`, `has_critique`, `has_plan`\n\nIf the call returns a 404 or any error, add a warning to `warnings` and continue to the next ticket.\n\n**Sub-step 4b — Build fields_to_update list**: Initialize an empty `fields_to_update` list, then apply the following rules:\n\n- If `has_clarifying_questions` is `true` AND `clarify_called` is null -> add `\"clarify_called\"` to `fields_to_update`\n- If `has_clarifying_questions` is `true` AND `clarify_answered` is null -> add `\"clarify_answered\"` to `fields_to_update`\n- If `has_critique` is `true` AND `critique_called` is null -> add `\"critique_called\"` to `fields_to_update`\n- If `has_critique` is `true` AND `critique_answered` is null -> add `\"critique_answered\"` to `fields_to_update`\n- If `has_plan` is `true` AND `plan_generated` is null -> add `\"plan_generated\"` to `fields_to_update`\n\n**Sub-step 4c — Call update_ticket_state if needed**: If `fields_to_update` is non-empty, call the `update_ticket_state` MCP tool with `ticket_number` set to the ticket's key and `fields` set to the `fields_to_update` array. If this succeeds, add an entry to `state_updated_list` recording the ticket key and the list of fields that were set. If `update_ticket_state` fails, add a warning to `warnings` and continue.\n\nDisplay a progress indicator every 25 tickets that includes the current ticket key, e.g., \"Checked state for {TICKET-KEY} ({N} of {tickets_scanned} tickets)\"\n\n## Stage 5 — Report Summary\n\n1. Calculate `state_updated_count` as the length of `state_updated_list`.\n\n2. Display the summary:\n\n ```\n **Scan complete**\n\n * Tickets scanned: {tickets_scanned}\n * Newly tracked: {newly_tracked}\n * State updated: {state_updated_count}\n ```\n\n3. If `state_updated_list` is non-empty, display a section titled \"Updated tickets:\" with one bullet per ticket showing the ticket key and the comma-separated list of fields that were set. Example:\n\n ```\n Updated tickets:\n * BAPI-101: clarify_called, clarify_answered\n * BAPI-105: critique_called, critique_answered, plan_generated\n ```\n\n4. If the `warnings` list is non-empty, display a section titled \"Warnings:\" listing each warning string as a bullet. Example:\n\n ```\n Warnings:\n * Warning: Failed to track ticket BAPI-99: Connection timeout\n * Warning: State query failed for BAPI-112: SQL error\n ```\n\n5. If there are no warnings, do not display the \"Warnings:\" section.\n",
|
|
19
|
+
"teach-bridge.md": "Update a Bridge API configuration field via a natural-language teaching.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command takes a natural-language teaching (e.g., \"use data-testid selectors in Playwright tests\") and updates the appropriate Bridge API configuration field. The teaching is auto-classified to the correct field, merged with existing content as actionable AI instructions, and uploaded after user confirmation.\n\n`$ARGUMENTS` is required — it is the teaching text. If `$ARGUMENTS` is empty, show:\n\n```\nUsage: /teach-bridge <teaching>\n\nExamples:\n /teach-bridge use data-testid selectors in Playwright tests\n /teach-bridge always validate input DTOs with Pydantic before passing to service layer\n /teach-bridge prefer composition over inheritance for service classes\n```\n\nIf any stage fails, stop immediately and report which stage failed and why.\n\n## Stage 0 — Preflight\n\n1. **Validate arguments**: If `$ARGUMENTS` is empty or contains only whitespace, display the usage instructions above and stop.\n\n2. **Connectivity check**: Call the `ping` MCP tool (no parameters). If the ping fails or returns an error, stop and show:\n - Check that the Bridge API MCP server is configured in your editor's MCP settings\n - Check that `BAPI_BASE_URL` is set and the server is reachable\n - Check that `BAPI_API_KEY` is valid\n - Check that `BAPI_REPO_NAME` matches a configured repository\n\n3. **Admin check**: Call the `get_my_role` MCP tool (no parameters). Inspect the response:\n - If `role` is `\"admin\"` OR `source` is `\"legacy\"`: proceed normally.\n - Otherwise: stop immediately and display:\n ```\n Admin access required. Your API key has role \"<role>\" (source: <source>).\n Only admin keys and legacy shared keys can update configuration fields.\n Contact your project administrator to request admin access.\n ```\n\nIf this stage fails, stop immediately and report the error. Do not proceed to Stage 1.\n\n## Stage 1 — Classify\n\n1. **List available fields**: Call the `list_config_fields` MCP tool (no parameters). This returns all available configuration field names with descriptions.\n\n2. **Evaluate the teaching**: Compare the user's teaching (`$ARGUMENTS`) against each field's description to determine which field it applies to.\n\n3. **Handle classification outcomes**:\n - **Clear single match**: If one field is clearly the best target, proceed to Stage 2 with that field.\n - **Multiple plausible matches**: If 2-3 fields are equally plausible, present them to the user with their descriptions and ask which one to update. Wait for user input before proceeding.\n - **No confident match**: If you cannot confidently map the teaching to any field, ask the user to elaborate or specify which field they intend. Wait for user input before proceeding.\n\n## Stage 2 — Merge\n\n1. **Read current value**: Call the `get_config_field` MCP tool with `field_name` set to the selected field from Stage 1. Capture the current value, description, and examples from the response.\n\n2. **Draft the update**:\n - **If the field is currently null or empty**: Compose initial content from the teaching. Rephrase the user's input as imperative, agent-facing instructions (e.g., convert \"I want you to use data-testid\" to \"Always use `data-testid` attributes for Playwright element locators\"). Do not use the user's exact conversational text.\n - **If the field has existing content**: Merge the teaching into the existing value at the most appropriate location. Rephrase as imperative, agent-facing instructions. Preserve the existing structure and formatting.\n\n3. **Handle contradictions**: If the teaching contradicts existing instructions in the field, present both the existing instruction and the new teaching side-by-side and ask the user which should take precedence. Wait for user input before proceeding.\n\n## Stage 3 — Confirm and Upload\n\n1. **Show the proposed update**: Display to the user:\n - **Field**: The name of the field being updated\n - **Change summary**: A brief description of what was added or changed\n - **Full proposed value**: The complete new value for the field (not just the diff)\n\n2. **Wait for confirmation**: Ask the user to confirm, request edits, or abort.\n\n3. **On confirmation**: Call the `update_config_field` MCP tool with:\n - `field_name`: the selected field name\n - `value`: the full merged value (pass inline, do not use `file_path`)\n\n Display a success message confirming the update.\n\n4. **On rejection**: Ask the user what they'd like to change. If they provide edits, revise the proposed value and show it again. If they abort, stop without making any changes.\n"
|
|
20
|
+
};
|
package/build/index.js
CHANGED
|
@@ -14,8 +14,13 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
import { writeFile, mkdir, readFile, stat } from "fs/promises";
|
|
16
16
|
import path from "path";
|
|
17
|
-
import { PIPELINES, INSTRUCTIONS } from "./pipelines.generated.js";
|
|
18
|
-
import {
|
|
17
|
+
import { PIPELINES as BUNDLED_PIPELINES, INSTRUCTIONS as BUNDLED_INSTRUCTIONS } from "./pipelines.generated.js";
|
|
18
|
+
import { COMMANDS } from "./commands.generated.js";
|
|
19
|
+
import { resolveRecipe, loadCustomPipelines } from "./pipeline-utils.js";
|
|
20
|
+
// Mutable pipeline/instruction state — starts with bundled, merged with user at startup
|
|
21
|
+
const PIPELINES = { ...BUNDLED_PIPELINES };
|
|
22
|
+
const INSTRUCTIONS = { ...BUNDLED_INSTRUCTIONS };
|
|
23
|
+
let userPipelineKeys = new Set();
|
|
19
24
|
// ---------------------------------------------------------------------------
|
|
20
25
|
// Configuration
|
|
21
26
|
// ---------------------------------------------------------------------------
|
|
@@ -23,6 +28,7 @@ const BASE_URL = process.env.BAPI_BASE_URL ?? "https://bridgegpt-api.com";
|
|
|
23
28
|
const REPO_NAME = process.env.BAPI_REPO_NAME ?? "";
|
|
24
29
|
const API_KEY = process.env.BAPI_API_KEY ?? "";
|
|
25
30
|
const BAPI_DOCS_DIR = process.env.BAPI_DOCS_DIR ?? "docs/tmp";
|
|
31
|
+
const BAPI_PIPELINES_DIR = path.resolve(process.cwd(), process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
|
|
26
32
|
const GET_HEADERS = { "X-API-Key": API_KEY };
|
|
27
33
|
const POST_HEADERS = {
|
|
28
34
|
"X-API-Key": API_KEY,
|
|
@@ -199,6 +205,179 @@ async function pollForResult(getUrl, timeoutMs, label) {
|
|
|
199
205
|
}
|
|
200
206
|
}
|
|
201
207
|
// ---------------------------------------------------------------------------
|
|
208
|
+
// CLI: --init scaffolds slash commands into the current project
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
if (process.argv.includes("--init")) {
|
|
211
|
+
try {
|
|
212
|
+
const cwd = process.cwd();
|
|
213
|
+
const dirs = [
|
|
214
|
+
path.join(cwd, ".claude", "commands"),
|
|
215
|
+
path.join(cwd, ".cursor", "commands"),
|
|
216
|
+
];
|
|
217
|
+
const writtenFiles = new Set();
|
|
218
|
+
const skippedFiles = new Set();
|
|
219
|
+
const overwrittenFiles = new Set();
|
|
220
|
+
for (const dir of dirs) {
|
|
221
|
+
await mkdir(dir, { recursive: true });
|
|
222
|
+
for (const [filename, content] of Object.entries(COMMANDS)) {
|
|
223
|
+
const target = path.join(dir, filename);
|
|
224
|
+
try {
|
|
225
|
+
const existing = await readFile(target, "utf-8");
|
|
226
|
+
if (existing === content) {
|
|
227
|
+
skippedFiles.add(filename);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
overwrittenFiles.add(filename);
|
|
231
|
+
}
|
|
232
|
+
catch { /* file doesn't exist */ }
|
|
233
|
+
await writeFile(target, content, "utf-8");
|
|
234
|
+
writtenFiles.add(filename);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// A file written in any directory takes priority over skipped
|
|
238
|
+
for (const f of writtenFiles)
|
|
239
|
+
skippedFiles.delete(f);
|
|
240
|
+
const total = Object.keys(COMMANDS).length;
|
|
241
|
+
console.log(`Bridge API: scaffolded ${total} commands into ${dirs.length} directories`);
|
|
242
|
+
if (writtenFiles.size > 0)
|
|
243
|
+
console.log(` Written: ${writtenFiles.size}`);
|
|
244
|
+
if (overwrittenFiles.size > 0)
|
|
245
|
+
console.log(` Overwritten (content changed): ${overwrittenFiles.size}`);
|
|
246
|
+
if (skippedFiles.size > 0)
|
|
247
|
+
console.log(` Skipped (unchanged): ${skippedFiles.size}`);
|
|
248
|
+
console.log(` .claude/commands/ and .cursor/commands/`);
|
|
249
|
+
// Scaffold custom pipeline directories
|
|
250
|
+
const pipelinesDir = BAPI_PIPELINES_DIR;
|
|
251
|
+
const instrDir = path.join(path.dirname(BAPI_PIPELINES_DIR), "instructions");
|
|
252
|
+
await mkdir(pipelinesDir, { recursive: true });
|
|
253
|
+
await mkdir(instrDir, { recursive: true });
|
|
254
|
+
const readmePath = path.join(pipelinesDir, "README.md");
|
|
255
|
+
const examplePath = path.join(pipelinesDir, "example-pipeline.json");
|
|
256
|
+
const readmeContent = `# Custom Pipelines
|
|
257
|
+
|
|
258
|
+
Place custom pipeline JSON files in this directory. They will be loaded at
|
|
259
|
+
server startup and available alongside the bundled pipelines.
|
|
260
|
+
|
|
261
|
+
## JSON Schema
|
|
262
|
+
|
|
263
|
+
Each pipeline file must be a JSON object with these fields:
|
|
264
|
+
|
|
265
|
+
| Field | Type | Required | Description |
|
|
266
|
+
|---------------|------------|----------|------------------------------------------|
|
|
267
|
+
| \`name\` | string | yes | Display name of the pipeline |
|
|
268
|
+
| \`description\` | string | no | Short description shown in list_pipelines |
|
|
269
|
+
| \`variables\` | string[] | no | Variable names for substitution |
|
|
270
|
+
| \`steps\` | Step[] | yes | Ordered list of steps to execute |
|
|
271
|
+
|
|
272
|
+
## Step Types
|
|
273
|
+
|
|
274
|
+
### mcp_call
|
|
275
|
+
Calls an MCP tool with specific parameters.
|
|
276
|
+
\`\`\`json
|
|
277
|
+
{
|
|
278
|
+
"type": "mcp_call",
|
|
279
|
+
"tool": "tool_name",
|
|
280
|
+
"params": { "key": "value" },
|
|
281
|
+
"description": "What this step does",
|
|
282
|
+
"on_error": "halt",
|
|
283
|
+
"requires_approval": false
|
|
284
|
+
}
|
|
285
|
+
\`\`\`
|
|
286
|
+
|
|
287
|
+
### agent_task
|
|
288
|
+
Gives the agent a free-form instruction to execute.
|
|
289
|
+
\`\`\`json
|
|
290
|
+
{
|
|
291
|
+
"type": "agent_task",
|
|
292
|
+
"instruction": "Do something specific",
|
|
293
|
+
"description": "What this step does",
|
|
294
|
+
"on_error": "warn_and_continue"
|
|
295
|
+
}
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
Or reference an instruction file from \`.bridge/instructions/\`:
|
|
299
|
+
\`\`\`json
|
|
300
|
+
{
|
|
301
|
+
"type": "agent_task",
|
|
302
|
+
"instruction_file": "my-instructions.md",
|
|
303
|
+
"description": "What this step does"
|
|
304
|
+
}
|
|
305
|
+
\`\`\`
|
|
306
|
+
|
|
307
|
+
## Variables
|
|
308
|
+
|
|
309
|
+
Declare variables in the \`variables\` array and reference them with \`{variable_name}\`
|
|
310
|
+
in step params, instructions, and descriptions. The \`docs_dir\` variable is
|
|
311
|
+
automatically provided by the server.
|
|
312
|
+
|
|
313
|
+
## on_error
|
|
314
|
+
|
|
315
|
+
- \`"halt"\` (default) — stop the pipeline immediately on failure
|
|
316
|
+
- \`"warn_and_continue"\` — log a warning and proceed to the next step
|
|
317
|
+
`;
|
|
318
|
+
const exampleContent = JSON.stringify({
|
|
319
|
+
name: "Example Pipeline",
|
|
320
|
+
description: "A sample pipeline demonstrating all step types and features.",
|
|
321
|
+
variables: ["ticket_key"],
|
|
322
|
+
steps: [
|
|
323
|
+
{
|
|
324
|
+
type: "mcp_call",
|
|
325
|
+
tool: "get_ticket",
|
|
326
|
+
params: { ticket_number: "{ticket_key}" },
|
|
327
|
+
description: "Fetch the ticket details from Jira",
|
|
328
|
+
on_error: "halt",
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
type: "agent_task",
|
|
332
|
+
instruction: "Read the ticket details above and summarize the key requirements in 3 bullet points.",
|
|
333
|
+
description: "Summarize ticket requirements",
|
|
334
|
+
on_error: "halt",
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
type: "agent_task",
|
|
338
|
+
instruction: "Search the codebase for files related to the ticket and list the top 5 most relevant files.",
|
|
339
|
+
description: "Find relevant source files",
|
|
340
|
+
on_error: "warn_and_continue",
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
type: "mcp_call",
|
|
344
|
+
tool: "add_comment",
|
|
345
|
+
params: {
|
|
346
|
+
ticket_number: "{ticket_key}",
|
|
347
|
+
comment_text: "Pipeline analysis complete. See agent output for details.",
|
|
348
|
+
},
|
|
349
|
+
description: "Post a summary comment to the ticket",
|
|
350
|
+
on_error: "warn_and_continue",
|
|
351
|
+
requires_approval: true,
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
}, null, 2) + "\n";
|
|
355
|
+
try {
|
|
356
|
+
await stat(readmePath);
|
|
357
|
+
console.log(` ${path.relative(cwd, readmePath)} (skipped — already exists)`);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
await writeFile(readmePath, readmeContent, "utf-8");
|
|
361
|
+
console.log(` ${path.relative(cwd, readmePath)} (written)`);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
await stat(examplePath);
|
|
365
|
+
console.log(` ${path.relative(cwd, examplePath)} (skipped — already exists)`);
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
await writeFile(examplePath, exampleContent, "utf-8");
|
|
369
|
+
console.log(` ${path.relative(cwd, examplePath)} (written)`);
|
|
370
|
+
}
|
|
371
|
+
console.log(` ${path.relative(cwd, instrDir)}/ (ensured)`);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
376
|
+
console.error(`Bridge API --init failed: ${msg}`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
202
381
|
// Server
|
|
203
382
|
// ---------------------------------------------------------------------------
|
|
204
383
|
const server = new McpServer({
|
|
@@ -1560,6 +1739,7 @@ server.registerTool("list_pipelines", {
|
|
|
1560
1739
|
name: key,
|
|
1561
1740
|
description: pipeline.description ?? "",
|
|
1562
1741
|
variables: (pipeline.variables ?? []).filter((v) => v !== "docs_dir"),
|
|
1742
|
+
source: userPipelineKeys.has(key) ? "user" : "bundled",
|
|
1563
1743
|
}));
|
|
1564
1744
|
return {
|
|
1565
1745
|
content: [{ type: "text", text: JSON.stringify(list, null, 2) }],
|
|
@@ -1625,6 +1805,17 @@ server.registerTool("get_pipeline_recipe", {
|
|
|
1625
1805
|
// ---------------------------------------------------------------------------
|
|
1626
1806
|
// Entry point
|
|
1627
1807
|
// ---------------------------------------------------------------------------
|
|
1808
|
+
// Load custom user pipelines before accepting connections
|
|
1809
|
+
const instructionsDir = path.join(path.dirname(BAPI_PIPELINES_DIR), "instructions");
|
|
1810
|
+
const customResult = await loadCustomPipelines(BAPI_PIPELINES_DIR, instructionsDir, BUNDLED_INSTRUCTIONS);
|
|
1811
|
+
for (const [key, pipeline] of Object.entries(customResult.pipelines)) {
|
|
1812
|
+
if (key in BUNDLED_PIPELINES) {
|
|
1813
|
+
console.error(`Warning: user pipeline "${key}" overrides bundled pipeline.`);
|
|
1814
|
+
}
|
|
1815
|
+
PIPELINES[key] = pipeline;
|
|
1816
|
+
}
|
|
1817
|
+
Object.assign(INSTRUCTIONS, customResult.instructions);
|
|
1818
|
+
userPipelineKeys = customResult.userPipelineKeys;
|
|
1628
1819
|
const transport = new StdioServerTransport();
|
|
1629
1820
|
await server.connect(transport);
|
|
1630
1821
|
console.error("Bridge API MCP server running on stdio");
|
package/build/pipeline-utils.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* substitution, and recipe resolution. Consumed by the build-time bundler
|
|
4
4
|
* (bundle-pipelines.js) and by MCP tool handlers at runtime.
|
|
5
5
|
*/
|
|
6
|
+
import { readdir, readFile } from "fs/promises";
|
|
7
|
+
import path from "path";
|
|
6
8
|
// ---------------------------------------------------------------------------
|
|
7
9
|
// Schema Validation
|
|
8
10
|
// ---------------------------------------------------------------------------
|
|
@@ -170,6 +172,97 @@ export function resolveRecipe(pipeline, instructions, variables, skipSteps) {
|
|
|
170
172
|
pipeline: pipeline.name,
|
|
171
173
|
description: pipeline.description ?? "",
|
|
172
174
|
total_steps: resolvedSteps.length,
|
|
175
|
+
agent_instructions: "IMPORTANT: Execute every step below in exact sequential order. " +
|
|
176
|
+
"For mcp_call steps, call the specified tool with the provided params. " +
|
|
177
|
+
"For agent_task steps, follow the instruction text using any tools it specifies. " +
|
|
178
|
+
"If requires_approval is true, pause and ask the user for confirmation before executing. " +
|
|
179
|
+
'For on_error "halt", stop the pipeline immediately on failure. ' +
|
|
180
|
+
'For on_error "warn_and_continue", log a warning and proceed. ' +
|
|
181
|
+
"Do not skip steps, reorder them, or substitute your own tool calls.",
|
|
173
182
|
steps: resolvedSteps,
|
|
174
183
|
};
|
|
175
184
|
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Custom Pipeline Loading
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
export async function loadCustomPipelines(pipelinesDir, instructionsDir, bundledInstructions) {
|
|
189
|
+
const mergedInstructions = { ...bundledInstructions };
|
|
190
|
+
const userPipelines = {};
|
|
191
|
+
const userPipelineKeys = new Set();
|
|
192
|
+
// Phase 1: Load custom instruction files
|
|
193
|
+
try {
|
|
194
|
+
const instrFiles = await readdir(instructionsDir);
|
|
195
|
+
for (const file of instrFiles) {
|
|
196
|
+
if (!file.endsWith(".md"))
|
|
197
|
+
continue;
|
|
198
|
+
try {
|
|
199
|
+
const content = await readFile(path.join(instructionsDir, file), "utf-8");
|
|
200
|
+
if (file in bundledInstructions) {
|
|
201
|
+
console.error(`Warning: custom instruction "${file}" overrides a bundled instruction.`);
|
|
202
|
+
}
|
|
203
|
+
mergedInstructions[file] = content;
|
|
204
|
+
}
|
|
205
|
+
catch (readErr) {
|
|
206
|
+
const msg = readErr instanceof Error ? readErr.message : String(readErr);
|
|
207
|
+
console.error(`Warning: skipping instruction "${file}" — failed to read: ${msg}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
if (err.code !== "ENOENT") {
|
|
213
|
+
console.error(`Warning: could not read instructions directory "${instructionsDir}": ${err.message}`);
|
|
214
|
+
}
|
|
215
|
+
// Either ENOENT or unreadable — skip custom instructions
|
|
216
|
+
}
|
|
217
|
+
// Phase 2: Load custom pipeline JSON files
|
|
218
|
+
try {
|
|
219
|
+
const pipelineFiles = await readdir(pipelinesDir);
|
|
220
|
+
for (const file of pipelineFiles) {
|
|
221
|
+
if (!file.endsWith(".json"))
|
|
222
|
+
continue;
|
|
223
|
+
let parsed;
|
|
224
|
+
try {
|
|
225
|
+
const raw = await readFile(path.join(pipelinesDir, file), "utf-8");
|
|
226
|
+
parsed = JSON.parse(raw);
|
|
227
|
+
}
|
|
228
|
+
catch (parseErr) {
|
|
229
|
+
const msg = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
230
|
+
console.error(`Warning: skipping "${file}" — failed to parse: ${msg}`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const { valid, errors } = validatePipelineSchema(parsed);
|
|
234
|
+
if (!valid) {
|
|
235
|
+
console.error(`Warning: skipping "${file}" — validation errors:\n ${errors.join("\n ")}`);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const pipeline = parsed;
|
|
239
|
+
const key = file.replace(/\.json$/, "");
|
|
240
|
+
// Validate instruction_file references
|
|
241
|
+
let hasInvalidRef = false;
|
|
242
|
+
for (const step of pipeline.steps) {
|
|
243
|
+
if (step.type === "agent_task" && step.instruction_file) {
|
|
244
|
+
if (!(step.instruction_file in mergedInstructions)) {
|
|
245
|
+
console.error(`Warning: skipping "${file}" — instruction_file "${step.instruction_file}" not found.`);
|
|
246
|
+
hasInvalidRef = true;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (hasInvalidRef)
|
|
252
|
+
continue;
|
|
253
|
+
userPipelines[key] = pipeline;
|
|
254
|
+
userPipelineKeys.add(key);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
if (err.code !== "ENOENT") {
|
|
259
|
+
console.error(`Warning: could not read pipelines directory "${pipelinesDir}": ${err.message}`);
|
|
260
|
+
}
|
|
261
|
+
// Either ENOENT or unreadable — skip custom pipelines
|
|
262
|
+
return { pipelines: userPipelines, instructions: mergedInstructions, userPipelineKeys };
|
|
263
|
+
}
|
|
264
|
+
if (userPipelineKeys.size > 0) {
|
|
265
|
+
console.error(`Loaded ${userPipelineKeys.size} user pipeline(s) from ${pipelinesDir}`);
|
|
266
|
+
}
|
|
267
|
+
return { pipelines: userPipelines, instructions: mergedInstructions, userPipelineKeys };
|
|
268
|
+
}
|