Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions plugins/codeforerunner/skills/forerunner-gaps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
name: forerunner-gaps
description: Surface missing or stale forerunner documentation findings and scan gaps. Use when the user wants to know what docs are outdated, what tasks have never been run, or what the scan couldn't determine.
---

# forerunner-gaps

Reads cached forerunner state and surfaces two categories of gaps:
1. **Doc gaps** — STALE or MISSING findings from the check report
2. **Scan gaps** — fields the scan couldn't determine from available evidence

Does not generate docs itself. Routes the user to the right next step.

## Activate when

User asks to: find gaps, check forerunner gaps, what's missing, surface stale findings, what docs are outdated, gaps report, what hasn't been run.

## Collect this context

- `.forerunner/scan.yaml` — scan result (check file modification time)
- `.forerunner/check-report.md` — staleness classifications (check file modification time)

## Execute

### Step 1 — Check cache freshness

Read `.forerunner/scan.yaml` and `.forerunner/check-report.md`.

**If either file is missing:** Tell the user which file is absent. Ask them to run `/forerunner-scan` and/or `/forerunner-check` first. Stop.

**If scan.yaml is older than 24 hours:** Warn the user: "scan.yaml is X hours old — findings may not reflect current codebase. Run `/forerunner-scan` to refresh, or continue with cached results?" Wait for their choice before proceeding.

**If check-report.md is older than scan.yaml:** Warn the user: "check-report.md predates the last scan — it may not reflect the current scan result. Run `/forerunner-check` to refresh, or continue?" Wait for their choice.

### Step 2 — Surface doc gaps

Extract all rows from check-report.md where status is `STALE` or `MISSING`.

If none: report "All documented tasks are CURRENT." Skip to Step 3.

Otherwise, present a summary table:

```
## Doc Gaps

| Document | Status | Issue |
|-----------------------|---------|-----------------------------------------|
| README.md | STALE | DATABASE_URL missing from config section |
| docs/api.md | MISSING | No API docs found |
```

Then offer: "Run `/forerunner-refresh` to regenerate all stale/missing docs, or invoke a specific task skill (e.g. `/forerunner-readme`) for individual items."

### Step 3 — Surface scan gaps

Read the `gaps:` field from scan.yaml. If absent or empty, report "No scan gaps detected." Stop.

Otherwise, list each gap and ask the user:

> "The scan couldn't determine the following from available evidence:
> - [gap 1]
> - [gap 2]
>
> How would you like to fill these?
> A) Quick fill — I'll ask one question per gap and patch scan.yaml
> B) Full grill session — deeper conversation that also updates CONTEXT.md and domain docs (requires grill-with-docs skill)
> C) Skip"

**If user selects B and `grill-with-docs` skill is not available** (check by looking for the skill in the skills directory or `skills-lock.json`): inform the user that `grill-with-docs` is not installed. Fall back to option A automatically, or suggest they install the skill first.

**If user selects A:** Ask one question per gap in sequence. After each answer, update the corresponding field in `.forerunner/scan.yaml` and mark it as user-provided (add `# user-provided` comment inline). Continue until all gaps are filled or user skips a gap.

**If user selects B:** Invoke the `grill-with-docs` skill, passing the gap list as the grilling focus.

## Output

No files written by default. Side effects:
- If quick-fill (A): patches `.forerunner/scan.yaml` with user-provided values
- If grill session (B): `grill-with-docs` owns its own output (CONTEXT.md, ADRs)

Always end with a summary of what was surfaced, what was filled, and what remains open.
6 changes: 6 additions & 0 deletions skills-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@
"sourceType": "local",
"skillPath": "skills/forerunner-version-audit/SKILL.md",
"computedHash": "17d9367e2e3608b9a9fde05730a4199cb1674a1e4529aaf53364c02b76e5149a"
},
"forerunner-gaps": {
"source": "plugins/codeforerunner/skills/forerunner-gaps/SKILL.md",
"sourceType": "local",
"skillPath": "skills/forerunner-gaps/SKILL.md",
"computedHash": "0c314bfa709227b23dc5405418da561d83b3382be5914cab6d502814a08dd860"
}
}
}
81 changes: 81 additions & 0 deletions skills/forerunner-gaps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
name: forerunner-gaps
description: Surface missing or stale forerunner documentation findings and scan gaps. Use when the user wants to know what docs are outdated, what tasks have never been run, or what the scan couldn't determine.
---

# forerunner-gaps

Reads cached forerunner state and surfaces two categories of gaps:
1. **Doc gaps** — STALE or MISSING findings from the check report
2. **Scan gaps** — fields the scan couldn't determine from available evidence

Does not generate docs itself. Routes the user to the right next step.

## Activate when

User asks to: find gaps, check forerunner gaps, what's missing, surface stale findings, what docs are outdated, gaps report, what hasn't been run.

## Collect this context

- `.forerunner/scan.yaml` — scan result (check file modification time)
- `.forerunner/check-report.md` — staleness classifications (check file modification time)

## Execute

### Step 1 — Check cache freshness

Read `.forerunner/scan.yaml` and `.forerunner/check-report.md`.

**If either file is missing:** Tell the user which file is absent. Ask them to run `/forerunner-scan` and/or `/forerunner-check` first. Stop.

**If scan.yaml is older than 24 hours:** Warn the user: "scan.yaml is X hours old — findings may not reflect current codebase. Run `/forerunner-scan` to refresh, or continue with cached results?" Wait for their choice before proceeding.

**If check-report.md is older than scan.yaml:** Warn the user: "check-report.md predates the last scan — it may not reflect the current scan result. Run `/forerunner-check` to refresh, or continue?" Wait for their choice.

### Step 2 — Surface doc gaps

Extract all rows from check-report.md where status is `STALE` or `MISSING`.

If none: report "All documented tasks are CURRENT." Skip to Step 3.

Otherwise, present a summary table:

```
## Doc Gaps

| Document | Status | Issue |
|-----------------------|---------|-----------------------------------------|
| README.md | STALE | DATABASE_URL missing from config section |
| docs/api.md | MISSING | No API docs found |
```

Then offer: "Run `/forerunner-refresh` to regenerate all stale/missing docs, or invoke a specific task skill (e.g. `/forerunner-readme`) for individual items."

### Step 3 — Surface scan gaps

Read the `gaps:` field from scan.yaml. If absent or empty, report "No scan gaps detected." Stop.

Otherwise, list each gap and ask the user:

> "The scan couldn't determine the following from available evidence:
> - [gap 1]
> - [gap 2]
>
> How would you like to fill these?
> A) Quick fill — I'll ask one question per gap and patch scan.yaml
> B) Full grill session — deeper conversation that also updates CONTEXT.md and domain docs (requires grill-with-docs skill)
> C) Skip"

**If user selects B and `grill-with-docs` skill is not available** (check by looking for the skill in the skills directory or `skills-lock.json`): inform the user that `grill-with-docs` is not installed. Fall back to option A automatically, or suggest they install the skill first.

**If user selects A:** Ask one question per gap in sequence. After each answer, update the corresponding field in `.forerunner/scan.yaml` and mark it as user-provided (add `# user-provided` comment inline). Continue until all gaps are filled or user skips a gap.

**If user selects B:** Invoke the `grill-with-docs` skill, passing the gap list as the grilling focus.

## Output

No files written by default. Side effects:
- If quick-fill (A): patches `.forerunner/scan.yaml` with user-provided values
- If grill session (B): `grill-with-docs` owns its own output (CONTEXT.md, ADRs)

Always end with a summary of what was surfaced, what was filled, and what remains open.
52 changes: 52 additions & 0 deletions src/codeforerunner/prompts/tasks/gaps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Task: Surface Documentation and Scan Gaps

Reads cached forerunner state and surfaces two categories of gaps for the user to act on:
1. **Doc gaps** — STALE or MISSING findings from the check report
2. **Scan gaps** — fields the scan could not determine from available evidence

Does not generate docs. Routes the user to the right next step.

## Input

- `.forerunner/scan.yaml` — cached scan result (check file modification time)
- `.forerunner/check-report.md` — cached staleness classifications (check file modification time)

## Instructions

### Step 1 — Check cache freshness

Read `.forerunner/scan.yaml` and `.forerunner/check-report.md`.

If either file is missing: report which file is absent and stop. Ask the user to run `forerunner doc scan` and/or `forerunner doc check` first.

If `scan.yaml` is older than 24 hours: warn the user and offer to continue with cached results or re-run scan first.

If `check-report.md` is older than `scan.yaml`: warn the user and offer to continue or re-run check first.

### Step 2 — Surface doc gaps

Extract all rows from `check-report.md` where status is `STALE` or `MISSING`.

If none: report "All documented tasks are CURRENT." Proceed to step 3.

Present a summary table and offer: run `/forerunner-refresh` to regenerate all stale/missing docs, or invoke a specific task skill for individual items.

### Step 3 — Surface scan gaps

Read the `gaps:` field from `scan.yaml`. If absent or empty: report "No scan gaps detected." Stop.

Otherwise, list each gap and ask the user how to fill them:

**A) Quick fill** — ask one question per gap and patch `scan.yaml` with user-provided values
**B) Full grill session** — deeper conversation that also updates `CONTEXT.md` and domain docs (requires `grill-with-docs` skill; fall back to A if not available)
**C) Skip**

For quick fill (A): ask one question per gap in sequence, update the corresponding field in `.forerunner/scan.yaml`, mark as `# user-provided` inline.

For grill session (B): invoke `grill-with-docs` with the gap list as the grilling focus.

## Rules

- Never run a fresh scan or check automatically. Only read cached files.
- If `grill-with-docs` is not available when user selects B, fall back to A and inform the user.
- End with a summary: what was surfaced, what was filled, what remains open.
3 changes: 2 additions & 1 deletion src/codeforerunner/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
{"name": "refresh", "scan_exempt": false, "skill_slug": "forerunner-refresh"},
{"name": "review", "scan_exempt": false, "skill_slug": "forerunner-review"},
{"name": "stack-docs", "scan_exempt": false, "skill_slug": "forerunner-stack-docs"},
{"name": "version-audit", "scan_exempt": false, "skill_slug": "forerunner-version-audit"}
{"name": "version-audit", "scan_exempt": false, "skill_slug": "forerunner-version-audit"},
{"name": "gaps", "scan_exempt": true, "skill_slug": "forerunner-gaps"}
]
}
32 changes: 31 additions & 1 deletion tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_get_unknown_raises():


def test_scan_exempt_names():
assert tasks.scan_exempt_names() == {"scan", "init-agent-onboarding"}
assert tasks.scan_exempt_names() == {"scan", "init-agent-onboarding", "gaps"}


def test_refresh_tasks_ordered():
Expand Down Expand Up @@ -75,6 +75,36 @@ def test_cli_rejects_unregistered_task(tmp_path, capsys):
assert "unknown task" in capsys.readouterr().err


def test_gaps_task_registered():
t = tasks.get("gaps")
assert t.scan_exempt is True
assert t.skill_slug == "forerunner-gaps"


def test_gaps_excluded_from_refresh_sequence():
names = [t.name for t in tasks.refresh_tasks()]
assert "gaps" not in names


def test_gaps_installable_slug_present():
assert "forerunner-gaps" in tasks.installable_slugs()


def test_gaps_skill_files_exist():
from pathlib import Path
root = Path(__file__).resolve().parent.parent
assert (root / "skills" / "forerunner-gaps" / "SKILL.md").is_file()
assert (root / "plugins" / "codeforerunner" / "skills" / "forerunner-gaps" / "SKILL.md").is_file()


def test_gaps_skill_copies_in_sync():
from pathlib import Path
root = Path(__file__).resolve().parent.parent
installed = (root / "skills" / "forerunner-gaps" / "SKILL.md").read_text(encoding="utf-8")
plugin = (root / "plugins" / "codeforerunner" / "skills" / "forerunner-gaps" / "SKILL.md").read_text(encoding="utf-8")
assert installed == plugin


def test_mcp_tools_list_matches_registry():
from codeforerunner.mcp_server import _tools
import codeforerunner
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.