Move plan skill to experimental (#22)

Rewrites `plan` skill as `create-plan` and removes all lifecycle
management skills. Moved to a new `.experimental` bucket for evaluation
and to get feedback on use.
This commit is contained in:
Gav Verma
2025-12-20 19:20:14 -08:00
committed by GitHub
parent 5a9bb4cbe6
commit 2a1cce6e76
7 changed files with 74 additions and 437 deletions
+74
View File
@@ -0,0 +1,74 @@
---
name: create-plan
description: Create a concise plan. Use when a user explicitly asks for a plan related to a coding task.
metadata:
short-description: Create a plan
---
# Create Plan
## Goal
Turn a user prompt into a **single, actionable plan** delivered in the final assistant message.
## Minimal workflow
Throughout the entire workflow, operate in read-only mode. Do not write or update files.
1. **Scan context quickly**
- Read `README.md` and any obvious docs (`docs/`, `CONTRIBUTING.md`, `ARCHITECTURE.md`).
- Skim relevant files (the ones most likely touched).
- Identify constraints (language, frameworks, CI/test commands, deployment shape).
2. **Ask follow-ups only if blocking**
- Ask **at most 12 questions**.
- Only ask if you cannot responsibly plan without the answer; prefer multiple-choice.
- If unsure but not blocked, make a reasonable assumption and proceed.
3. **Create a plan using the template below**
- Start with **1 short paragraph** describing the intent and approach.
- Clearly call out what is **in scope** and what is **not in scope** in short.
- Then provide a **small checklist** of action items (default 610 items).
- Each checklist item should be a concrete action and, when helpful, mention files/commands.
- **Make items atomic and ordered**: discovery → changes → tests → rollout.
- **Verb-first**: “Add…”, “Refactor…”, “Verify…”, “Ship…”.
- Include at least one item for **tests/validation** and one for **edge cases/risk** when applicable.
- If there are unknowns, include a tiny **Open questions** section (max 3).
4. **Do not preface the plan with meta explanations; output only the plan as per template**
## Plan template (follow exactly)
```markdown
# Plan
<13 sentences: what were doing, why, and the high-level approach.>
## Scope
- In:
- Out:
## Action items
[ ] <Step 1>
[ ] <Step 2>
[ ] <Step 3>
[ ] <Step 4>
[ ] <Step 5>
[ ] <Step 6>
## Open questions
- <Question 1>
- <Question 2>
- <Question 3>
```
## Checklist item guidance
Good checklist items:
- Point to likely files/modules: src/..., app/..., services/...
- Name concrete validation: “Run npm test”, “Add unit tests for X”
- Include safe rollout when relevant: feature flag, migration plan, rollback note
Avoid:
- Vague steps (“handle backend”, “do auth”)
- Too many micro-steps
- Writing code snippets (keep the plan implementation-agnostic)
-180
View File
@@ -1,180 +0,0 @@
---
name: plan
description: Generate a plan for how an agent should accomplish a complex coding task. Use when a user asks for a plan, and optionally when they want to save, find, read, update, or delete plan files in $CODEX_HOME/plans (default ~/.codex/plans).
metadata:
short-description: Generate a plan for a complex task
---
# Plan
## Overview
Draft structured plans that clarify intent, scope, requirements, action items, testing/validation, and risks.
Optionally, save plans to disk as markdown files with YAML frontmatter and free-form content. When drafting in chat, output only the plan body without frontmatter; add frontmatter only when saving to disk. Only write to the plans folder; do not modify the repository codebase.
This skill can also be used to draft codebase or system overviews.
## Core rules
- Resolve the plans directory as `$CODEX_HOME/plans` or `~/.codex/plans` when `CODEX_HOME` is not set.
- Create the plans directory if it does not exist.
- Never write to the repo; only read files to understand context.
- Require frontmatter with **only** `name` and `description` (single-line values) for on-disk plans.
- When presenting a draft plan in chat, omit frontmatter and start at `# Plan`.
- Enforce naming rules: short, lower-case, hyphen-delimited; filename must equal `<name>.md`.
- If a plan is not found, state it clearly and offer to create one.
- Allow overview-style plans that document flows, architecture, or context without a work checklist.
## Decide the task
1. **Find/list**: discover plans by frontmatter summary; confirm if multiple matches exist.
2. **Read/use**: validate frontmatter; present summary and full contents.
3. **Create**: inspect repo read-only; choose plan style (implementation vs overview); draft plan; write to plans directory only.
4. **Update**: load plan; revise content and/or description; preserve frontmatter keys; overwrite the plan file.
5. **Delete**: confirm intent, then remove the plan file if asked.
## Plan discovery
- Prefer `scripts/list_plans.py` for quick summaries.
- Use `scripts/read_plan_frontmatter.py` to validate a specific plan.
- If name mismatches filename or frontmatter is missing fields, call it out and ask whether to fix.
## Plan creation workflow
1. Scan context quickly: read README.md and obvious docs (docs/, CONTRIBUTING.md, ARCHITECTURE.md); skim likely touched files; identify constraints (language, frameworks, CI/test commands, deployment).
2. Ask follow-ups only if blocked: at most 1-2 questions, prefer multiple-choice. If unsure but not blocked, state assumptions and proceed.
3. Identify scope, constraints, and data model/API implications (or capture existing behavior for an overview).
4. Draft either an ordered implementation plan or a structured overview plan with diagrams/notes as needed.
5. Immediately output the plan body only (no frontmatter), then ask the user if they want to 1. Make changes, 2. Implement it, 3. Save it as per plan.
6. If the user wants to save it, prepend frontmatter and save the plan under the computed plans directory using `scripts/create_plan.py`.
## Plan update workflow
- Re-read the plan and related code/docs before updating.
- Keep the plan name stable unless the user explicitly wants a rename.
- If renaming, update both frontmatter `name` and filename together.
## Scripts (low-freedom helpers)
Create a plan file (body only; frontmatter is written for you). Run from the plan skill directory:
```bash
python ./scripts/create_plan.py \
--name codex-rate-limit-overview \
--description "Scope and update plan for Codex rate limiting" \
--body-file /tmp/plan-body.md
```
Read frontmatter summary for a plan (run from the plan skill directory):
```bash
python ./scripts/read_plan_frontmatter.py ~/.codex/plans/codex-rate-limit-overview.md
```
List plan summaries (optional filter; run from the plan skill directory):
```bash
python ./scripts/list_plans.py --query "rate limit"
```
## Plan file format
Use one of the structures below for the plan body. When drafting, output only the body (no frontmatter). When saving, prepend this frontmatter:
```markdown
---
name: <plan-name>
description: <1-line summary>
---
```
### Implementation plan body template
```markdown
# Plan
<1-3 sentences: intent, scope, and approach.>
## Requirements
- <Requirement 1>
- <Requirement 2>
## Scope
- In:
- Out:
## Files and entry points
- <File/module/entry point 1>
- <File/module/entry point 2>
## Data model / API changes
- <If applicable, describe schema or contract changes>
## Action items
[ ] <Step 1>
[ ] <Step 2>
[ ] <Step 3>
[ ] <Step 4>
[ ] <Step 5>
[ ] <Step 6>
## Testing and validation
- <Tests, commands, or validation steps>
## Risks and edge cases
- <Risk 1>
- <Risk 2>
## Open questions
- <Question 1>
- <Question 2>
```
### Overview plan body template
```markdown
# Plan
<1-3 sentences: intent and scope of the overview.>
## Overview
<Describe the system, flow, or architecture at a high level.>
## Diagrams
<Include text or Mermaid diagrams if helpful.>
## Key file references
- <File/module/entry point 1>
- <File/module/entry point 2>
## Auth / routing / behavior notes
- <Capture relevant differences (e.g., auth modes, routing paths).>
## Current status
- <What is live today vs pending work, if known.>
## Action items
- None (overview only).
## Testing and validation
- None (overview only).
## Risks and edge cases
- None (overview only).
## Open questions
- None.
```
## Writing guidance
- Start with 1 short paragraph describing intent and approach.
- Keep action items ordered and atomic (discovery -> changes -> tests -> rollout); use verb-first phrasing.
- Scale action item count to complexity (simple: 1-2; complex: up to about 10).
- Include file/entry-point hints and concrete validation steps where useful.
- Always include testing/validation and risks/edge cases in implementation plans; include safe rollout/rollback when relevant.
- Use open questions only when necessary (max 3).
- Avoid vague steps, micro-steps, and code snippets; keep the plan implementation-agnostic.
- For overview plans, keep action items minimal and set non-applicable sections to "None."
-114
View File
@@ -1,114 +0,0 @@
#!/usr/bin/env python3
"""Create or overwrite a plan markdown file in $CODEX_HOME/plans."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
from plan_utils import get_plans_dir, validate_plan_name
DEFAULT_TEMPLATE = """# Plan
<1-3 sentences: intent, scope, and approach.>
## Requirements
- <Requirement 1>
- <Requirement 2>
## Scope
- In:
- Out:
## Files and entry points
- <File/module/entry point 1>
- <File/module/entry point 2>
## Data model / API changes
- <If applicable, describe schema or contract changes>
## Action items
[ ] <Step 1>
[ ] <Step 2>
[ ] <Step 3>
[ ] <Step 4>
[ ] <Step 5>
[ ] <Step 6>
## Testing and validation
- <Tests, commands, or validation steps>
## Risks and edge cases
- <Risk 1>
- <Risk 2>
## Open questions
- <Question 1>
- <Question 2>
"""
def read_body(args: argparse.Namespace) -> str | None:
if args.template:
return DEFAULT_TEMPLATE
if args.body_file:
return Path(args.body_file).read_text(encoding="utf-8")
if not sys.stdin.isatty():
return sys.stdin.read()
return None
def main() -> int:
parser = argparse.ArgumentParser(
description="Create a plan file under $CODEX_HOME/plans or ~/.codex/plans."
)
parser.add_argument("--name", required=True, help="Plan name (lower-case, hyphen-delimited).")
parser.add_argument("--description", required=True, help="Short plan description.")
parser.add_argument(
"--body-file",
help="Path to markdown body (without frontmatter). If omitted, read from stdin.",
)
parser.add_argument(
"--template",
action="store_true",
help="Write a template body instead of reading from stdin or --body-file.",
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Overwrite the plan file if it already exists.",
)
args = parser.parse_args()
name = args.name.strip()
description = args.description.strip()
validate_plan_name(name)
if not description or "\n" in description:
raise SystemExit("Description must be a single line.")
body = read_body(args)
if body is None:
raise SystemExit("Provide --body-file, stdin, or --template to supply plan content.")
body = body.strip()
if not body:
raise SystemExit("Plan body cannot be empty.")
if body.lstrip().startswith("---"):
raise SystemExit("Plan body should not include frontmatter.")
plans_dir = get_plans_dir()
plans_dir.mkdir(parents=True, exist_ok=True)
plan_path = plans_dir / f"{name}.md"
if plan_path.exists() and not args.overwrite:
raise SystemExit(f"Plan already exists: {plan_path}. Use --overwrite to replace.")
content = f"---\nname: {name}\ndescription: {description}\n---\n\n{body}\n"
plan_path.write_text(content, encoding="utf-8")
print(str(plan_path))
return 0
if __name__ == "__main__":
raise SystemExit(main())
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python3
"""List plan summaries by reading frontmatter only."""
from __future__ import annotations
import argparse
import json
from plan_utils import get_plans_dir, parse_frontmatter
def main() -> int:
parser = argparse.ArgumentParser(description="List plan summaries from $CODEX_HOME/plans.")
parser.add_argument("--query", help="Case-insensitive substring to filter name/description.")
parser.add_argument("--json", action="store_true", help="Emit JSON output.")
args = parser.parse_args()
plans_dir = get_plans_dir()
if not plans_dir.exists():
raise SystemExit(f"Plans directory not found: {plans_dir}")
query = args.query.lower() if args.query else None
items = []
for path in sorted(plans_dir.glob("*.md")):
try:
data = parse_frontmatter(path)
except ValueError:
continue
name = data.get("name")
description = data.get("description")
if not name or not description:
continue
if query:
haystack = f"{name} {description}".lower()
if query not in haystack:
continue
items.append({"name": name, "description": description, "path": str(path)})
if args.json:
print(json.dumps(items))
else:
for item in items:
print(f"{item['name']}\t{item['description']}\t{item['path']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
-53
View File
@@ -1,53 +0,0 @@
#!/usr/bin/env python3
"""Shared helpers for plan scripts."""
from __future__ import annotations
import os
import re
from pathlib import Path
_NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
def get_codex_home() -> Path:
"""Return CODEX_HOME if set, else ~/.codex."""
return Path(os.environ.get("CODEX_HOME", "~/.codex")).expanduser()
def get_plans_dir() -> Path:
return get_codex_home() / "plans"
def validate_plan_name(name: str) -> None:
if not name or not _NAME_RE.match(name):
raise ValueError(
"Invalid plan name. Use short, lower-case, hyphen-delimited names "
"(e.g., codex-rate-limit-overview)."
)
def parse_frontmatter(path: Path) -> dict:
"""Parse YAML frontmatter from a markdown file without reading the body."""
with path.open("r", encoding="utf-8") as handle:
first = handle.readline()
if first.strip() != "---":
raise ValueError("Frontmatter must start with '---'.")
data: dict[str, str] = {}
for line in handle:
stripped = line.strip()
if stripped == "---":
return data
if not stripped or stripped.startswith("#"):
continue
if ":" not in line:
raise ValueError(f"Invalid frontmatter line: {line.rstrip()}")
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
if value and len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]
data[key] = value
raise ValueError("Frontmatter must end with '---'.")
@@ -1,41 +0,0 @@
#!/usr/bin/env python3
"""Read plan frontmatter without loading the full markdown body."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from plan_utils import parse_frontmatter
def main() -> int:
parser = argparse.ArgumentParser(description="Read name/description from plan frontmatter.")
parser.add_argument("plan_path", help="Path to the plan markdown file.")
parser.add_argument("--json", action="store_true", help="Emit JSON output.")
args = parser.parse_args()
path = Path(args.plan_path).expanduser()
if not path.exists():
raise SystemExit(f"Plan not found: {path}")
data = parse_frontmatter(path)
name = data.get("name")
description = data.get("description")
if not name or not description:
raise SystemExit("Frontmatter must include name and description.")
payload = {"name": name, "description": description, "path": str(path)}
if args.json:
print(json.dumps(payload))
else:
print(f"name: {name}")
print(f"description: {description}")
print(f"path: {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())