diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..c39c6b969ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,207 @@ +name: Bug Report +description: Report something in Paperclip that is not working correctly. +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. The more detail you provide, the faster it can be fixed. + + > **⚠️ Privacy notice:** Several fields below accept logs, config, or transcript output that may contain **personally identifiable information (PII)** — usernames in file paths, API keys, tokens, company names, agent output, or task content. Before pasting: + > 1. Review for sensitive data + > 2. Redact usernames, paths, API keys, tokens (e.g. replace `/Users/yourname/` with `/Users/REDACTED/`) + > 3. For heavy output, consider running it through an anonymizer such as **[presidio-anonymizer](https://microsoft.github.io/presidio/)** (open-source, local-only) or **[scrub](https://github.com/dssg/scrub)** + + - type: checkboxes + id: preflight + attributes: + label: Pre-submission checklist + description: Please confirm each of these before filing. + options: + - label: I have searched existing open and closed issues and this is not a duplicate. + required: true + - label: I am on the latest released version of Paperclip (or can reproduce on `master`). + required: true + - label: I have confirmed the error originates in Paperclip itself — not in my agent adapter, API provider, or local configuration. See `AGENTS.md` → "For AI Agents Filing Issues" if you are an AI agent. + required: true + + - type: input + id: version + attributes: + label: Paperclip version + description: "Run `paperclip --version`, or check the version field in the repo's `package.json`." + placeholder: "e.g. 1.24.0, or commit SHA if on master" + validations: + required: true + + - type: input + id: node_version + attributes: + label: Node.js version + description: "Run `node --version`." + placeholder: "e.g. v20.11.0" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Operating system + options: + - macOS + - Windows + - Linux (Ubuntu / Debian) + - Linux (Fedora / RHEL) + - Linux (Arch) + - Linux (other) + - WSL + - Other (please describe in additional context) + validations: + required: true + + - type: dropdown + id: install_method + attributes: + label: Installation method + options: + - npm / pnpm global install + - Docker + - Built from source (pnpm dev / pnpm build) + - Other + validations: + required: true + + - type: checkboxes + id: adapters + attributes: + label: Agent adapter(s) involved + description: Select all that apply. If unsure, leave unchecked and describe in additional context. + options: + - label: Claude Code + - label: Codex + - label: Cursor + - label: Droid + - label: Hermes + - label: Custom / external plugin adapter + - label: Not adapter-specific (core bug) + + - type: dropdown + id: database + attributes: + label: Database mode + options: + - Embedded PGlite (default — `DATABASE_URL` unset) + - External Postgres + - Not database-related + validations: + required: true + + - type: dropdown + id: access_context + attributes: + label: Access context + description: Whose credentials were being used when the bug occurred? + options: + - Board (human operator) + - Agent (bearer API key via `agent_api_keys`) + - Both + - Unclear / not applicable + validations: + required: true + + - type: textarea + id: what_happened + attributes: + label: What happened? + description: Describe the bug clearly. Be specific about which Paperclip command, endpoint, or UI action was involved. + placeholder: | + When I call POST /api/companies with ..., the server responds with 500 and logs ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect? + description: Describe the behavior you expected instead. + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: Exact steps so someone else can reproduce this. Start from a clean install where possible. + placeholder: | + 1. `pnpm install && pnpm dev` + 2. `curl http://localhost:3100/api/health` → 200 OK + 3. `curl -X POST http://localhost:3100/api/companies -d '{"name":"foo"}'` → 500 + 4. Error appears in server log at ... + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Error output / logs + description: | + Paste relevant stack traces, console output, or log lines. This will be rendered as code. + + **⚠️ PII warning:** Server logs often contain file paths with your username (e.g. `/Users/yourname/...`). Redact before pasting. + render: shell + validations: + required: false + + - type: textarea + id: config + attributes: + label: Relevant config (if applicable) + description: | + If the bug is config-related, paste the relevant snippet. + + **⚠️ PII warning:** Paperclip config may contain API keys, encrypted-secrets paths, company names, or tokens. **Remove all secrets before pasting.** + render: json + validations: + required: false + + - type: dropdown + id: frequency + attributes: + label: How often does this happen? + options: + - Every time (100% reproducible) + - Most of the time + - Sometimes / intermittent + - Only happened once + validations: + required: true + + - type: dropdown + id: impact + attributes: + label: Impact + description: How much does this affect your use of Paperclip? + options: + - Blocker — cannot use Paperclip at all + - Major — core feature is broken, no workaround + - Moderate — feature is broken but I have a workaround + - Minor — cosmetic or edge case + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional context + description: Screenshots, related issues, adapter plugin details, or anything else. + validations: + required: false + + - type: checkboxes + id: pii_confirmed + attributes: + label: Privacy checklist + description: Please confirm you've reviewed your submission for sensitive data. + options: + - label: I have reviewed all pasted output for PII (usernames, file paths, API keys, tokens, company names) and redacted where necessary. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..f1e896a192e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Discord — questions & support + url: https://discord.gg/m4HZY7xNG3 + about: For setup help, general questions, and conversation — not bug reports. + - name: GitHub Discussions + url: https://github.com/paperclipai/paperclip/discussions + about: For ideas, RFCs, and open-ended discussion. diff --git a/.github/ISSUE_TEMPLATE/docs_issue.yml b/.github/ISSUE_TEMPLATE/docs_issue.yml new file mode 100644 index 00000000000..34ff9df5237 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs_issue.yml @@ -0,0 +1,46 @@ +name: Documentation Issue +description: Report incorrect, missing, or unclear documentation. +labels: ["documentation", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Help improve the docs. Point us at what's wrong, missing, or confusing. + + - type: dropdown + id: type + attributes: + label: Issue type + options: + - Incorrect information + - Missing documentation + - Unclear or confusing + - Outdated (no longer matches behavior) + - Typo or formatting + validations: + required: true + + - type: input + id: location + attributes: + label: Where is the issue? + description: File path, URL, or section name. + placeholder: "e.g. README.md#installation, doc/DEVELOPING.md, docs/plugins.md" + validations: + required: true + + - type: textarea + id: description + attributes: + label: What's wrong? + description: Describe the documentation issue. + validations: + required: true + + - type: textarea + id: suggestion + attributes: + label: Suggested fix + description: If you know what the correct information should be, include it here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 00000000000..bce23547d26 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,93 @@ +name: Enhancement Proposal +description: Propose an improvement to an existing Paperclip feature. +labels: ["enhancement", "needs-triage"] +body: + - type: markdown + attributes: + value: | + An enhancement improves something that already exists — better output, broader edge-case handling, clearer UX, performance. It does not add new commands, endpoints, or concepts. If you are proposing something new, use the **Feature Request** template instead. + + - type: checkboxes + id: preflight + attributes: + label: Pre-submission checklist + options: + - label: I have confirmed this improves existing behavior — it does not add a new command, endpoint, or concept. + required: true + - label: I have searched existing open and closed issues and this has not already been proposed. + required: true + + - type: input + id: what_is_improved + attributes: + label: What existing behavior does this improve? + description: Name the specific command, endpoint, UI view, or behavior being enhanced. + placeholder: "e.g. /api/companies response shape, board task list sorting, adapter startup log output" + validations: + required: true + + - type: dropdown + id: subsystem + attributes: + label: Subsystem affected + options: + - server/ — REST API & orchestration services + - ui/ — React + Vite board UI + - packages/db — Drizzle schema, migrations + - packages/shared — types, constants, validators, API paths + - packages/adapters — agent adapter implementations + - packages/plugins — plugin system + - Cross-cutting (multiple of the above) + - Unsure + validations: + required: true + + - type: textarea + id: current_behavior + attributes: + label: Current behavior + description: Describe exactly how it works today. Include example output or a screenshot if helpful. + placeholder: | + Currently, when I call ..., the response is ... + validations: + required: true + + - type: textarea + id: proposed_behavior + attributes: + label: Proposed behavior + description: Describe exactly how it should work after the enhancement. Include example output. + placeholder: | + After this enhancement, the response would be ... + validations: + required: true + + - type: textarea + id: reason + attributes: + label: Reason and benefit + description: | + Why is the current behavior a problem, and what is the concrete benefit of the proposed behavior? + + Vague answers like "it would be better" or "more user-friendly" are not enough. + validations: + required: true + + - type: textarea + id: breaking_changes + attributes: + label: Breaking changes + description: | + Does this change any behavior, response shape, or output format that users or agents might rely on? + If yes, describe exactly what changes and how backward compatibility is maintained. + Write "None" only if you are certain. + validations: + required: true + + - type: textarea + id: additional_context + attributes: + label: Additional context + description: Screenshots, related issues, or anything else. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..d674ac3d90d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,102 @@ +name: Feature Request +description: Propose a new capability for Paperclip. +labels: ["enhancement", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to propose a feature. Please read `CONTRIBUTING.md` before opening — bigger changes should be discussed in Discord `#dev` first. + + - type: checkboxes + id: preflight + attributes: + label: Pre-submission checklist + options: + - label: I have searched existing open and closed issues and Discussions — this has not been proposed before. + required: true + - label: This feature belongs in Paperclip core, not in a plugin, skill, or external adapter. + required: true + - label: I have read `AGENTS.md` §3 (Repo Map) and know which subsystem this would affect. + required: true + + - type: input + id: feature_name + attributes: + label: Feature name + description: A short, concrete name for what you're proposing. + placeholder: "e.g. Per-company budget overrides, Cross-company task templates" + validations: + required: true + + - type: dropdown + id: subsystem + attributes: + label: Subsystem affected + description: Which part of the codebase would this primarily live in? + options: + - server/ — REST API & orchestration services + - ui/ — React + Vite board UI + - packages/db — Drizzle schema, migrations + - packages/shared — types, constants, validators, API paths + - packages/adapters — agent adapter implementations + - packages/plugins — plugin system + - Cross-cutting (multiple of the above) + - Unsure + validations: + required: true + + - type: textarea + id: problem_statement + attributes: + label: Problem + description: Describe the concrete problem this solves. Focus on the problem, not the solution yet. + placeholder: | + Today, when [specific situation], the user cannot [specific thing], which causes [specific negative outcome]. + validations: + required: true + + - type: textarea + id: proposed_solution + attributes: + label: Proposed solution + description: Describe what you'd like to see added. Be specific about commands, endpoints, UI, and behavior. + placeholder: | + Add ... so that ... + Example usage: ... + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: What other approaches did you consider? Why did you reject them? + validations: + required: true + + - type: textarea + id: invariants + attributes: + label: Impact on Paperclip invariants + description: | + `AGENTS.md` §5 lists the control-plane invariants that must be preserved: + - Single-assignee task model + - Atomic issue checkout semantics + - Approval gates for governed actions + - Budget hard-stop auto-pause behavior + - Activity logging for mutating actions + - Company-scoping of all domain entities + + Does this feature affect any of them? How is compliance preserved? Write "None" if not applicable. + placeholder: | + This feature touches activity logging — any mutations made by the new endpoint must write to the activity log. Other invariants unaffected. + validations: + required: true + + - type: textarea + id: additional_context + attributes: + label: Additional context + description: Screenshots, mockups, prior art from other tools, related Discussions, or anything else that helps explain the proposal. + validations: + required: false diff --git a/.github/workflows/auto-label-issues.yml b/.github/workflows/auto-label-issues.yml new file mode 100644 index 00000000000..9268da30cee --- /dev/null +++ b/.github/workflows/auto-label-issues.yml @@ -0,0 +1,42 @@ +name: Auto-label new issues + +# Security review: this workflow only reads context.issue.number, context.repo.owner, +# and context.repo.repo via actions/github-script. No untrusted user input +# (issue title, body, comments, head_ref, etc.) is interpolated into shell commands. +# No run: steps at all — all logic runs inside github-script's JS sandbox. + +on: + issues: + types: [opened] + +permissions: + issues: write + +jobs: + add-triage-label: + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + // Ensure the label exists before applying it. Self-healing on first run. + try { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'needs-triage', + color: 'fbca04', + description: 'Newly filed, not yet reviewed by a maintainer', + }); + } catch (e) { + // 422 = label already exists — safe no-op. + if (e.status !== 422) throw e; + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['needs-triage'], + }); diff --git a/.github/workflows/require-issue-link.yml b/.github/workflows/require-issue-link.yml new file mode 100644 index 00000000000..8e4e45b62b4 --- /dev/null +++ b/.github/workflows/require-issue-link.yml @@ -0,0 +1,88 @@ +name: Require Issue Link + +# Security review: +# - Uses pull_request_target so fork PRs receive a write-scoped GITHUB_TOKEN and the +# comment step actually posts instead of silently 403-ing. Safe here because +# neither step checks out PR-supplied code — the scan reads $PR_BODY from an env +# var, and the github-script step only calls the REST API with trusted context +# values (owner, repo, PR number). No untrusted content is executed or written. +# - The PR body (github.event.pull_request.body) is untrusted and is handled via +# the safe env-var pattern: bound to $PR_BODY, then matched with grep. Never +# interpolated directly into the shell command string. +# - "synchronize" is intentionally omitted from the trigger types to avoid posting +# duplicate "missing issue link" comments on every push. Adding a Closes/Fixes/ +# Resolves reference happens by editing the PR body, which fires "edited", so +# the check still runs when contributors add the reference after opening. + +on: + pull_request_target: + types: [opened, edited, reopened] + +permissions: + pull-requests: write + issues: write + +jobs: + check-issue-link: + name: Issue link required + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Scan PR body for closing reference + id: check + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: | + if printf '%s' "$PR_BODY" | grep -qiE '(close[sd]?|fix(e[sd])?|resolve[sd]?)\s+#[0-9]+'; then + echo "found=true" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Comment and fail if missing + if: steps.check.outputs.found == 'false' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + // Dedup guard: only post the comment if one isn't already present. + // The "edited" event fires on both title and body edits, so without + // this check a contributor editing the title while the body still + // lacks a closing reference would accumulate a new bot comment each + // time. Uses github.paginate so the check scans every page of + // comments, not just the first 100 — otherwise on high-traffic PRs + // the original bot comment could fall outside the fetched page and + // duplicates would reappear. The check still fails unconditionally + // when the link is missing — only the comment is skipped on repeats. + const comments = await github.paginate(github.rest.issues.listComments, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + per_page: 100, + }); + const alreadyCommented = comments.some( + c => c.user && c.user.type === 'Bot' && typeof c.body === 'string' && c.body.startsWith('## Missing issue link') + ); + + if (!alreadyCommented) { + const repoUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}`; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: [ + '## Missing issue link', + '', + 'This PR does not reference an issue. Every PR should link to an open issue using a closing keyword in the PR body:', + '', + '```', + 'Closes #123', + '```', + '', + `If no issue exists yet, please [open one first](${repoUrl}/issues/new/choose) and update this PR body to reference it.`, + '', + 'This is a soft check — if your PR genuinely has no associated issue (e.g. a trivial typo fix or maintainer-only tooling change), add a short note in the PR body explaining why and ask a maintainer to override this check.', + ].join('\n'), + }); + } + + core.setFailed('PR body must contain a closing issue reference (e.g. "Closes #123").'); diff --git a/AGENTS.md b/AGENTS.md index 89cefbef4be..65720132838 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -164,7 +164,27 @@ When creating a pull request (via `gh pr create` or any other method), you **mus - **Model Used** — the AI model that produced or assisted with the change (provider, exact model ID, context window, capabilities). Write "None — human-authored" if no AI was used. - **Checklist** — all items checked -## 11. Definition of Done +## 11. For AI Agents Filing Issues + +If you are an AI agent and you have encountered an error while using Paperclip, read this before filing an issue. + +**Before filing, verify all three:** + +1. The error originates in Paperclip's own source code — not in your API provider, your configuration, your `CLAUDE.md`, or your agent's own logic. +2. You can reproduce the error on a clean install with the latest Paperclip version. +3. No existing open or closed issue already describes this — search first. + +**Common false positives — do NOT file issues for:** + +- API quota, rate limit, or 429 errors from Anthropic, OpenAI, or any other model provider +- Authentication failures caused by expired, missing, or malformed API keys +- Errors originating in your own agent code, `CLAUDE.md` configuration, or local toolchain +- Network timeouts on your host machine +- Errors coming from an external adapter plugin — file those upstream in the plugin's own repository + +If in doubt, raise the error with your human operator or ask in Discord `#help` before filing an issue. All issues must use one of the templates at `.github/ISSUE_TEMPLATE/` — blank issues are disabled. + +## 12. Definition of Done A change is done when all are true: @@ -174,7 +194,7 @@ A change is done when all are true: 4. Docs updated when behavior or commands change 5. PR description follows the [PR template](.github/PULL_REQUEST_TEMPLATE.md) with all sections filled in (including Model Used) -## 11. Fork-Specific: HenkDz/paperclip +## 13. Fork-Specific: HenkDz/paperclip This is a fork of `paperclipai/paperclip` with QoL patches and an **external-only** Hermes adapter story on branch `feat/externalize-hermes-adapter` ([tree](https://github.com/HenkDz/paperclip/tree/feat/externalize-hermes-adapter)). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a69aa86d144..c337873268a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,8 @@ Thanks for wanting to contribute! We really appreciate both small fixes and thoughtful larger changes. +> **Filing an issue?** Please use one of the [issue templates](./.github/ISSUE_TEMPLATE). Blank issues are disabled. For questions or general discussion, please use [Discord](https://discord.gg/m4HZY7xNG3) or [GitHub Discussions](https://github.com/paperclipai/paperclip/discussions) instead of filing an issue. + ## Two Paths to Get Your Pull Request Accepted ### Path 1: Small, Focused Changes (Fastest way to get merged)