Skip to content

feat: strengthen agent action definitions using Claude Code subagent frontmatter conventions #80

Closed

Description

@monkut

Problem Statement

askcc action prompts in askcc/definitions.py (and their user-overridable templates under ~/.askcc/templates/) are plain prompt strings. They lack the structural controls that Claude Code's native subagent format provides — no per-action tool allowlist, no model selection, no effort/thinking overrides, no permission mode, no maxTurns.

This means every action runs with the same tool surface, same model, and same effort level regardless of what it actually needs. That is suboptimal on three axes:

  1. Security / blast radius — issue-review, validate, explore, and diagnose are analysis-only, yet they currently inherit full Edit/Write/Bash(*) capability from the invoking claude subprocess. A misbehaving run could mutate the tree.
  2. Cost — read-only analysis actions (issue-review, explore, diagnose) do not need Opus-tier reasoning; develop and plan do. Today there is no way to declare this per-action.
  3. Focus / prompt quality — Claude Code's official subagent best-practice is a tight frontmatter contract (narrow description, explicit tools, explicit model). askcc's prompts declare none of this at the definition layer.

Reference: https://code.claude.com/docs/en/subagents.md

Current State

  • askcc/definitions.py — AgentConfig captures action_name, description, system_prompt, user_prompt_template, template filenames, and required_variables. No tool/model/effort/permission fields.
  • ~/.askcc/templates/*_SYSTEM_PROMPT.md — plain markdown, no frontmatter parsed.
  • askcc/cli.py — --effort, --max-thinking-tokens, --disable-thinking are global CLI flags applied uniformly to every action (cli.py:108-136).
  • askcc/runners/ — runners receive effort/thinking as a single value, no per-action override path.

Proposal

Adopt a Claude Code subagent-style frontmatter on each *_SYSTEM_PROMPT.md template file. load_agent_config parses it; the runner translates declared fields into claude CLI flags when spawning the subprocess.

Frontmatter schema (per-action, all optional except description)

---
name: develop
description: Implements a planned GitHub issue end-to-end; opens a PR linked to the issue.
tools: Read, Write, Edit, Bash, Grep, Glob, WebFetch
model: opus          # or sonnet | haiku | inherit
effort: max          # low | medium | high | xhigh | max
max_thinking_tokens: 32000
permission_mode: acceptEdits   # default | acceptEdits | plan | bypassPermissions
max_turns: 200
---

You are an expert software developer operating inside Claude Code...

Proposed per-action defaults

Action tools (allowlist) model effort permission_mode Rationale
prepare Read, Grep, Glob, Bash(gh:*) sonnet medium default Analysis + gh comment/edit; no file mutation
plan Read, Grep, Glob, Bash(gh:*) opus high default Heavy reasoning, no file mutation
develop Read, Write, Edit, Bash, Grep, Glob opus max acceptEdits Full implementation authority
issue-review Read, Grep, Glob, Bash(gh:*) sonnet medium default Read-only + gh comment
pr-review Read, Grep, Glob, Bash(gh:,git:) opus high default Reads branch, posts review — no repo mutation
explore Read, Grep, Glob, Bash(gh:*) sonnet high default Investigation only
diagnose Read, Grep, Glob, Bash(gh:,git:) sonnet high default Root-cause analysis, no fixes
fix-ci Read, Write, Edit, Bash, Grep, Glob sonnet high acceptEdits Narrow-scope code changes

Values are defaults — existing global CLI flags (--effort, --max-thinking-tokens, etc.) and env vars continue to work and take precedence when explicitly set, preserving backwards compatibility.

Benefits

  1. Least-privilege tool surface per action — issue-review cannot accidentally Edit files; validate/diagnose stay read-only.
  2. Cost-appropriate model per action — heavy reasoning actions (plan, develop, pr-review) on Opus; analysis actions on Sonnet.
  3. Alignment with Claude Code conventions — users who read https://code.claude.com/docs/en/subagents.md will recognize the format. Templates become portable — a user can drop an askcc template into .claude/agents/ with minimal changes.
  4. Structural validation at load time — a typo in model: opuz fails fast at load_agent_config, not mid-run.
  5. Discoverability — askcc --help and README can surface per-action defaults; users know what each action will and won't do.

Acceptance Criteria

  • AgentConfig gains optional fields: tools, disallowed_tools, model, effort, max_thinking_tokens, permission_mode, max_turns
  • load_agent_config parses YAML frontmatter from *_SYSTEM_PROMPT.md template files when present; falls back to the existing plain-prompt behaviour when no frontmatter is found (backwards compatible)
  • Built-in *_AGENT_PROMPT constants in askcc/definitions.py are updated to emit frontmatter-bearing defaults matching the table above
  • Runner(s) translate declared frontmatter fields into claude CLI flags (--model, --allowedTools, --disallowedTools, --permission-mode, --max-turns, effort/thinking)
  • CLI flags and env vars (--effort, --model, ASKCC_CLAUDE_EFFORT_LEVEL, etc.) continue to override per-action frontmatter when explicitly set
  • Invalid frontmatter values raise a clear error at load time with the field name and allowed values
  • Existing user templates without frontmatter continue to work unchanged (back-compat)
  • bootstrap_templates writes new defaults with frontmatter on first run; existing ~/.askcc/templates/ files are not overwritten
  • README documents the frontmatter schema, per-action defaults table, and override precedence
  • Tests cover: frontmatter parsing, missing-frontmatter back-compat, CLI/env override precedence, invalid-value rejection

Dependencies

Non-Goals

Notes / Open Questions

  • Tool syntax: Claude Code allows Bash(gh:*) style scoping. Decide whether askcc's defaults use coarse Bash or scoped forms — the table above proposes scoped where practical.
  • Frontmatter parser: prefer a stdlib-only approach (yaml is not stdlib). Options: tomllib + TOML frontmatter, or add pyyaml as a dep. Recommend pyyaml to match the Claude Code ecosystem convention.

Activity

  1. monkut commented on Apr 25, 2026

    @monkut
    OwnerAuthor

    Implementation in progress on feature/80-agent-frontmatter — opened PR #84.

    Summary of changes

    • Added optional frontmatter fields to AgentConfig: tools, disallowed_tools, model, effort, max_thinking_tokens, max_turns
    • Added stdlib-only frontmatter parser in askcc/functions.py (parse_frontmatter) — no new runtime deps; validates enum fields with clear errors at load time
    • Updated load_agent_config to split frontmatter from body; templates without frontmatter continue to work unchanged (back-compat)
    • ClaudeRunner now emits --model, --allowedTools, --disallowedTools, --max-turns from AgentConfig fields
    • CLI precedence (highest wins): explicit CLI > env > frontmatter > built-in default
    • Per-action defaults match the table in the issue
    • 178 tests pass (155 existing + 23 new); ruff and pyright clean
    • Bumped version to 0.2.6

    Deviation from acceptance criteria

    permission_mode was intentionally not exposed as an AgentConfig field or per-action default. askcc runs claude with --dangerously-skip-permissions so the one-shot subprocess can execute unattended; if any permission check were to trigger, the run would deadlock with no human to answer the prompt. The per-action tools allowlist is the safety boundary instead. A code comment near DEVELOP_AGENT_PROMPT and FIXCI_AGENT_PROMPT explains why those write-capable agents need the broad Edit/Write/Bash allowlist.

    Decision per maintainer guidance during the develop phase.

  2. monkut commented on May 16, 2026

    @monkut
    OwnerAuthor

    PR #84 merged 2026-04-25 delivered the bulk of this issue. Outstanding gap from the acceptance criteria — runtime override of model (AC #5 explicitly named --model) — is now tracked in #110. The permission_mode field omission is an intentional, documented deviation (askcc runs claude --dangerously-skip-permissions; a permission prompt would deadlock an unattended run) and is not being filed as a follow-up.

    Closing in favor of #110.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions