@bridge_gpt/mcp-server 0.1.16 → 0.1.17
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 +36 -1
- package/build/agent-launchers/claude.js +85 -0
- package/build/agent-launchers/index.js +17 -0
- package/build/agent-launchers/types.js +1 -0
- package/build/chain-orchestrator.js +1150 -0
- package/build/chain-utils.js +68 -0
- package/build/commands.generated.js +3 -1
- package/build/fetch-stub.js +139 -0
- package/build/index.js +137 -10
- package/build/pipeline-orchestrator.js +57 -0
- package/build/pipelines.generated.js +132 -3
- package/build/schedule-run.js +951 -0
- package/build/schedule-store.js +132 -0
- package/build/scheduler-backends/at-fallback.js +144 -0
- package/build/scheduler-backends/escaping.js +113 -0
- package/build/scheduler-backends/index.js +72 -0
- package/build/scheduler-backends/launchd.js +216 -0
- package/build/scheduler-backends/systemd-user.js +237 -0
- package/build/scheduler-backends/task-scheduler.js +219 -0
- package/build/scheduler-backends/types.js +23 -0
- package/build/start-tickets.js +119 -59
- package/build/version.generated.js +1 -1
- package/package.json +7 -7
- package/pipelines/full-automation.json +47 -0
- package/pipelines/idea-to-ticket.json +71 -0
- package/smoke-test/SMOKE-TEST.md +509 -0
- package/smoke-test/smoke-test-mcp.md +23 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain utilities — pure types and side-effect-free helpers for the
|
|
3
|
+
* full-automation chain recipe schema and variable substitution.
|
|
4
|
+
*
|
|
5
|
+
* A chain recipe is intentionally a DIFFERENT shape from a pipeline recipe
|
|
6
|
+
* (``pipeline-utils.ts``): pipelines have ``steps``; chains have ``stages``,
|
|
7
|
+
* each naming a child ``pipeline_name`` plus optional fan-out and output
|
|
8
|
+
* declarations. Keep this module free of network, filesystem, or MCP-server
|
|
9
|
+
* imports so it can be unit-tested in isolation and consumed by both the
|
|
10
|
+
* build-time bundler and the runtime chain orchestrator.
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Pure helpers
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/** True only for strings whose trimmed content is non-empty. */
|
|
16
|
+
export function isNonEmptyString(value) {
|
|
17
|
+
return typeof value === "string" && value.trim() !== "";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Convert a supported primitive value into the string form used for child
|
|
21
|
+
* pipeline variables: strings pass through, booleans become "true"/"false",
|
|
22
|
+
* numbers become decimal strings, and null/undefined become "".
|
|
23
|
+
*/
|
|
24
|
+
export function toStringVariable(value) {
|
|
25
|
+
if (typeof value === "string")
|
|
26
|
+
return value;
|
|
27
|
+
if (typeof value === "boolean")
|
|
28
|
+
return value ? "true" : "false";
|
|
29
|
+
if (typeof value === "number")
|
|
30
|
+
return String(value);
|
|
31
|
+
if (value === null || value === undefined)
|
|
32
|
+
return "";
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
const chainVariablePattern = () => /\{([a-zA-Z_][a-zA-Z0-9_]*)}/g;
|
|
36
|
+
/**
|
|
37
|
+
* Replace every ``{name}`` placeholder in ``template`` with the matching
|
|
38
|
+
* value from ``variables``. Unknown placeholders are left intact.
|
|
39
|
+
*/
|
|
40
|
+
export function substituteChainTemplate(template, variables) {
|
|
41
|
+
return template.replace(chainVariablePattern(), (full, name) => {
|
|
42
|
+
if (Object.prototype.hasOwnProperty.call(variables, name)) {
|
|
43
|
+
return variables[name];
|
|
44
|
+
}
|
|
45
|
+
return full;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Recursively substitute placeholders in any string contained in ``value``
|
|
50
|
+
* (objects, arrays, and nested combinations) without mutating the original.
|
|
51
|
+
* Non-string scalar values are returned unchanged.
|
|
52
|
+
*/
|
|
53
|
+
export function substituteChainValue(value, variables) {
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
return substituteChainTemplate(value, variables);
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(value)) {
|
|
58
|
+
return value.map((item) => substituteChainValue(item, variables));
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === "object" && value !== null) {
|
|
61
|
+
const result = {};
|
|
62
|
+
for (const [k, v] of Object.entries(value)) {
|
|
63
|
+
result[k] = substituteChainValue(v, variables);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
@@ -8,6 +8,8 @@ export const COMMANDS = {
|
|
|
8
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
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 — Resolve Docs Directory\n\nCall the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n## Step 3 — 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
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\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. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n3. **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\n4. **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
|
+
"full-automation.md": "Run the end-to-end full-automation chain (idea-to-ticket → review-ticket → start-tickets) via the server-side chain orchestrator.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command drives Phase A's server-side full-automation chain. The only orchestration tools you may drive are `run_full_automation` and `resume_full_automation`; any other Bridge API MCP call you make must be one a server `agent_task` instruction explicitly directs. The server owns all orchestration — ticket creation, review fan-out, and the start-tickets handoff. Do NOT enrich, re-implement, or second-guess any of that work on the client side.\n\n## Stage 0 — Parse arguments\n\n1. Tokenize `$ARGUMENTS` on whitespace. Recognize the following position-independent flags. Each flag supports both the space form (`--flag value`) and the equals form (`--flag=value`) where a value is taken:\n - `--idea <text>` / `--idea=<text>`\n - `--idea-file <path>` / `--idea-file=<path>`\n - `--auto-approve`\n - `--require-approval`\n - `--scheduled-at <ISO-8601>` / `--scheduled-at=<ISO-8601>`\n - `--chain-run-id <UUID>` / `--chain-run-id=<UUID>`\n - `--max-children N` / `--max-children=N`\n - `--allow-duplicate`\n\n2. Value-consumption rules:\n - `--idea` (space form) consumes every subsequent token until the next recognized flag — the idea may contain spaces.\n - `--idea-file`, `--scheduled-at`, `--chain-run-id`, and `--max-children` each consume exactly one value token (the immediately following token, or the text after `=`).\n - `--auto-approve`, `--require-approval`, and `--allow-duplicate` are boolean toggles and consume no value.\n\n3. Free-form idea: all non-flag tokens become the free-form `idea` text **only when both `--idea` and `--idea-file` are absent**. Join those tokens back together preserving order and trim surrounding whitespace. When `--idea` or `--idea-file` is present, there must be no leftover non-flag tokens: reject any stray non-flag token (for example, text following `--idea=<text>` or following the `--idea-file <path>` value) before any MCP tool call rather than silently dropping it.\n\n4. Reject **unknown flags** (any token beginning with `--` that is not one of the recognized flags above) before making any MCP tool call. Stop and report the offending flag.\n\n5. Reject **combined `--idea` and `--idea-file`** before making any MCP tool call:\n ```text\n Provide exactly one of --idea or --idea-file; do not pass both.\n ```\n\n6. Missing-input rule: unless `--chain-run-id` is present, an idea is required. If `--chain-run-id` is absent **and** no idea was supplied (no `--idea`, no `--idea-file`, and no free-form idea tokens), stop immediately and display exactly:\n ```text\n Usage: /full-automation (--idea \"<text>\" | --idea-file <path> | <free-form idea>) [--require-approval] [--scheduled-at <ISO-8601>] [--chain-run-id <UUID>] [--max-children N] [--allow-duplicate]\n ```\n\n7. `--chain-run-id` is the resume path and does **not** require any idea content — when it is present, skip the missing-input check above and proceed to resume.\n\n8. `--idea-file` is forwarded as a path. The skill must **not** read the file contents locally; the server resolves the file.\n\n9. Resolve the derived values:\n - `auto_approve` defaults to `true` (full automation is hands-off by default). It is `false` **only** when `--require-approval` is present. `--auto-approve` is accepted but redundant (a no-op that restates the default), and `--scheduled-at` likewise runs hands-off. When `--require-approval` is present, the chain pauses at external-mutation and review-decision gates for confirmation.\n - `max_children` is the parsed positive integer when `--max-children` is present; otherwise omit it entirely so the server default applies.\n - `allow_duplicate` is `true` only when `--allow-duplicate` is present; otherwise omit it.\n\n## Stage 1 — Drift-check gate\n\nThis gate runs immediately after parsing and **before any MCP tool call**.\n\n1. If `--scheduled-at` is absent, skip this entire stage.\n2. Compute `delta_seconds = now_utc - scheduled_at` (both in UTC).\n3. If `delta_seconds <= 60`, proceed silently to Stage 2.\n4. If `delta_seconds > 60`, present this prompt verbatim (substituting the bracketed values):\n ```text\n Scheduled at <T-iso> UTC; running now at <now-iso> UTC (<Δ human-readable> late). The laptop was likely asleep or unavailable at the scheduled time. Confirm to proceed with the chain, or cancel.\n ```\n Offer the user the choices: `[Confirm] / [Cancel]`.\n5. On `Confirm`, proceed to Stage 2.\n6. On `Cancel`, print this message verbatim and stop:\n ```text\n Chain cancelled by user (drift confirmation declined). No Jira tickets created.\n ```\n When the user cancels, `run_full_automation` must **not** be called.\n7. The 60-second threshold is fixed and must not be made configurable.\n\n## Stage 2 — Run or resume the chain\n\nThe chain is driven entirely by the server-side orchestrator. Announce progress using each envelope's `preamble`, preserving its `Stage N of M — <title>` shape.\n\n### Stage 2a — Start (when `--chain-run-id` is absent)\n\nCall **only** `run_full_automation`. Build the payload, **omitting** any optional value that was not provided (never send `null` or empty strings):\n```json\n{\n \"idea\": \"<resolved inline/free-form idea, when provided>\",\n \"idea_file\": \"<idea-file path, when provided>\",\n \"auto_approve\": \"<resolved boolean>\",\n \"scheduled_at\": \"<scheduled-at value, when provided>\",\n \"max_children\": \"<parsed integer, when provided>\",\n \"allow_duplicate\": \"<true, when provided>\"\n}\n```\n\n### Stage 2b — Resume (when `--chain-run-id` is present)\n\nCall **only** `resume_full_automation` first, with:\n```json\n{\n \"chain_run_id\": \"<UUID>\",\n \"agent_result\": \"Manual resume requested from /full-automation --chain-run-id.\"\n}\n```\n\n### Stage 2c — Envelope loop\n\nFor each envelope returned by `run_full_automation` / `resume_full_automation`, dispatch on `status` / `next_action.kind`:\n\n- `status: \"failed\"` → stop chain progression and render the final report (Stage 3) with the failure status. Do **not** advance to any later stage.\n- `status: \"completed\"` or `next_action.kind: \"complete\"` → render the final report (Stage 3).\n- `status: \"needs_agent_task\"` with `next_action.kind: \"agent_task\"` → display the envelope `preamble`, perform the agent task exactly as the `next_action.instruction` directs, then call `resume_full_automation` with `chain_run_id` set to the envelope's `chain_run_id` and `agent_result` set to the resulting text. Loop back and process the next envelope.\n\nSpecial case — the stage-3 handoff: when the agent-task instruction names a `/start-tickets ...` command, invoke that slash command in **this same session**, summarize the outcome in one line, and pass that one-line summary as `agent_result` to `resume_full_automation`.\n\nConstraints:\n- On your own initiative, the skill must **not** call any Bridge API MCP tool other than `run_full_automation` / `resume_full_automation` — in particular, never independently drive orchestration (`run_pipeline`, `resume_pipeline`, `get_pipeline_recipe`) or enrich tickets (`get_ticket`, `update_ticket_description`, etc.). **However, when a `needs_agent_task` instruction returned by the server explicitly directs you to call a specific Bridge API MCP tool** (for example an orchestrator-directed `get_tickets`, `create_ticket`, `upload_attachment`, or `track_ticket`), **you must invoke that tool exactly as instructed** — performing an orchestrator-directed agent task is not re-orchestrating.\n- If a v1 envelope unexpectedly returns `next_action.kind: \"mcp_call\"`, stop with a clear protocol error instead of bypassing the server-side orchestrator:\n ```text\n Protocol error: chain returned next_action.kind \"mcp_call\", which is out of scope for /full-automation v1. Stopping.\n ```\n\n## Stage 3 — Final report\n\nWhen the chain completes or fails, render this skeleton verbatim:\n\n```markdown\n## Full Automation Complete\n\nChain run: <chain_run_id>\nIdea: <first 80 chars of idea>...\nStages:\n 1. idea-to-ticket: <stages[0].summary>\n 2. review-ticket: <stages[1].summary>\n 3. start-tickets: <stages[2].summary>\n\nTotal Jira tickets created: N\nTotal worktrees spawned: M\nStatus: Success / Failed at stage N — <reason>\n```\n\n- Stage summaries come from the chain envelope or manifest when present.\n- When the completed envelope does not include full stage objects, use the summaries already surfaced in the prior `preamble` text rather than calling additional tools.\n- A stage-1 `too_vague_to_ticket` failure must render the upstream halt reason and set `Status: Failed at stage 1 — <reason>`.\n- Failed chains must not advance to later stages after a failed envelope is received.\n",
|
|
12
|
+
"idea-to-ticket.md": "Convert a short human idea into a Jira ticket (or Epic plus child tickets) via the server-side idea-to-ticket pipeline.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly other than `get_pipeline_recipe` — the recipe determines which tools to call and with what parameters.\n\n## Stage 0 — Parse arguments\n\n1. Tokenize `$ARGUMENTS` on whitespace. Recognize the following position-independent flags (any other tokens are part of the idea):\n - `--allow-duplicate`\n - `--max-children=N` where `N` is a positive integer\n - `--auto-approve-external`\n\n Flags are optional. Treat absence of a flag as the default; never pass empty-string or null as a placeholder.\n\n2. Everything that is not a recognized flag is the free-form `idea` text. Join those tokens back together preserving order. Trim surrounding whitespace.\n\n3. If the resulting `idea` is empty, stop immediately and display:\n ```\n Usage: /idea-to-ticket <idea> [--allow-duplicate] [--max-children=N] [--auto-approve-external]\n ```\n\n## Stage 1 — Derive pipeline variables\n\n4. Derive `slug` from the first 6-8 meaningful words of the idea: lowercase, kebab-case, strip non-alphanumeric characters except hyphens, and truncate to roughly 60 characters. Skip stop-words such as \"the\", \"a\", \"an\" when picking the 6-8 meaningful words.\n\n5. Derive `run_id` as `<YYYYMMDD-HHMMSS>-<short-uuid>` using the current UTC time and a short UUID suffix (8 hex chars is enough). The combination of `slug` and `run_id` uniquely identifies this run's artifact directory.\n\n6. Derive the boolean-as-string variables:\n - `allow_duplicate` is `\"true\"` if `--allow-duplicate` was present, otherwise `\"false\"`.\n - `auto_approve_external` is `\"true\"` if `--auto-approve-external` was present, otherwise `\"false\"`.\n - `max_children` is the integer following `--max-children=` as a string, or `\"10\"` when the flag is absent.\n\n## Stage 2 — Call the recipe\n\n7. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"idea-to-ticket\"`\n - `variables`: `{ \"idea\": \"<idea>\", \"slug\": \"<slug>\", \"run_id\": \"<run_id>\", \"allow_duplicate\": \"<allow_duplicate>\", \"auto_approve_external\": \"<auto_approve_external>\", \"max_children\": \"<max_children>\" }`\n\n Do NOT pass `docs_dir` or `idea_hash` in variables — both are auto-injected by the pipeline system (`docs_dir` from `BAPI_DOCS_DIR`; `idea_hash` is a stable hash derived from the `idea`).\n\n If the tool returns an error, stop and report the failure.\n\n8. 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\n## Stage 3 — Final summary\n\n9. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Idea**: <first 80 characters of idea>...\n **Slug**: <slug>\n **Run directory**: <docs_dir>/idea-to-ticket/<slug>-<run_id>/\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
11
13
|
"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:\n - A required `ticket_key` matching the Jira key pattern (`[A-Z][A-Z0-9]+-\\d+`).\n - An optional position-independent `--auto-approve` flag.\n\n Tokenize `$ARGUMENTS` on whitespace. The first token matching the Jira key pattern is the `ticket_key`; ignore any additional ticket-key tokens. The presence of a `--auto-approve` token (anywhere in `$ARGUMENTS`) sets `auto_approve` to `true`.\n\n If `$ARGUMENTS` is empty or contains no token matching the Jira key pattern, stop immediately and display:\n ```\n Invalid ticket key format. Expected: PROJ-123 [--auto-approve]\n Usage: /implement-ticket <ticket_key> [--auto-approve]\n ```\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"implement-ticket\"`\n - `variables`: `{ \"ticket_key\": \"<ticket_key>\" }`\n - `auto_approve`: `true` — only when `--auto-approve` was passed; otherwise omit this field entirely.\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
14
|
"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
15
|
"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 — 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",
|
|
@@ -17,6 +19,6 @@ export const COMMANDS = {
|
|
|
17
19
|
"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:\n - A required `ticket_key` matching the Jira key pattern (`[A-Z][A-Z0-9]+-\\d+`).\n - An optional position-independent `--auto-approve` flag.\n\n Tokenize `$ARGUMENTS` on whitespace. The first token matching the Jira key pattern is the `ticket_key`; ignore any additional ticket-key tokens. The presence of a `--auto-approve` token (anywhere in `$ARGUMENTS`) sets `auto_approve` to `true`.\n\n If `$ARGUMENTS` is empty or contains no token matching the Jira key pattern, stop immediately and display:\n ```\n Invalid ticket key format. Expected: PROJ-123 [--auto-approve]\n Usage: /review-ticket <ticket_key> [--auto-approve]\n ```\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"review-ticket\"`\n - `variables`: `{ \"ticket_key\": \"<ticket_key>\" }`\n - `auto_approve`: `true` — only when `--auto-approve` was passed; otherwise omit this field entirely.\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",
|
|
18
20
|
"run-tests.md": "Run the project's full test suite (unit and E2E) using the project-configured test stacks, triage failures, fix test-code issues, and produce a structured health-check report.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command discovers how to run tests by reading per-project configuration from the Bridge API, not from hardcoded paths. Stages run only when the project has the corresponding stack configured.\n\n## Stage 0 — Argument Parsing and Setup\n\n1. **Parse `$ARGUMENTS`** for optional flags. Supported flags:\n - `--skip-e2e` — skip the E2E test stage even if an E2E stack is configured (e.g., when no local server is running)\n - `--unit-only` — shorthand that implies `--skip-e2e`\n\n Resolve flags to boolean variables:\n - Start with: `run_unit = true`, `run_e2e = true`\n - If `--unit-only` is present: set `run_e2e = false`\n - If `--skip-e2e` is present: set `run_e2e = 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 — Resolve Project Config via MCP\n\nRead the per-project test setup from the Bridge database. Every subsequent stage is driven by what these calls return.\n\n1. **Resolve docs directory**: Call the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n2. **Read unit-test stack**: Call the `get_config_field` MCP tool with `field_name` set to `unit_testing_stack`. Store the returned value as `unit_stack` (may be null/empty).\n\n3. **Read unit-test instructions**: Call the `get_config_field` MCP tool with `field_name` set to `unit_testing_instructions`. Store the returned value as `unit_instructions` (may be null/empty).\n\n4. **Read E2E stack**: Call the `get_config_field` MCP tool with `field_name` set to `e2e_testing_stack`. Store as `e2e_stack`.\n\n5. **Read E2E instructions**: Call the `get_config_field` MCP tool with `field_name` set to `e2e_testing_instructions`. Store as `e2e_instructions`.\n\n6. **Compute configuration booleans**:\n - `unit_configured` = `true` if either `unit_stack` or `unit_instructions` is a non-empty string; otherwise `false`\n - `e2e_configured` = `true` if either `e2e_stack` or `e2e_instructions` is a non-empty string; otherwise `false`\n\n7. **Create the output directory**:\n ```\n mkdir -p {docs_dir}/testing/\n ```\n If this fails, stop immediately and report: `Cannot create output directory {docs_dir}/testing/ — check permissions.`\n\nIf any MCP call fails (e.g., the API is unreachable or returns 4xx/5xx), stop immediately and report which call failed. Do not fall back to hardcoded commands — the whole point of this command is that test setup lives in config.\n\n## Stage 2 — Unit / Standard Tests\n\nIf `run_unit` is `false`, skip this stage and record: `Unit tests: SKIPPED — run_unit was set to false (this should not happen in normal use; report as a bug).`\n\nIf `unit_configured` is `false`, skip and record:\n```\nUnit tests: SKIPPED — no unit_testing_stack or unit_testing_instructions configured for this repo. Configure via /learn-unit-testing or the project setup UI before running /run-tests.\n```\n\nOtherwise:\n\n1. Read `unit_instructions` carefully. It is the source of truth for **how to run unit tests in this repo** — runner binary, paths, environment activation, sub-suites (if the project distinguishes \"unit\" from \"integration\", both belong in this stage), and any flags. Pair it with `unit_stack` (a short label, e.g., `Pytest`, `Jest + React Testing Library`) for context.\n\n2. **Derive the test command(s)**: Extract the literal shell commands the instructions describe. If the instructions describe multiple sub-suites (e.g., a fast unit batch and a slower integration batch), plan to run each as a **separate batch** in the order described. Do not invent runners or paths that the instructions do not mention.\n\n3. **If the instructions do not specify any runnable command**, skip and record:\n ```\n Unit tests: SKIPPED — unit_testing_instructions does not describe how to invoke tests; please update via /learn-unit-testing.\n ```\n\n4. **Run each batch sequentially** in the terminal. **Continue to the next batch even if the current one has failures.** Capture the full output of each batch, including the runner's summary line (e.g., `47 passed, 3 failed in 12.4s` or `Tests: 5 failed, 22 passed`).\n\n5. For each failing test, apply the **Triage Logic** (below), then record the result.\n\n## Stage 3 — E2E Tests\n\nIf `run_e2e` is `false`, skip this stage and record: `E2E tests: SKIPPED — --skip-e2e or --unit-only flag was set.`\n\nIf `e2e_configured` is `false`, skip and record:\n```\nE2E tests: SKIPPED — no e2e_testing_stack or e2e_testing_instructions configured (the project may not have an E2E suite).\n```\n\nOtherwise:\n\n1. Read `e2e_instructions`. It is the source of truth for the E2E runner, spec paths, browser config, and any prerequisites. Pair with `e2e_stack` for context.\n\n2. **Detect server prerequisites**: If `e2e_instructions` indicates that a local server must be running (look for explicit cues such as \"server\", \"running\", \"localhost\", \"started\", \"dev server\", a URL, or a port number) and describes a readiness check, perform that check exactly as described. If the instructions describe a server prerequisite but do not describe a check, attempt the check the instructions imply (e.g., curl the URL the instructions mention) and skip the stage if it fails:\n ```\n E2E tests: SKIPPED — e2e_testing_instructions describe a server prerequisite that wasn't met. Start the server per the instructions and re-run.\n ```\n\n3. **Derive the test command(s)** from the instructions, including any spec-directory batching the instructions specify.\n\n4. **If the instructions do not specify any runnable command**, skip and record:\n ```\n E2E tests: SKIPPED — e2e_testing_instructions does not describe how to invoke tests; please update via /learn-e2e-testing.\n ```\n\n5. **Run each batch sequentially** in the terminal. **Continue to the next batch even if the current one has failures.** Capture the full output and summary line of each batch.\n\n6. For each failing test, apply the **Triage Logic** (below), then record the result.\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 references a removed table column, model field, or schema member\n\n**Action**: Apply a minimal, targeted fix to the test file only. Then re-run just that failing test, using the runner described in the relevant instructions field (`unit_instructions` for unit-test failures, `e2e_instructions` for E2E failures). Adapt the runner invocation that the instructions provide to target a single test, following whatever convention the instructions or stack idiomatically use.\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, 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 handler returns the wrong status code or response shape 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 modify any file outside the test directories described in `unit_testing_instructions` / `e2e_testing_instructions`. When in doubt about whether a path is test-only, treat it as production code and escalate. Record the failure in the implementation-issues document for the user to triage.\n\n## Stage 4 — Write Output Documents\n\n### Document 1: Test Run Report (always write this)\n\nWrite to: `{docs_dir}/testing/test-run-{run_timestamp}.md`\n\n```markdown\n# Test Run: {run_timestamp}\n\n## Configuration\n- Unit stack: {unit_stack or \"not configured\"}\n- E2E stack: {e2e_stack or \"not configured\"}\n- Unit tests: RUN | SKIPPED — (reason)\n- E2E tests: RUN | SKIPPED — (reason)\n\n## Unit Tests\n**Stack**: {unit_stack or \"not configured\"}\n**Result**: X passed, Y failed (sum across batches)\n\n### Batch 1: `<command>`\n**Result**: X passed, Y failed\n**Fixes applied**:\n- `path/to/test_file`: brief description of what was fixed\n- (or \"none\" if no fixes were needed)\n\n### Batch 2: `<command>`\n...\n\n**Failures escalated as implementation issues**: N\n\n## E2E Tests\n**Stack**: {e2e_stack or \"not configured\"}\n**Result**: X passed, Y failed (sum across batches)\n\n### Batch 1: `<command>`\n**Result**: X passed, Y failed\n**Fixes applied**: ...\n\n### Batch 2: `<command>`\n...\n\n**Failures escalated as implementation issues**: N\n\n## Overall Summary\n- Total test fixes applied: N\n- Suspected implementation issues found: N\n- Implementation issues document: {docs_dir}/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_dir}/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::test_function_name`\n- **Tier**: unit | e2e\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_dir}/testing/test-run-{run_timestamp}.md\nImplementation issues: {docs_dir}/testing/implementation-issues-{run_timestamp}.md (if applicable)\nNo suspected implementation issues found. (if none)\n```\n",
|
|
19
21
|
"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 — 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 2.\n\n## Stage 2 — 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 3 — 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 4 — 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",
|
|
20
|
-
"start-tickets.md": "# Start Tickets: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes one or more Jira ticket keys (e.g., `BAPI-248 BAPI-250`) and invokes the packaged `@bridge_gpt/mcp-server` CLI subcommand `start-tickets`, which creates a Worktrunk worktree for each key and opens one tab/session per worktree running the **selected agent** — Claude Code (`claude`) by default, or Cursor Agent (`cursor-agent`) via `--agent` — in a macOS Terminal/iTerm tab, a Windows Terminal tab (or PowerShell fallback window), or a detached Linux tmux session, chosen automatically by platform. It replaces Parts 2–5 of `docs/claude/parallel-worktrees.md` with a single command.\n\nBecause the orchestration ships inside the `@bridge_gpt/mcp-server` npm package (not a repo-local script), this command works for every consumer — including projects that installed the package via `--init`.\n\nStage 0 and Stage 1 are critical (stop on failure). Stage 2 is non-critical (per-ticket enrichment failures fall back to the default branch and continue). Stage 3 is critical (propagate the packaged CLI's exit code).\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline that spawns N parallel Worktrunk worktrees and selected-agent sessions (Claude Code by default) via the packaged CLI. Execute all stages in sequence directly in the main thread.\n\n## Stage 0 — Argument Parsing and Connectivity Check\n\n1. **Parse `$ARGUMENTS`** into ticket keys, pass-through flags, and branch overrides:\n - **Ticket keys**: every whitespace-separated token matching `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). If zero keys are found, stop immediately and display:\n\n ```\n No ticket keys found in arguments. Expected one or more keys like BAPI-248.\n Usage: /start-tickets [flags] <KEY> [KEY ...] (e.g., /start-tickets BAPI-248 BAPI-250)\n ```\n\n - **Pass-through flags**: collect any of `--agent <name>` (and the equals form `--agent=<name>`), `--terminal terminal|iterm`, `--dry-run`, `--no-refresh-main`, and `--max-parallel N` that the user supplied. These are forwarded verbatim to the CLI in Stage 3.\n - **Selected agent**: track a `selected_agent` variable that defaults to `claude`. If the user passed `--agent <name>` / `--agent=<name>`, validate the value against the supported agents `claude` and `cursor-agent`, set `selected_agent` to it, and reject any other (malformed/unsupported) `--agent` value before proceeding. The agent is not auto-detected from the host editor — the user selects it explicitly (default `claude`).\n - **User branch overrides**: collect any user-supplied repeatable `--branch KEY=BRANCH` flags. A user-provided override always takes precedence over Stage 2 enrichment for that key.\n - Reject malformed input before proceeding: if a token looks like a flag but is not one of the supported flags, or a ticket key does not match `[A-Z]+-[0-9]+`, or a `--branch` value is not `KEY=BRANCH`, or `--agent` names an agent other than `claude`/`cursor-agent`, stop and report the malformed argument.\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 — Acknowledge CLI Pre-flight\n\nThe packaged CLI runs its own per-platform pre-flight checks and then fetches `origin` and fast-forwards local `main` so the new worktrees are based on up-to-date `main` (skippable via `--no-refresh-main`). The required commands depend on the OS:\n\n- **macOS**: `wt`, `git`, `osascript`.\n- **Windows**: `git-wt`, `git`, Git for Windows / Git Bash (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash), and Windows Terminal **or** PowerShell.\n- **Linux**: `wt`, `git`, `tmux`.\n\nOn **Windows** the Worktrunk binary is `git-wt` (its winget alias), which is a different tool from Windows Terminal's `wt.exe`: the CLI uses `git-wt` to **create worktrees** and `wt.exe` to **open a tab**, and never conflates the two. On **Linux** the CLI opens one detached `tmux` session per ticket (a window is added if that ticket's session already exists); attach later with `tmux attach -t <session>`. An unsupported OS (not macOS/Windows/Linux) fails fast with a clear \"unsupported platform\" message.\n\nThis stage simply notes that the CLI will fail fast if any prerequisite is missing or if local `main` has diverged from `origin/main` — you do not need to verify anything separately here, and you must not run any pre-flight commands yourself. When the CLI's pre-flight fails it now hints the user to run the read-only diagnostics command `npx -y @bridge_gpt/mcp-server doctor`, which reports found/missing for every prerequisite on the current OS — the pre-flight set plus `uv` plus the selected agent's command — and prints the manual install command for each missing one. `doctor` is strictly read-only and never installs anything; never run install commands automatically on the user's behalf. The CLI does not call any Bridge API tools; all credential-bearing work (branch enrichment in Stage 2) stays in this command. Proceed to Stage 2.\n\nThis stage is **critical** in the sense that the CLI will abort if its pre-flight fails; you will see the error in Stage 3's output and must surface it.\n\n## Stage 2 — Enrich Branch Names (best-effort)\n\nBranch enrichment happens here, in the command, **before** invoking the CLI — the `get_ticket` MCP tool runs inside the MCP server process, which holds the Bridge API credentials the shell-spawned CLI does not have. For each parsed ticket key that does **not** already have a user-provided `--branch` override:\n\n1. Call the `get_ticket` MCP tool with `ticket_number` set to the key and `save_locally` set to `false`.\n2. From the response, extract the `summary` field. Slugify it: lowercase the string, replace every run of non-alphanumeric characters (`[^a-z0-9]+`) with a single dash `-`, trim leading and trailing dashes, and truncate to at most `40` characters (cutting at a dash boundary if possible).\n3. The enriched branch name is `feature/<KEY>-<slug>`. Example: `BAPI-248` with summary `\"Add PR rating pre-evaluation step\"` becomes `feature/BAPI-248-add-pr-rating-pre-evaluation-step` (trimmed at 40 chars).\n4. If the `get_ticket` call fails for a particular key (404, network error, missing summary) or produces an empty slug, emit a single-line warning like `Warning: could not enrich BAPI-248, falling back to feature/BAPI-248` and let the CLI apply its default `feature/<KEY>` for that key only. Do NOT stop the pipeline.\n5. Build a list of `--branch <KEY>=<BRANCH>` arguments — one entry per key whose enrichment succeeded — and merge it with any user-provided overrides from Stage 0. **Do not** call `get_ticket` for keys that already have a user-provided override; those overrides win.\n\nThis stage is **non-critical** — warnings are acceptable, the pipeline continues with the fallback default for any key that fails. Do not call the Bridge API from the CLI itself; the CLI never has credentials.\n\n## Stage 3 — Invoke the Packaged CLI\n\nUse the **Bash tool** to invoke the packaged CLI. Build the command line as:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets <pass-through-flags> <branch-overrides> <ticket-keys>\n```\n\nWhere:\n- `<pass-through-flags>` are the supported flags collected in Stage 0 (`--agent`, `--terminal`, `--dry-run`, `--no-refresh-main`, `--max-parallel`), forwarded verbatim. Forward `--agent <name>` only if the user supplied it; otherwise omit it and the CLI defaults to `claude`.\n- `<branch-overrides>` is the list of `--branch KEY=BRANCH` flags assembled in Stage 2 (enrichment results merged with user overrides; omit any key whose enrichment failed and had no user override).\n- `<ticket-keys>` is the original list of ticket keys parsed in Stage 0, space-separated and in the original order.\n\nExample for two tickets after successful enrichment, throttled to 2 concurrent worktrees:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets \\\n --max-parallel 2 \\\n --branch BAPI-248=feature/BAPI-248-add-pr-rating-pre-evaluation-step \\\n --branch BAPI-250=feature/BAPI-250-deep-research-durability \\\n BAPI-248 BAPI-250\n```\n\nExample launching Cursor Agent instead of the default Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\nPass through the CLI's stdout and stderr to the user verbatim. If the CLI exits non-zero, treat that as a critical failure: report the exit code and the CLI's error output, and stop.\n\nThis stage is **critical** — propagate any non-zero exit from the packaged CLI.\n\n## Stage 4 — Final Report\n\nOnce the CLI exits 0, parse its `Summary` section (one stable line per ticket in the form `KEY branch=BRANCH status=STATUS`, with an optional trailing `path=PATH`) and reformat it as a markdown table:\n\n```\n| Ticket | Branch | Status |\n|----------|-----------------------------------------------------|----------|\n| BAPI-248 | feature/BAPI-248-add-pr-rating-pre-evaluation-step | spawned |\n| BAPI-250 | feature/BAPI-250-deep-research-durability | spawned |\n```\n\nStatus values are `dry-run`, `spawned`, `create-failed`, and `spawn-failed`. End the report with the worktree-first explanation, rendered for the tracked `selected_agent`. When `selected_agent` is `claude` (the default):\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`claude '/implement-ticket <KEY>'` inside its already-created worktree, which launches\nClaude Code with the starter prompt as its first message. Switch to each tab — or on\nLinux run `tmux attach -t <session>` — to monitor.\n```\n\nWhen `selected_agent` is `cursor-agent`, render the same explanation but with the Cursor handoff — do **not** claim it launches Claude Code:\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`cursor-agent '/implement-ticket <KEY>'` inside its already-created worktree, which\nlaunches Cursor Agent with the starter prompt as its first message. Switch to each\ntab — or on Linux run `tmux attach -t <session>` — to monitor.\n```\n\nThe `/implement-ticket <KEY>` prompt is identical for both agents; only the launched command differs.\n\nIf the CLI reported any `create-failed` or `spawn-failed` statuses, or Stage 2 emitted any enrichment warnings, list them under a `Warnings:` heading at the bottom of the report. If there were none, omit that section.\n\nSee `docs/claude/parallel-worktrees.md` for the deep-dive runbook and the Worktrunk verification result behind this worktree-first model.\n",
|
|
22
|
+
"start-tickets.md": "# Start Tickets: $ARGUMENTS\n\n$ARGUMENTS\n\nThis command takes one or more Jira ticket keys (e.g., `BAPI-248 BAPI-250`) and invokes the packaged `@bridge_gpt/mcp-server` CLI subcommand `start-tickets`, which creates a Worktrunk worktree for each key and opens one tab/session per worktree running the **selected agent** — Claude Code (`claude`) by default, or Cursor Agent (`cursor-agent`) via `--agent` — in a macOS Terminal/iTerm tab, a Windows Terminal tab (or PowerShell fallback window), or a detached Linux tmux session, chosen automatically by platform. It replaces Parts 2–5 of `docs/claude/parallel-worktrees.md` with a single command.\n\nBecause the orchestration ships inside the `@bridge_gpt/mcp-server` npm package (not a repo-local script), this command works for every consumer — including projects that installed the package via `--init`.\n\nStage 0 and Stage 1 are critical (stop on failure). Stage 2 is non-critical (per-ticket enrichment failures fall back to the default branch and continue). Stage 3 is critical (propagate the packaged CLI's exit code).\n\n---\n\n# Instructions\n\nYou are executing a 4-stage pipeline that spawns N parallel Worktrunk worktrees and selected-agent sessions (Claude Code by default) via the packaged CLI. Execute all stages in sequence directly in the main thread.\n\n## Stage 0 — Argument Parsing and Connectivity Check\n\n1. **Parse `$ARGUMENTS`** into ticket keys, pass-through flags, and branch overrides:\n - **Ticket keys**: every whitespace-separated token matching `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). If zero keys are found, stop immediately and display:\n\n ```\n No ticket keys found in arguments. Expected one or more keys like BAPI-248.\n Usage: /start-tickets [flags] <KEY> [KEY ...] (e.g., /start-tickets BAPI-248 BAPI-250)\n ```\n\n - **Pass-through flags**: collect any of `--agent <name>` (and the equals form `--agent=<name>`), `--terminal terminal|iterm`, `--dry-run`, `--auto-approve`, `--no-refresh-main`, `--base-branch <branch>` (and the equals form `--base-branch=<branch>`), and `--max-parallel N` that the user supplied. These are forwarded verbatim to the CLI in Stage 3. `--auto-approve` makes each spawned agent run `/implement-ticket <KEY> --auto-approve` (hands-off implementation); omit it to keep the implementation agents interactive.\n - **Selected agent**: track a `selected_agent` variable that defaults to `claude`. If the user passed `--agent <name>` / `--agent=<name>`, validate the value against the supported agents `claude` and `cursor-agent`, set `selected_agent` to it, and reject any other (malformed/unsupported) `--agent` value before proceeding. The agent is not auto-detected from the host editor — the user selects it explicitly (default `claude`).\n - **User-supplied base branch**: track a `user_supplied_base_branch` boolean that defaults to `false`. If the user passed `--base-branch <branch>` or `--base-branch=<branch>`, set the boolean to `true` and capture the value. A user-supplied `--base-branch` value **takes precedence** over any value resolved from Bridge API config in Stage 2. Validate the user-supplied value before proceeding: after trimming surrounding whitespace it must be non-empty, at most 255 characters, must not start with `-`, and must not contain ASCII control characters (`0x00`–`0x1F` or `0x7F`); reject any malformed value with a clear error.\n - **User branch overrides**: collect any user-supplied repeatable `--branch KEY=BRANCH` flags. A user-provided override always takes precedence over Stage 2 enrichment for that key.\n - Reject malformed input before proceeding: if a token looks like a flag but is not one of the supported flags, or a ticket key does not match `[A-Z]+-[0-9]+`, or a `--branch` value is not `KEY=BRANCH`, or `--agent` names an agent other than `claude`/`cursor-agent`, or `--base-branch` fails the validation rules above, stop and report the malformed argument.\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 — Acknowledge CLI Pre-flight\n\nThe packaged CLI runs its own per-platform pre-flight checks and then fetches `origin` and fast-forwards the local **configured base branch** (the value resolved in Stage 2 below, or `main` when none is configured) from `origin/<base>` so the new worktrees are based on an up-to-date base. The historical flag `--no-refresh-main` still controls this behavior — the flag name is preserved for backward compatibility, but it now skips refresh of whatever base branch resolves (default `main`). The required commands depend on the OS:\n\n- **macOS**: `wt`, `git`, `osascript`.\n- **Windows**: `git-wt`, `git`, Git for Windows / Git Bash (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash), and Windows Terminal **or** PowerShell.\n- **Linux**: `wt`, `git`, `tmux`.\n\nOn **Windows** the Worktrunk binary is `git-wt` (its winget alias), which is a different tool from Windows Terminal's `wt.exe`: the CLI uses `git-wt` to **create worktrees** and `wt.exe` to **open a tab**, and never conflates the two. On **Linux** the CLI opens one detached `tmux` session per ticket (a window is added if that ticket's session already exists); attach later with `tmux attach -t <session>`. An unsupported OS (not macOS/Windows/Linux) fails fast with a clear \"unsupported platform\" message.\n\nThis stage simply notes that the CLI will fail fast if any prerequisite is missing or if local `main` has diverged from `origin/main` — you do not need to verify anything separately here, and you must not run any pre-flight commands yourself. When the CLI's pre-flight fails it now hints the user to run the read-only diagnostics command `npx -y @bridge_gpt/mcp-server doctor`, which reports found/missing for every prerequisite on the current OS — the pre-flight set plus `uv` plus the selected agent's command — and prints the manual install command for each missing one. `doctor` is strictly read-only and never installs anything; never run install commands automatically on the user's behalf. The CLI does not call any Bridge API tools; all credential-bearing work (branch enrichment in Stage 2) stays in this command. Proceed to Stage 2.\n\nThis stage is **critical** in the sense that the CLI will abort if its pre-flight fails; you will see the error in Stage 3's output and must surface it.\n\n## Stage 2 — Resolve Base Branch + Enrich Branch Names (best-effort)\n\n### Stage 2a — Resolve configured `base_branch`\n\nThe CLI must be told which branch to cut new worktrees from. Resolution order:\n\n1. If `user_supplied_base_branch` from Stage 0 is `true`, **skip the config-field lookup entirely** and use the user-supplied value. The user's explicit `--base-branch` always wins; never call `get_config_field` for `base_branch` in that case.\n2. Otherwise, call the `get_config_field` MCP tool with `field_name` set to `base_branch` (do not pass any other parameters; the tool resolves the repository from the MCP server's configured `BAPI_REPO_NAME`).\n3. Parse the response. Treat the result as the **configured base branch** only when the response is a JSON object whose `value` field is a non-empty string after trimming surrounding whitespace.\n4. Treat **all** of the following as \"unset\" — emit a single-line warning like `Warning: base_branch is unset; CLI will default to main` and **omit** the `--base-branch` flag entirely from the Stage 3 command (the CLI's own default is `main`):\n - `value` is `null`.\n - `value` is an empty string or a whitespace-only string.\n - The endpoint returns HTTP `400` (invalid field — happens before the registry includes `base_branch`).\n - The tool returns a network error, timeout, or non-JSON parse failure.\n - Any other lookup failure.\n5. When the configured value is usable, capture it in a `resolved_base_branch` variable. **Do not** stop the pipeline on a lookup failure; fall through to the CLI default.\n\nWhen forwarding `resolved_base_branch` into the Bash invocation in Stage 3, **shell-escape it safely**: replace every literal single quote `'` in the value with the four-character sequence `'\\''`, then wrap the entire resulting string in single quotes (so the final argument looks like `'<escaped-value>'`). This is the standard POSIX single-quote escaping rule and is **mandatory** because `base_branch` is admin-configurable data that gets interpolated into a Bash command string; any unescaped single quote would otherwise break out of the surrounding quotes. Pass `--base-branch '<escaped-value>'` to the CLI as a single argv element — never expand the value unquoted into the command line.\n\n### Stage 2b — Enrich Branch Names\n\nBranch enrichment happens here, in the command, **before** invoking the CLI — the `get_ticket` MCP tool runs inside the MCP server process, which holds the Bridge API credentials the shell-spawned CLI does not have. For each parsed ticket key that does **not** already have a user-provided `--branch` override:\n\n1. Call the `get_ticket` MCP tool with `ticket_number` set to the key and `save_locally` set to `false`.\n2. From the response, extract the `summary` field. Slugify it: lowercase the string, replace every run of non-alphanumeric characters (`[^a-z0-9]+`) with a single dash `-`, trim leading and trailing dashes, and truncate to at most `40` characters (cutting at a dash boundary if possible).\n3. The enriched branch name is `feature/<KEY>-<slug>`. Example: `BAPI-248` with summary `\"Add PR rating pre-evaluation step\"` becomes `feature/BAPI-248-add-pr-rating-pre-evaluation-step` (trimmed at 40 chars).\n4. If the `get_ticket` call fails for a particular key (404, network error, missing summary) or produces an empty slug, emit a single-line warning like `Warning: could not enrich BAPI-248, falling back to feature/BAPI-248` and let the CLI apply its default `feature/<KEY>` for that key only. Do NOT stop the pipeline.\n5. Build a list of `--branch <KEY>=<BRANCH>` arguments — one entry per key whose enrichment succeeded — and merge it with any user-provided overrides from Stage 0. **Do not** call `get_ticket` for keys that already have a user-provided override; those overrides win.\n\nThis stage is **non-critical** — warnings are acceptable, the pipeline continues with the fallback default for any key that fails. Do not call the Bridge API from the CLI itself; the CLI never has credentials.\n\n## Stage 3 — Invoke the Packaged CLI\n\nUse the **Bash tool** to invoke the packaged CLI. Build the command line as:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets <pass-through-flags> <base-branch-flag> <branch-overrides> <ticket-keys>\n```\n\nWhere:\n- `<pass-through-flags>` are the supported flags collected in Stage 0 (`--agent`, `--terminal`, `--dry-run`, `--auto-approve`, `--no-refresh-main`, `--max-parallel`), forwarded verbatim. Forward `--agent <name>` only if the user supplied it; otherwise omit it and the CLI defaults to `claude`. Forward `--auto-approve` only if the user supplied it.\n- `<base-branch-flag>` is `--base-branch '<escaped-value>'` (single-quoted using the Stage 2a escaping rule) **only when** the user supplied `--base-branch` in Stage 0 **or** Stage 2a's `get_config_field` lookup returned a non-empty configured value. When the configured value is unset / lookup fails / user did not supply one, **omit this flag entirely** so the CLI's own default (`main`) takes effect.\n- `<branch-overrides>` is the list of `--branch KEY=BRANCH` flags assembled in Stage 2 (enrichment results merged with user overrides; omit any key whose enrichment failed and had no user override).\n- `<ticket-keys>` is the original list of ticket keys parsed in Stage 0, space-separated and in the original order.\n\nExample for two tickets after successful enrichment, throttled to 2 concurrent worktrees:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets \\\n --max-parallel 2 \\\n --branch BAPI-248=feature/BAPI-248-add-pr-rating-pre-evaluation-step \\\n --branch BAPI-250=feature/BAPI-250-deep-research-durability \\\n BAPI-248 BAPI-250\n```\n\nExample launching Cursor Agent instead of the default Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\nExample cutting worktrees from a non-`main` base (either user-supplied via `--base-branch develop` in Stage 0 or resolved from Bridge API config in Stage 2a):\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --base-branch develop BAPI-248\n```\n\nPass through the CLI's stdout and stderr to the user verbatim. If the CLI exits non-zero, treat that as a critical failure: report the exit code and the CLI's error output, and stop.\n\nThis stage is **critical** — propagate any non-zero exit from the packaged CLI.\n\n## Stage 4 — Final Report\n\nOnce the CLI exits 0, parse its `Summary` section (one stable line per ticket in the form `KEY branch=BRANCH status=STATUS`, with an optional trailing `path=PATH`) and reformat it as a markdown table:\n\n```\n| Ticket | Branch | Status |\n|----------|-----------------------------------------------------|----------|\n| BAPI-248 | feature/BAPI-248-add-pr-rating-pre-evaluation-step | spawned |\n| BAPI-250 | feature/BAPI-250-deep-research-durability | spawned |\n```\n\nStatus values are `dry-run`, `spawned`, `create-failed`, and `spawn-failed`. End the report with the worktree-first explanation, rendered for the tracked `selected_agent`. When `selected_agent` is `claude` (the default):\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`claude '/implement-ticket <KEY>'` inside its already-created worktree, which launches\nClaude Code with the starter prompt as its first message. Switch to each tab — or on\nLinux run `tmux attach -t <session>` — to monitor.\n```\n\nWhen `selected_agent` is `cursor-agent`, render the same explanation but with the Cursor handoff — do **not** claim it launches Claude Code:\n\n```\nThe CLI created/switched each Worktrunk worktree first (throttled by --max-parallel),\nthen opened one tab/session per successful worktree (macOS Terminal/iTerm tab, Windows\nTerminal tab or PowerShell window, or Linux tmux session). Each one runs\n`cursor-agent '/implement-ticket <KEY>'` inside its already-created worktree, which\nlaunches Cursor Agent with the starter prompt as its first message. Switch to each\ntab — or on Linux run `tmux attach -t <session>` — to monitor.\n```\n\nThe `/implement-ticket <KEY>` prompt is identical for both agents; only the launched command differs. When start-tickets was invoked with `--auto-approve`, the spawned prompt is `/implement-ticket <KEY> --auto-approve` (the implementation pipeline runs hands-off, without approval gates).\n\nIf the CLI reported any `create-failed` or `spawn-failed` statuses, or Stage 2 emitted any enrichment warnings, list them under a `Warnings:` heading at the bottom of the report. If there were none, omit that section.\n\nSee `docs/claude/parallel-worktrees.md` for the deep-dive runbook and the Worktrunk verification result behind this worktree-first model.\n",
|
|
21
23
|
"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. **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"
|
|
22
24
|
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-rolled global ``fetch`` stub for the pipeline-orchestrator tests.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the undici MockAgent/setGlobalDispatcher mocking that broke when
|
|
5
|
+
* Dependabot bumped undici past Node's bundled copy. Instead of intercepting
|
|
6
|
+
* Node's internal dispatcher, we swap ``globalThis.fetch`` itself: the stub
|
|
7
|
+
* matches registered routes by method + path (string-exact or RegExp) +
|
|
8
|
+
* optional query subset, records every call for assertions, and throws loudly
|
|
9
|
+
* on any unmatched request so nothing escapes to the real network (the
|
|
10
|
+
* ``agent.disableNetConnect()`` equivalent). It depends only on Node's global
|
|
11
|
+
* ``Response``/``Headers``/``URL`` — no package required, and no coupling to
|
|
12
|
+
* Node's bundled undici version.
|
|
13
|
+
*
|
|
14
|
+
* Two registration styles mirror undici's ``.intercept().reply(...)``:
|
|
15
|
+
* - ``replyOnce(spec, status, body, responseOptions?)`` — a one-shot route
|
|
16
|
+
* consumed FIFO; matches a single request, like an undici interceptor. Used
|
|
17
|
+
* by the persistence and execution tests (including tests that register two
|
|
18
|
+
* responses for the same method+path to be returned in order).
|
|
19
|
+
* - ``route(spec, handler)`` — a persistent route whose handler runs on every
|
|
20
|
+
* matching request. The handler receives the captured call and returns
|
|
21
|
+
* ``{ statusCode, data, responseOptions }`` — the same shape undici's
|
|
22
|
+
* ``reply((opts) => ...)`` callback returned — so stateful fakes port over
|
|
23
|
+
* almost verbatim.
|
|
24
|
+
*/
|
|
25
|
+
export class FetchStub {
|
|
26
|
+
original;
|
|
27
|
+
routes = [];
|
|
28
|
+
/** Every request seen since the last ``reset()``, in order. */
|
|
29
|
+
calls = [];
|
|
30
|
+
/** Replace ``globalThis.fetch`` with the stub. */
|
|
31
|
+
install() {
|
|
32
|
+
this.original = globalThis.fetch;
|
|
33
|
+
globalThis.fetch = ((input, init) => this.handle(input, init));
|
|
34
|
+
}
|
|
35
|
+
/** Restore the original ``globalThis.fetch``. */
|
|
36
|
+
restore() {
|
|
37
|
+
if (this.original)
|
|
38
|
+
globalThis.fetch = this.original;
|
|
39
|
+
this.original = undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Clear all registered routes and recorded calls (per-test reset). */
|
|
42
|
+
reset() {
|
|
43
|
+
this.routes = [];
|
|
44
|
+
this.calls = [];
|
|
45
|
+
}
|
|
46
|
+
/** Register a persistent route that matches every qualifying request. */
|
|
47
|
+
route(spec, handler) {
|
|
48
|
+
this.routes.push({ spec, handler, once: false, consumed: false });
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register a one-shot route consumed FIFO — mirrors a single undici
|
|
53
|
+
* ``.intercept(spec).reply(status, body, responseOptions)``. Object bodies
|
|
54
|
+
* are JSON-serialized (and default to ``content-type: application/json``
|
|
55
|
+
* unless the caller set one); string bodies pass through untouched.
|
|
56
|
+
*/
|
|
57
|
+
replyOnce(spec, status, body, responseOptions) {
|
|
58
|
+
const isObjectBody = body !== undefined && body !== null && typeof body === "object";
|
|
59
|
+
const data = body === undefined || body === null
|
|
60
|
+
? ""
|
|
61
|
+
: typeof body === "string"
|
|
62
|
+
? body
|
|
63
|
+
: JSON.stringify(body);
|
|
64
|
+
const headers = { ...(responseOptions?.headers ?? {}) };
|
|
65
|
+
if (isObjectBody &&
|
|
66
|
+
!Object.keys(headers).some((k) => k.toLowerCase() === "content-type")) {
|
|
67
|
+
headers["content-type"] = "application/json";
|
|
68
|
+
}
|
|
69
|
+
this.routes.push({
|
|
70
|
+
spec,
|
|
71
|
+
handler: () => ({ statusCode: status, data, responseOptions: { headers } }),
|
|
72
|
+
once: true,
|
|
73
|
+
consumed: false,
|
|
74
|
+
});
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
handle(input, init) {
|
|
78
|
+
const opts = (init ?? {});
|
|
79
|
+
const rawUrl = typeof input === "string"
|
|
80
|
+
? input
|
|
81
|
+
: input instanceof URL
|
|
82
|
+
? input.toString()
|
|
83
|
+
: String(input?.url ?? input);
|
|
84
|
+
const url = new URL(rawUrl);
|
|
85
|
+
const method = (opts.method ?? "GET").toUpperCase();
|
|
86
|
+
const headers = new Headers(opts.headers ?? {});
|
|
87
|
+
const body = opts.body === undefined || opts.body === null
|
|
88
|
+
? undefined
|
|
89
|
+
: String(opts.body);
|
|
90
|
+
const call = {
|
|
91
|
+
url,
|
|
92
|
+
path: url.pathname + url.search,
|
|
93
|
+
method,
|
|
94
|
+
headers,
|
|
95
|
+
body,
|
|
96
|
+
json: () => JSON.parse(body ?? ""),
|
|
97
|
+
};
|
|
98
|
+
this.calls.push(call);
|
|
99
|
+
for (const r of this.routes) {
|
|
100
|
+
if (r.once && r.consumed)
|
|
101
|
+
continue;
|
|
102
|
+
if (!FetchStub.matches(r.spec, call))
|
|
103
|
+
continue;
|
|
104
|
+
if (r.once)
|
|
105
|
+
r.consumed = true;
|
|
106
|
+
const result = r.handler(call);
|
|
107
|
+
return Promise.resolve(new Response(result.data ?? "", {
|
|
108
|
+
status: result.statusCode,
|
|
109
|
+
headers: result.responseOptions?.headers ?? {},
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
// No match: emulate disableNetConnect() — fail loudly, never hit the net.
|
|
113
|
+
const registered = this.routes
|
|
114
|
+
.map((r) => `${(r.spec.method ?? "GET").toUpperCase()} ${String(r.spec.path ?? "*")}`)
|
|
115
|
+
.join(", ");
|
|
116
|
+
throw new Error(`FetchStub: no route matched ${method} ${call.path} ` +
|
|
117
|
+
`(net connect is disabled). Registered routes: [${registered}]`);
|
|
118
|
+
}
|
|
119
|
+
static matches(spec, call) {
|
|
120
|
+
if ((spec.method ?? "GET").toUpperCase() !== call.method)
|
|
121
|
+
return false;
|
|
122
|
+
if (spec.path !== undefined) {
|
|
123
|
+
if (typeof spec.path === "string") {
|
|
124
|
+
if (spec.path !== call.url.pathname)
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
else if (!spec.path.test(call.url.pathname)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (spec.query) {
|
|
132
|
+
for (const [k, v] of Object.entries(spec.query)) {
|
|
133
|
+
if (call.url.searchParams.get(k) !== v)
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|