Process a raw command template into agent-ready content. Performs the same transformations as the release script: 1. Extract ``scripts. `` value from YAML frontmatter 2. Replace ``{SCRIPT}`` with the extracted script command 3. Strip ``scripts:`` section
(
content: str,
agent_name: str,
script_type: str,
arg_placeholder: str = "$ARGUMENTS",
invoke_separator: str = ".",
project_root: Path | None = None,
)
| 582 | |
| 583 | @staticmethod |
| 584 | def process_template( |
| 585 | content: str, |
| 586 | agent_name: str, |
| 587 | script_type: str, |
| 588 | arg_placeholder: str = "$ARGUMENTS", |
| 589 | invoke_separator: str = ".", |
| 590 | project_root: Path | None = None, |
| 591 | ) -> str: |
| 592 | """Process a raw command template into agent-ready content. |
| 593 | |
| 594 | Performs the same transformations as the release script: |
| 595 | 1. Extract ``scripts.<script_type>`` value from YAML frontmatter |
| 596 | 2. Replace ``{SCRIPT}`` with the extracted script command |
| 597 | 3. Strip ``scripts:`` section from frontmatter |
| 598 | 4. Replace ``{ARGS}`` and ``$ARGUMENTS`` with *arg_placeholder* |
| 599 | 5. Replace ``__AGENT__`` with *agent_name* |
| 600 | 6. Rewrite paths: ``scripts/`` → ``.specify/scripts/`` etc. |
| 601 | 7. Replace ``__SPECKIT_COMMAND_<NAME>__`` with invocation strings |
| 602 | """ |
| 603 | # 1. Extract script command from frontmatter |
| 604 | script_command = "" |
| 605 | script_pattern = re.compile( |
| 606 | rf"^\s*{re.escape(script_type)}:\s*(.+)$", re.MULTILINE |
| 607 | ) |
| 608 | # Find the scripts: block |
| 609 | in_scripts = False |
| 610 | for line in content.splitlines(): |
| 611 | if line.strip() == "scripts:": |
| 612 | in_scripts = True |
| 613 | continue |
| 614 | if in_scripts and line and not line[0].isspace(): |
| 615 | in_scripts = False |
| 616 | if in_scripts: |
| 617 | m = script_pattern.match(line) |
| 618 | if m: |
| 619 | script_command = m.group(1).strip() |
| 620 | break |
| 621 | |
| 622 | # 2. Replace {SCRIPT} |
| 623 | if script_command: |
| 624 | # For the Python script type, prefix the resolved interpreter so |
| 625 | # the command is portable (``.py`` files are not directly |
| 626 | # executable on Windows). |
| 627 | if script_type == "py": |
| 628 | interpreter = IntegrationBase.resolve_python_interpreter(project_root) |
| 629 | # Quote the interpreter if it contains whitespace (e.g. an |
| 630 | # absolute ``sys.executable`` path under Windows |
| 631 | # ``Program Files``) so it isn't split into multiple args. |
| 632 | if any(ch.isspace() for ch in interpreter): |
| 633 | interpreter = f'"{interpreter}"' |
| 634 | script_command = f"{interpreter} {script_command}" |
| 635 | content = content.replace("{SCRIPT}", script_command) |
| 636 | |
| 637 | # 3. Strip scripts: section from frontmatter |
| 638 | lines = content.splitlines(keepends=True) |
| 639 | output_lines: list[str] = [] |
| 640 | in_frontmatter = False |
| 641 | skip_section = False |