Claude
Skills
Sign in
Back

pm-feedback

Included with Lifetime
$97 forever

Use this skill when the user wants to report a bug, an unexpected result, or an improvement idea about the project-manager plugin ITSELF (or one of its pm-* skills) to the functional-claude maintainers — or when the agent has just hit a project-manager failure (a pm-* CLI error, a wrong/empty briefing, a crash, a clearly-incorrect result) and should offer to report it. Trigger phrases "report a bug in pm-review / pm-notes / any pm-* skill", "this plugin is broken", "pm gave the wrong answer", "send feedback about project-manager", "tell the functional-claude team", "/pm-feedback", "file a plugin issue". The defining cue is that the target of the complaint is the plugin or a named pm-* skill, not the user's own product. Assembles a README-shaped report (plugin + version, repro, expected vs actual, safe environment), scrubs every credential via the tested `hooks/lib/feedback-report.js` guard, previews it, then delivers — email to the Linear intake address (via resend-cli), GitHub issue fallback, or copy-paste draft. NOT for filing issues about the user's OWN project or product (that's pm-issues) — a bare "report a bug" that names no plugin or skill is ambiguous, so ask whether the bug is in the plugin or in their project rather than assuming.

AI Agents

What this skill does


# PM Feedback

Turn "this plugin broke" or "I have an idea for project-manager" into a clean, credential-safe report and route it to the functional-claude maintainers. This is the outbound counterpart to the `pm-troubleshoot` agent: troubleshoot **diagnoses locally**, pm-feedback **reports upstream**.

## Usage

```
/pm-feedback                         # interactive: ask what went wrong
/pm-feedback <one-line description>  # seed the summary, then fill the rest
```

- **`description`** *(string, optional)* — a one-line summary of the bug/idea. If omitted, the skill asks.

## When to run this

- The user explicitly asks to report a problem or idea about the **project-manager plugin** (not their own project's work).
- **Proactively** — right after a project-manager operation fails or returns a clearly-wrong result: a `pm-cache.js` / `pm-issue.js` / `pm-project.js` non-zero exit with an error `code`, a briefing that's empty when issues plainly exist, a tracker-mismatch the user didn't cause, a crash/stack trace from a pm-* script. Offer once: *"That looks like a project-manager bug — want me to send a report to the maintainers? I'll show you exactly what gets sent first."* Never send without explicit confirmation.

**Not when:** the user wants to file an issue in their **own** tracker — that's `pm-issues`. This skill reports problems with the plugin to its authors.

## What gets reported (and what never does)

The destination is the maintainers' intake, documented in the repo `README.md` → "Feedback & Issues". Email routes straight into their tracker with no account required.

- **Maintainer email:** `[email protected]`
- **GitHub fallback:** `https://github.com/nthplusio/functional-claude/issues` (repo `nthplusio/functional-claude`)

**Privacy guarantee — enforced in code, not by trust.** All report assembly and redaction go through `hooks/lib/feedback-report.js`:
- `safeContext()` **whitelists** only non-sensitive diagnostic fields (repo slug, branch, tracker type, team key, commit SHA). It never copies the raw merged project config, which can embed `linear_api_key`, recipient emails, or spec paths.
- `scrubString()` / `scrubObject()` redact Linear/GitHub/Resend/JWT/Bearer/AWS/Slack token shapes and any `*_API_KEY` / `*_TOKEN` / `*_SECRET` env assignment, recursively, as a second net.
- The skill must **never** read `.resend.md`, `.claude/pm/project.local.json`, or environment variables into the report body.

## Workflow

### Step 1 — Capture

Gather, from the conversation and the user (ask only for what's missing):
- **kind**: `bug` | `unexpected` | `idea`
- **problem**: one-line summary
- **reproduction**: the steps (for a bug, the exact `/pm…` command or pm-* invocation that failed)
- **expected** vs **actual**
- **errorCode**: if a pm-* CLI returned a structured `{ ok:false, code }`, capture the `code` string (e.g. `TRACKER_WRITE_FAILED`, `NO_API_KEY`)

### Step 2 — Gather safe context + version (deterministic)

```bash
# Plugin version (authoritative)
PM_VERSION=$(node -e 'console.log(require(process.env.CLAUDE_PLUGIN_ROOT + "/.claude-plugin/plugin.json").version)')

# Claude Code version (per the README's request)
CLAUDE_VERSION=$(claude --version 2>/dev/null || echo "unknown")

# Runtime context written by the session-start hook (may contain secrets — DO NOT use raw)
SLUG=...                            # the active project slug
CTX="$HOME/.claude/project-manager/cache/$SLUG/context.json"
```

Then assemble + scrub the report in one shot. Pass the captured fields and the raw context **through `feedback-report.js`** — do not hand-build the body:

```bash
node -e '
  const fr = require(process.env.CLAUDE_PLUGIN_ROOT + "/hooks/lib/feedback-report.js");
  const fs = require("fs");
  const ctxPath = process.argv[1];
  let context = null;
  try { context = JSON.parse(fs.readFileSync(ctxPath, "utf8")); } catch (_) {}
  const report = fr.assembleReport({
    pluginVersion: process.env.PM_VERSION,
    claudeVersion: process.env.CLAUDE_VERSION,
    kind: process.env.FB_KIND,
    problem: process.env.FB_PROBLEM,
    reproduction: process.env.FB_REPRO,
    expected: process.env.FB_EXPECTED,
    actual: process.env.FB_ACTUAL,
    errorCode: process.env.FB_CODE,
    context,
  });
  process.stdout.write(JSON.stringify(report));
' "$CTX"
```

`assembleReport` returns `{ subject, body, environment }` — all already scrubbed.

### Step 3 — Preview (mandatory)

Show the user the exact `subject` and `body` that will be sent, and the chosen channel. This mirrors the preview-then-send discipline of `pm-spec` / `pm-prep`. Get explicit confirmation. If the user edits the text, re-run it through `scrubString` before sending.

### Step 4 — Deliver (auto-detect, email-first)

Pick the first available channel; tell the user which one you used.

1. **Email (preferred)** — if `RESEND_API_KEY` is set **or** a `.resend.md` exists at the repo root, deliver via the **`resend-cli` skill**, overriding the recipient to the maintainer intake (not the project's default `to`):
   - to: `[email protected]`
   - from: the verified sender from `.resend.md` (`from:` field)
   - subject / text: the `subject` / `body` from Step 2
   - Invoke the resend-cli skill to perform the send so its config + error handling apply.
2. **GitHub issue (fallback)** — else if `gh auth status` succeeds:
   ```bash
   printf '%s' "$BODY" > "$CLAUDE_JOB_DIR/tmp/pm-feedback.md" 2>/dev/null || printf '%s' "$BODY" > /tmp/pm-feedback.md
   gh issue create --repo nthplusio/functional-claude \
     --title "$SUBJECT" --body-file <the file above>
   ```
   Tell the user this creates a **public** issue and confirm before running.
3. **Draft only (always works)** — else, present the scrubbed `subject` + `body` and the maintainer address so the user can paste it into their own mail client. Remind them the README also accepts GitHub issues.

### Step 5 — Confirm

Report the outcome: channel used + the message ID / issue URL if the send returned one. If delivery failed, fall back to the draft (Step 4.3) so the user is never left empty-handed.

## Failure modes

| Symptom | Handling |
|---|---|
| No `.resend.md` and no `gh` auth | Fall through to draft (4.3) — never block. |
| `feedback-report.js` missing / errors | Stop and tell the user; do not hand-build an unscrubbed body. |
| User pasted a credential into the description | The scrubber catches known shapes, but warn the user and re-preview. |
| resend send returns `RESEND_NOT_CONFIGURED` | Drop to the GitHub or draft channel. |

## Why a tested lib instead of prompt-only

`context.json` is written by `pm-session-start.js` from the **merged** project config, which can include `linear_api_key`. "Please don't include secrets" in a prompt is a hope; `safeContext()` + `scrubObject()` with unit tests (`feedback-report.test.js`) are a guarantee. Security-relevant invariants live in the deterministic layer.

Related in AI Agents