/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export function storyMappingSkillMd() {
    return `---
name: story-mapping
description: >-
  Generate user story maps following the Jeff Patton format with epics, user
  stories, personas, and priorities. Render Mermaid diagrams to PDF using
  mermaid-cli with truncation-safe CSS. USE FOR: story map, user story mapping,
  story mapping, epic breakdown, backlog visualization, user stories board,
  release planning map. Reusable by BA, Architect, and Solution Analyst profiles.
---

# Story Mapping

## Prerequisites

| Tool | Check | Install |
|------|-------|---------|
| Node.js >= 18 | \`node --version\` | [https://nodejs.org](https://nodejs.org) |
| npx | \`npx --version\` | Included with Node.js |

> mermaid-cli is auto-downloaded via \`npx -y\` — no manual install needed.

## When to Use

Use this skill when the user asks to create a story map, user story board,
epic breakdown, or backlog visualization. The output follows the Jeff Patton
story mapping format: personas across the top, epics as horizontal swim-lanes,
and user stories as cards within each epic, ordered by priority.

## Story Map Template (Markdown)

Generate a structured markdown document with this format:

\`\`\`markdown
# Story Map: <Project Name>

> Author: Salesforce Professional Services | Version: 1.0

## Personas

| ID | Persona | Description |
|----|---------|-------------|
| P1 | <Name>  | <Role and key goals> |
| P2 | <Name>  | <Role and key goals> |

## Priority Legend

| Level | Label | Criteria |
|-------|-------|----------|
| P1    | Must Have   | Critical for MVP / go-live |
| P2    | Should Have | High value, deferrable one sprint |
| P3    | Nice to Have | Enhances UX, not blocking |

## Epic 1: <Epic Name>

| US ID  | User Story | Persona | Priority | Acceptance Criteria |
|--------|-----------|---------|----------|---------------------|
| US-101 | <As a P1, I want to...> | P1 | P1 | Given... When... Then... |
| US-102 | <As a P2, I want to...> | P2 | P2 | Given... When... Then... |

## Epic 2: <Epic Name>

| US ID  | User Story | Persona | Priority | Acceptance Criteria |
|--------|-----------|---------|----------|---------------------|
| US-201 | ... | ... | ... | ... |
\`\`\`

### Numbering Convention

- Epic N user stories start at \`US-N01\` (e.g., Epic 3 → US-301, US-302...).
- Always use Gherkin (Given/When/Then) for acceptance criteria.

## Mermaid Diagram Generation

After producing the markdown, generate a Mermaid flowchart for visual
representation. Use subgraphs for epics and nodes for user stories.

\`\`\`mermaid
graph LR
  subgraph epic1 [Epic 1: Authentication]
    US101["US-101: Login via SSO"]
    US102["US-102: MFA Setup"]
    US103["US-103: Password Recovery"]
  end
  subgraph epic2 [Epic 2: Product Catalog]
    US201["US-201: Browse Products"]
    US202["US-202: Search Filters"]
  end
  subgraph epic3 [Epic 3: Checkout]
    US301["US-301: Add to Cart"]
    US302["US-302: Payment Processing"]
  end
\`\`\`

### Diagram Rules

1. Node IDs must not contain spaces: use \`US101\`, not \`US 101\`.
2. Wrap labels with special characters in double quotes: \`US101["US-101: Login"]\`.
3. Use \`graph LR\` (left-to-right) for story maps; \`graph TD\` for flow diagrams.
4. Do NOT apply custom colors or styles -- let the default theme handle it.
5. Keep subgraph IDs lowercase without spaces: \`subgraph epic1 [Epic 1: Name]\`.

## Rendering to PDF

Save the Mermaid diagram to a \`.mmd\` file, then render using the included script:

\`\`\`bash
bash .setup-agents/skills/story-mapping/scripts/render-pdf.sh story-map.mmd story-map.pdf
\`\`\`

### Validation (CRITICAL)

After rendering, **always** check the output:

1. If the script exits with code 1 and prints "No diagram detected", the Mermaid
   syntax is invalid. Common fixes:
   - Ensure the file starts with a valid diagram type (\`graph\`, \`flowchart\`, \`sequenceDiagram\`).
   - Remove trailing whitespace or BOM characters.
   - Escape special characters in labels with double quotes.
2. Open the PDF and verify no content is truncated (cut off at edges).
   The included CSS (\`assets/mermaid-pdf.css\`) prevents this, but very wide
   diagrams may need \`graph TD\` instead of \`graph LR\`.
3. For markdown-embedded diagrams, verify the diagram renders in the markdown
   preview before committing.

## Lucidspark Board Generation

When generating a story map as a Lucidspark board via the Lucid MCP, apply these
constants exactly. Do NOT use assisted layout — all positions are explicit.

### Sticky note dimensions (uniform)

All sticky notes use the same size:

\`\`\`
SW  = 80    # width  (0.502" at 160 Lucid units/inch)
SH  = 75    # height (0.469")
GAP = 8     # gap between stickies
COL_STEP = SW + GAP   # 88 — horizontal step between feature columns
\`\`\`

### Layout constants

\`\`\`
SW = 80, SH = 75             # ALL stickies — uniform 0.502" × 0.469" @ 160 units/inch
GAP = 8
COL_STEP = 88                # SW + GAP

RETO_X        = 50           # purple challenge column — far left
LABEL_X       = 138          # row-type labels (Roles / Objetivos / Features / Épicas)
MAIN_X        = 226          # content columns start

Y_HDR         = 30           # actor grid + title + star legend row
Y_ROLES       = 140
Y_OBJ         = 223          # Y_ROLES + SH + GAP
Y_FEAT        = 306          # Y_OBJ + SH + GAP
Y_EPIC        = 389          # Y_FEAT + SH + GAP
Y_RETO_START  = Y_OBJ        # retos align with Objetivos row downward
\`\`\`

### Color map

| Row | Hex |
|-----|-----|
| Título principal | \`#0D2B5E\` azul oscuro |
| Roles | \`#1565C0\` azul |
| Features | \`#f19b6c\` naranja claro |
| Objetivos | \`#E53935\` rojo |
| Retos / Challenges | \`#7B1FA2\` morado |
| Épicas técnicas | \`#FFF176\` amarillo claro |

### Structural layout zones

| Zone | Position | Description |
|------|----------|-------------|
| Actor grid | (\`RETO_X\`, \`Y_HDR\`) | Colored circles, 4 per row, one per persona |
| Title | Center-top over content | Double-width sticky \`w=160\` |
| Star legend | (\`MAIN_X + content_width + 20\`, \`Y_HDR\`) | polyStar shapes + label rectangles |
| Row labels | Column at \`LABEL_X\` | "Roles", "Objetivos", "Features", "Épicas" per row |
| Retos | Column at \`RETO_X\`, from \`Y_RETO_START\` | Vertical purple stacks downward |
| Roles | Row at \`Y_ROLES\`, from \`MAIN_X\` | Blue \`#1565C0\` |
| Features | Row at \`Y_FEAT\`, from \`MAIN_X\` | Orange \`#f19b6c\` — one per column |
| Objetivos | Row at \`Y_OBJ\`, sparse | Red \`#E53935\` under relevant feature |
| Épicas | Row at \`Y_EPIC\`, sparse | Light yellow \`#FFF176\` under relevant feature |

### Standard Import shape types (validated)

| Shape | Type in Standard Import JSON | Notes |
|-------|------------------------------|-------|
| Sticky note | \`stickyNote\` | ✅ works |
| Priority star | \`polyStar\` + \`"shape": {"numPoints": 5, "innerRadius": 0.5}\` | ✅ works |
| Actor avatar | \`circle\` with colored fill + initial letter | ✅ works |
| Separator line | \`rectangle\` h=3, gray fill | ✅ works |

> **CRITICAL:** Do NOT use \`namedShape\` with \`className: "aws-general-user2017"\` (stickman icon)
> — this returns HTTP 400 from the Standard Import API. Use colored \`circle\` shapes
> with the persona initial as text instead.

### Structural rules (CRITICAL)

- **Challenges (purple):** vertical column at \`RETO_X\` on the **far left**, starting
  at \`Y_RETO_START\` (same row as Objetivos) — NOT a horizontal row.
- **Features (orange):** column headers at \`Y_FEAT\`, one per feature, horizontal from \`MAIN_X\`.
- **Objectives / Epics:** sparse, positioned under their relevant feature column.
- **Row labels:** thin column at \`LABEL_X\` labeling each horizontal row.
- Set \`product: "lucidspark"\` and \`use_assisted_layout: false\` on every
  board call — positional layout must not be auto-arranged.

## Incremental Board Edits (update / reflow modes)

The skill supports three edit modes. Always declare which mode you are using
before issuing any Lucid MCP calls.

### Mode overview

| Mode | When to use | What changes |
|------|-------------|-------------|
| \`create\` | New board from scratch | Everything — new document, all items written |
| \`update\` | Add/change/remove stories on an existing board | Only the delta (new, changed, deleted) |
| \`reflow\` | Layout restructure on an existing board | Positions and banner widths only — no content changes |

---

### Item ID Cache Contract

Every board operation must maintain a cache file alongside the story map source.
Default location: \`docs/story-map/.cache/business-story-mapping.json\`

\`\`\`json
{
  "board_id": "<lucid-document-id>",
  "layout": "horizontal-dual-layer",
  "col_step": 176,
  "features": [
    {
      "epic_id": "s57",
      "capability": "Cap-1",
      "pairs": [
        { "biz_id": "j48W", "tech_id": "s89", "x": 226, "y": 472 }
      ]
    }
  ],
  "grouping_banners": [
    { "id": "s49", "type": "capability", "label": "Cap-1 Datos Maestros", "spans_features": [0, 1, 2, 3] }
  ],
  "integration_stories": [],
  "last_synced": "2026-01-01T00:00:00Z"
}
\`\`\`

Always read this cache at the start of \`update\` and \`reflow\` operations to get
existing item IDs. Always write updated IDs back after each operation.

---

### \`update\` mode — diff and apply delta

**Step 1 — Fetch current board state**

Call \`fetch\` MCP on the board document to get the live item list.

**Step 2 — Diff desired state vs. current board**

Match stories between the markdown source and the board using:
1. Cache ID if the story was previously synced.
2. Fuzzy text match (≥ 85% similarity) as fallback.

Classify each story:
- **New** (in source, not on board) → \`lucid_add_block\`
- **Changed** (text or color differs) → \`lucid_edit_item\`
- **Removed** (on board, not in source) → **ask user for confirmation** before \`lucid_delete_items\`
- **Unchanged** → skip entirely (preserve manual positioning)

**Step 3 — Apply delta**

Issue only the minimum API calls required by the diff. Update cache after each add.

**Step 4 — Handle untracked items**

Items on the board not present in the cache and not in the source are **untracked**.
Log a warning: \`"Untracked item <id> on board — not modified. Add to cache manually if needed."\`
Do NOT delete untracked items.

---

### \`reflow\` mode — reposition without content changes

1. Load cache to get all item IDs and their feature column assignments.
2. Recalculate x/y for each item using the new \`COL_STEP\` / layout strategy.
3. Auto-resize grouping banners (see Grouping Banner Auto-Resize section).
4. Issue one \`lucid_edit_item\` call per item with updated \`x\`, \`y\`, (and \`width\` for banners).
5. Update \`col_step\` and \`layout\` in the cache.

> **CRITICAL:** Never change \`text\` or \`color\` in \`reflow\` mode — only geometry.

---

### Conflict resolution

| Conflict type | Resolution |
|--------------|-----------|
| Position differs from cache (manual Lucid edit) | In \`update\` mode: **preserve** manual position. In \`reflow\` mode: **overwrite** with recalculated position. |
| Text on board differs from source | Prefer source; log warning before overwriting. |
| Story in cache but not in source | Treat as "Removed" — ask user before deleting. |
| Story on board not in cache and not in source | Untracked — preserve, log warning. |

## Grouping Banner Auto-Resize on Layout Change

Sticky notes whose width exceeds \`COL_STEP\` (88) are **grouping banners** (capability
headers, swim-lane separators, domain dividers). When the layout spacing changes
(e.g., switching from 88px to 176px column step after a dual-layer restructure),
grouping banners must be resized to span the correct columns.

### Detection rule

\`\`\`
if sticky.width > COL_STEP:  # item is a grouping banner, not an individual story
\`\`\`

### Resize formula

Given:
- \`first_biz_x\` — x-coordinate of the leftmost feature column covered by the banner
- \`last_tech_x\` — x-coordinate of the rightmost feature column covered by the banner
- \`SW = 80\` — sticky note width

\`\`\`
new_width = (last_tech_x + SW) - first_biz_x
\`\`\`

Apply via \`lucid_edit_item\` with the \`width\` parameter:

\`\`\`json
{ "id": "<banner-id>", "x": <first_biz_x>, "y": <banner_y>, "width": <new_width>, "height": 75 }
\`\`\`

### Feature-to-capability mapping

Maintain the mapping in the story map cache (\`grouping-map.json\` alongside
\`business-story-mapping.json\`). Each entry maps a capability banner id to
the list of feature column indices it covers:

\`\`\`json
{
  "<banner-id>": { "label": "Cap-1 Datos Maestros", "featureColumns": [0, 1, 2, 3] }
}
\`\`\`

### Reflow procedure (CRITICAL)

When any layout parameter changes (\`COL_STEP\`, \`MAIN_X\`, dual-layer toggle):

1. Load \`grouping-map.json\` to get each banner's feature column span.
2. Compute \`first_biz_x = MAIN_X + (min(featureColumns) * COL_STEP)\`.
3. Compute \`last_tech_x = MAIN_X + (max(featureColumns) * COL_STEP)\`.
4. Compute \`new_width = (last_tech_x + SW) - first_biz_x\`.
5. Call \`lucid_edit_item\` for each grouping banner with the new \`x\` and \`width\`.
6. Update \`grouping-map.json\` cache with the new positions.

> **Example:** 8 capabilities × 4 features each, \`COL_STEP\` changes from 88 to 176.
> Cap-1 covering columns 0–3: \`first_biz_x = 226\`, \`last_tech_x = 226 + 3×176 = 754\`,
> \`new_width = (754 + 80) - 226 = 608\`.

## Unicode Character Safety (Lucid MCP)

The Lucid MCP server (\`https://mcp.lucid.app/mcp\`) has a known UTF-8 encoding
issue where non-ASCII characters in document titles are corrupted (e.g., em-dash
\`—\` becomes \`?\`, Spanish accented characters become garbled). Apply these
substitutions **to document titles and board names only** before sending any
MCP call — sticky note body text is generally safe.

### Character substitution table

| Original | Substitute | Note |
|----------|-----------|------|
| \`—\` U+2014 em-dash | \` - \` (space-hyphen-space) | Most common corruption source |
| \`–\` U+2013 en-dash | \`-\` | |
| \`"\` U+201C left double quote | \`"\` | |
| \`"\` U+201D right double quote | \`"\` | |
| \`'\` U+2018 left single quote | \`'\` | |
| \`'\` U+2019 right single quote | \`'\` | |
| \`…\` U+2026 ellipsis | \`...\` | |
| \`•\` U+2022 bullet | \`*\` | |
| \`°\` U+00B0 degree sign | \`deg\` | |
| \`á é í ó ú\` accented vowels | Keep as-is; verify round-trip | |
| \`ñ Ñ\` | Keep as-is; verify round-trip | |

### Verification after board creation

After creating a document via Lucid MCP, **always** fetch the document back
and compare the returned title to the intended title. If any character was
corrupted, apply additional substitutions and recreate.

\`\`\`
Expected title: "CDF - Technical Story Map"   <- use hyphen, not em-dash
Corrupted title: "CDF ? Technical Story Map"  <- em-dash was mangled
\`\`\`

## Integration with Docs

Every story map document placed in \`/docs\` must follow the documentation standard:

1. Start with the Salesforce Cloud logo header.
2. Author: **Salesforce Professional Services**.
3. Scan existing \`/docs\` files before creating -- update rather than duplicate.
`;
}
export function renderPdfScript() {
    return [
        '#!/usr/bin/env bash',
        '# Renders a Mermaid diagram (.mmd) to PDF using mermaid-cli.',
        '# Validates that the diagram was detected and parsed successfully.',
        '#',
        '# Usage: render-pdf.sh <input.mmd> [output.pdf]',
        '# Dependencies: Node.js >= 18 (npx)',
        'set -euo pipefail',
        '',
        '# ── Dependency guard ─────────────────────────────────────────────────────',
        'if ! command -v npx &>/dev/null; then',
        '  echo "ERROR: npx not found. Install Node.js >= 18 from https://nodejs.org"',
        '  exit 1',
        'fi',
        '',
        'INPUT="${1:?Usage: render-pdf.sh <input.mmd> [output.pdf]}"',
        'OUTPUT="${2:-${INPUT%.mmd}.pdf}"',
        'SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"',
        'CSS_FILE="${SCRIPT_DIR}/../assets/mermaid-pdf.css"',
        'LOG_FILE="$(mktemp /tmp/mmdc-XXXXXX.log)"',
        '',
        'cleanup() { rm -f "$LOG_FILE"; }',
        'trap cleanup EXIT',
        '',
        'if [ ! -f "$INPUT" ]; then',
        '  echo "ERROR: Input file not found: $INPUT"',
        '  exit 1',
        'fi',
        '',
        'if [ ! -f "$CSS_FILE" ]; then',
        '  echo "WARN: CSS file not found at $CSS_FILE -- rendering without custom styles."',
        '  CSS_FLAG=""',
        'else',
        '  CSS_FLAG="--cssFile $CSS_FILE"',
        'fi',
        '',
        '# ── Puppeteer browser path ────────────────────────────────────────────────',
        '# npx runs mermaid-cli in an isolated cache without access to the system',
        '# Puppeteer browser. Resolve the most recent installed Chrome for Testing.',
        'PUPPETEER_CACHE="${PUPPETEER_CACHE:-$HOME/.cache/puppeteer}"',
        'CHROME_BIN="$(find "$PUPPETEER_CACHE/chrome" -name "Google Chrome for Testing" -type f 2>/dev/null | sort -V | tail -1 || true)"',
        'if [ -n "$CHROME_BIN" ]; then',
        '  export PUPPETEER_EXECUTABLE_PATH="$CHROME_BIN"',
        'fi',
        '',
        'echo "Rendering $INPUT -> $OUTPUT ..."',
        '',
        '# shellcheck disable=SC2086',
        'npx -y @mermaid-js/mermaid-cli \\',
        '  -i "$INPUT" \\',
        '  -o "$OUTPUT" \\',
        '  $CSS_FLAG \\',
        '  --pdfFit 2>&1 | tee "$LOG_FILE"',
        '',
        'ERROR_PATTERNS="No diagram detected|Syntax error in text|Parse error|UnknownDiagramError"',
        '',
        'if grep -qiE "$ERROR_PATTERNS" "$LOG_FILE"; then',
        '  echo ""',
        '  echo "ERROR: Mermaid could not parse the diagram. Check syntax in $INPUT."',
        '  echo "Common fixes:"',
        '  echo "  - Ensure the file starts with a valid type (graph, flowchart, sequenceDiagram)."',
        '  echo "  - Remove BOM characters or trailing whitespace."',
        '  echo "  - Wrap labels with special characters in double quotes."',
        '  exit 1',
        'fi',
        '',
        'if [ ! -f "$OUTPUT" ]; then',
        '  echo "ERROR: PDF was not generated. Check mmdc output above."',
        '  exit 1',
        'fi',
        '',
        '# ── Output content validation ────────────────────────────────────────────',
        'FILESIZE=$(wc -c < "$OUTPUT" | tr -d " ")',
        'if [ "$FILESIZE" -lt 1024 ]; then',
        '  echo "WARN: Output file is suspiciously small (${FILESIZE} bytes). Verify the diagram rendered correctly."',
        'fi',
        '',
        'ERROR_PATTERNS="No diagram detected|Syntax error in text|Parse error|UnknownDiagramError"',
        '',
        'case "$OUTPUT" in',
        '  *.svg)',
        '    if grep -qiE "$ERROR_PATTERNS" "$OUTPUT" 2>/dev/null; then',
        '      echo "ERROR: Output SVG contains error markers. The diagram was not rendered correctly."',
        '      echo "Check the Mermaid syntax in $INPUT."',
        '      rm -f "$OUTPUT"',
        '      exit 1',
        '    fi',
        '    ;;',
        '  *.pdf)',
        '    if command -v pdftotext &>/dev/null; then',
        '      PDF_TEXT=$(pdftotext "$OUTPUT" - 2>/dev/null || true)',
        '      if echo "$PDF_TEXT" | grep -qiE "$ERROR_PATTERNS"; then',
        '        echo "ERROR: Output PDF contains error text. The diagram was not rendered correctly."',
        '        echo "Check the Mermaid syntax in $INPUT."',
        '        rm -f "$OUTPUT"',
        '        exit 1',
        '      fi',
        '    fi',
        '    ;;',
        'esac',
        '',
        'echo "PDF generated successfully: $OUTPUT"',
        '',
    ].join('\n');
}
export function mermaidPdfCss() {
    return `/*
 * Custom CSS for mermaid-cli PDF rendering.
 * Prevents diagram truncation and ensures full visibility in PDF output.
 */

/* Remove max-width constraint that causes truncation in PDF */
svg {
  max-width: none !important;
  width: 100% !important;
  height: auto !important;
}

/* Ensure the container does not clip overflow */
.mermaid {
  overflow: visible !important;
  max-width: none !important;
}

/* Rounded corners on nodes for readability */
.node rect,
.node polygon {
  rx: 5;
  ry: 5;
}

/* Ensure text inside nodes is not clipped */
.nodeLabel {
  overflow: visible !important;
  white-space: normal !important;
}

/* Subgraph labels (epic headers) */
.cluster-label {
  font-weight: bold;
}
`;
}
//# sourceMappingURL=story-mapping-content.js.map