From 7f3d7633b81bb659b59c53243b0be5725b81c054 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 5 Oct 2026 15:04:43 -0700 Subject: [PATCH 1/5] Add the hosted basecamp plugin; rename the CLI plugin basecamp-cli basecamp is now the plugin Anthropic's directory lists: Basecamp through its hosted connector, for claude.ai chat, Cowork, and Claude Code. It's mirrored into plugins/basecamp from the connector repository, which stays the source of truth; bin/sync-basecamp-plugin copies and validates it, and --check reports drift. The CLI plugin keeps its source and becomes basecamp-cli. The README says which to pick and how an existing install moves to the new name. --- .claude-plugin/marketplace.json | 12 +++- README.md | 47 +++++++++++-- bin/sync-basecamp-plugin | 60 ++++++++++++++++ plugins/basecamp/.claude-plugin/plugin.json | 21 ++++++ plugins/basecamp/.mcp.json | 8 +++ plugins/basecamp/README.md | 58 ++++++++++++++++ plugins/basecamp/commands/catch-up.md | 30 ++++++++ plugins/basecamp/commands/draft-check-in.md | 26 +++++++ plugins/basecamp/commands/meeting-prep.md | 34 +++++++++ plugins/basecamp/commands/plan-project.md | 31 +++++++++ plugins/basecamp/commands/triage-hey.md | 27 ++++++++ plugins/basecamp/commands/weekly-recap.md | 29 ++++++++ plugins/basecamp/skills/basecamp/SKILL.md | 77 +++++++++++++++++++++ 13 files changed, 454 insertions(+), 6 deletions(-) create mode 100755 bin/sync-basecamp-plugin create mode 100644 plugins/basecamp/.claude-plugin/plugin.json create mode 100644 plugins/basecamp/.mcp.json create mode 100644 plugins/basecamp/README.md create mode 100644 plugins/basecamp/commands/catch-up.md create mode 100644 plugins/basecamp/commands/draft-check-in.md create mode 100644 plugins/basecamp/commands/meeting-prep.md create mode 100644 plugins/basecamp/commands/plan-project.md create mode 100644 plugins/basecamp/commands/triage-hey.md create mode 100644 plugins/basecamp/commands/weekly-recap.md create mode 100644 plugins/basecamp/skills/basecamp/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 488152f..1265456 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,17 @@ "plugins": [ { "name": "basecamp", - "description": "Basecamp integration for Claude Code and Codex. Create todos, track work, link code to projects.", + "description": "Basecamp for Claude, through Basecamp's hosted connector. Works in chat, Cowork, and Claude Code with no CLI to install: catch up on projects, recap your week, triage Hey!, plan work, draft check-ins.", + "source": { + "source": "git-subdir", + "url": "https://github.com/basecamp/claude-plugins.git", + "path": "plugins/basecamp" + }, + "category": "productivity" + }, + { + "name": "basecamp-cli", + "description": "CLI-based Basecamp for Claude Code and Codex, driven by the basecamp command. Create todos, track work, link code to projects.", "source": { "source": "url", "url": "https://github.com/basecamp/basecamp-cli.git" diff --git a/README.md b/README.md index b331003..eb204ea 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,38 @@ claude plugin install | Plugin | Description | |--------|-------------| -| [basecamp](https://github.com/basecamp/basecamp-cli/tree/main/.claude-plugin) | Todos, cards, messages, files, schedule, check-ins, timeline, recordings, templates, webhooks, subscriptions, lineup, and campfire. | +| [basecamp](plugins/basecamp) | Basecamp through its hosted connector, for claude.ai chat, Cowork, and Claude Code. Six workflows: catch up, weekly recap, triage Hey!, plan a project, draft a check-in, prep a meeting. | +| [basecamp-cli](https://github.com/basecamp/basecamp-cli/tree/main/.claude-plugin) | Basecamp through the `basecamp` command, for Claude Code and Codex. Todos, cards, messages, files, schedule, check-ins, timeline, recordings, templates, webhooks, subscriptions, lineup, and campfire; links code to projects. | +| [hey](https://github.com/basecamp/hey-cli) | Read email, manage todos, track time. | +| [fizzy](https://github.com/basecamp/fizzy-cli) | Manage boards and cards, track work, link code to projects. | -HEY and Fizzy plugins coming soon. +### Which Basecamp plugin? + +- **`basecamp`** talks to Basecamp's hosted connector at + `https://mcp.basecamp.com/mcp`. Nothing to install beyond the plugin: you + connect your Basecamp account from the plugin's Connectors tab. It's the + same plugin Anthropic's directory lists, and the only one that works in + claude.ai chat and Cowork. +- **`basecamp-cli`** drives the [`basecamp` CLI](https://github.com/basecamp/basecamp-cli) + on your machine. Pick it for terminal work in Claude Code or Codex: + scripting, full API coverage, and linking commits and branches to + Basecamp. It needs the CLI installed and signed in. + +You can have both; they don't share state. + +### Moving from the old `basecamp` plugin + +Until October 2026, `basecamp@37signals` was the CLI plugin. It's now +`basecamp-cli@37signals`, and `basecamp@37signals` is the hosted plugin. If +you installed the CLI plugin before the rename, switch to its new name: + +``` +claude plugin marketplace update 37signals +claude plugin uninstall basecamp@37signals +claude plugin install basecamp-cli@37signals +``` + +Then install `basecamp@37signals` as well if you want the hosted one. ## Development plugins @@ -29,6 +58,14 @@ HEY and Fizzy plugins coming soon. ## About -This repo is a thin marketplace index. Plugin source code (skills, hooks, -commands) lives in each product's CLI repo. To contribute, open issues or PRs -there. +This repo is mostly a thin marketplace index. Plugin source code (skills, +hooks, commands) lives in each product's own repo. To contribute, open issues +or PRs there. + +The one plugin with files here, `plugins/basecamp`, is a mirror. Its source +of truth is `plugins/claude/basecamp` in the (private) Basecamp connector +repository, which tests it against the tools the connector actually serves. +Don't edit the mirror; change it upstream, then run +`bin/sync-basecamp-plugin ` to copy and validate it, and commit +with the source commit it prints. `bin/sync-basecamp-plugin --check +` reports drift. diff --git a/bin/sync-basecamp-plugin b/bin/sync-basecamp-plugin new file mode 100755 index 0000000..9e00d4d --- /dev/null +++ b/bin/sync-basecamp-plugin @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Mirror the hosted Basecamp plugin from basecamp-mcp-server, its source of truth. +# +# bin/sync-basecamp-plugin copy, validate, print the source commit +# bin/sync-basecamp-plugin --check exit 1 if plugins/basecamp differs +# +# The source is plugins/claude/basecamp in that checkout. Sync from a clean +# checkout of its main branch, and put the printed commit in the commit message. +# Never edit plugins/basecamp here: change it upstream and sync. +set -euo pipefail + +check=0 +if [[ "${1:-}" == "--check" ]]; then + check=1 + shift +fi + +if [[ $# -ne 1 ]]; then + sed -n '4,5p' "$0" | sed 's/^# *//' >&2 + exit 2 +fi + +root="$(cd "$(dirname "$0")/.." && pwd)" +upstream="$(cd "$1" && pwd)" +src="$upstream/plugins/claude/basecamp" +dest="$root/plugins/basecamp" + +if [[ ! -f "$src/.claude-plugin/plugin.json" ]]; then + echo "error: $src is not the Basecamp plugin (no .claude-plugin/plugin.json)" >&2 + exit 2 +fi + +if [[ $check -eq 1 ]]; then + if diff -r "$src" "$dest"; then + echo "plugins/basecamp matches $(git -C "$upstream" rev-parse --short HEAD)" + else + echo "plugins/basecamp has drifted from basecamp-mcp-server; run bin/sync-basecamp-plugin" >&2 + exit 1 + fi + exit 0 +fi + +if [[ -n "$(git -C "$upstream" status --porcelain -- plugins/claude/basecamp)" ]]; then + echo "error: uncommitted changes under $src; sync from a committed source" >&2 + exit 2 +fi + +rm -rf "$dest" +mkdir -p "$dest" +cp -R "$src/." "$dest/" +find "$dest" -name .DS_Store -delete + +if command -v claude >/dev/null 2>&1; then + claude plugin validate "$dest" +else + echo "claude not installed; skipping claude plugin validate" >&2 +fi + +echo +echo "Mirrored from basecamp/basecamp-mcp-server@$(git -C "$upstream" rev-parse HEAD)" diff --git a/plugins/basecamp/.claude-plugin/plugin.json b/plugins/basecamp/.claude-plugin/plugin.json new file mode 100644 index 0000000..0468464 --- /dev/null +++ b/plugins/basecamp/.claude-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "basecamp", + "displayName": "Basecamp", + "version": "0.1.0", + "description": "Work in your Basecamp projects from Claude: catch up on a project, recap your week, triage your Hey! menu, plan work into to-dos and cards, draft a check-in answer, and prep for a meeting, through Basecamp's own hosted connector.", + "author": { + "name": "37signals", + "email": "support@37signals.com", + "url": "https://basecamp.com" + }, + "homepage": "https://basecamp.com", + "repository": "https://github.com/basecamp/claude-plugins", + "license": "MIT", + "keywords": [ + "basecamp", + "projects", + "to-dos", + "project management", + "check-ins" + ] +} diff --git a/plugins/basecamp/.mcp.json b/plugins/basecamp/.mcp.json new file mode 100644 index 0000000..3a30f34 --- /dev/null +++ b/plugins/basecamp/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "basecamp": { + "type": "http", + "url": "https://mcp.basecamp.com/mcp" + } + } +} diff --git a/plugins/basecamp/README.md b/plugins/basecamp/README.md new file mode 100644 index 0000000..2790cdf --- /dev/null +++ b/plugins/basecamp/README.md @@ -0,0 +1,58 @@ +# Basecamp + +Bring your Basecamp projects into Claude. Ask what happened on a project +while you were out, what's on your plate this week, or what a long thread +actually decided. Then have Claude do the follow-through: add and assign +to-dos, move cards, post a comment, draft a check-in answer, write up an +agenda. + +This plugin pairs Basecamp's own hosted connector with a skill for working +in Basecamp and six ready-made workflows. + +## Get started + +1. Add the plugin, then open its **Connectors** tab and connect Basecamp. +2. Sign in to Basecamp and choose **Full access**, or **Read-only** if you + only want Claude to read. +3. Ask away: "What's assigned to me in Basecamp this week?" + +You need a Basecamp account. There's no extra charge. + +## Workflows + +In Cowork and Claude Code, run them as commands. In chat, just ask, and +Claude picks the one that fits. + +| Command | What it does | +| --- | --- | +| `/basecamp:catch-up ` | What changed on a project, what was decided, what's open, and what needs you | +| `/basecamp:weekly-recap` | What you finished, what moved across your projects, and what's next week | +| `/basecamp:triage-hey` | Sorts your Hey! menu into needs-you, worth-knowing, and nothing-to-do, with draft replies | +| `/basecamp:plan-project ` | Turns a goal or notes into to-do lists or cards with owners and dates | +| `/basecamp:draft-check-in [question]` | Drafts your answer to an automatic check-in from what you actually did | +| `/basecamp:meeting-prep ` | A one-page brief and agenda from the project's recent activity | + +Claude shows you a plan or a draft before it creates or posts anything, +and every workflow that only reads leaves your Basecamp untouched. + +## What it can work with + +Projects and the people on them; to-dos and to-do lists; messages, +comments and boosts; Card Tables, cards and steps; schedules; Docs & Files; +Campfire chats and Pings; automatic check-ins; your assignments and Hey! +menu; and search across your account. + +## Your data + +The plugin connects to one place, Basecamp's hosted connector at +`https://mcp.basecamp.com/mcp`, run by 37signals. It acts as you: Claude +sees only what you can see in Basecamp and can change only what you can +change there. The plugin itself is instructions and stores nothing; the +connector passes Claude's requests to Basecamp's API on your behalf. Most +things you remove go to the Basecamp trash, where +they can be restored. Disconnect any time from the plugin's Connectors tab +or from Connected apps in your Basecamp profile menu. + +- Privacy policy: https://37signals.com/policies/privacy +- Terms: https://37signals.com/policies/terms +- Support: https://basecamp.com/support diff --git a/plugins/basecamp/commands/catch-up.md b/plugins/basecamp/commands/catch-up.md new file mode 100644 index 0000000..81024b5 --- /dev/null +++ b/plugins/basecamp/commands/catch-up.md @@ -0,0 +1,30 @@ +--- +description: Catch up on a Basecamp project — what changed, what was decided, what's still open, and what needs you. +argument-hint: +--- + +# Catch up on a project + +The project to catch up on: $ARGUMENTS + +1. **Find the project.** A link: `get_by_url`. A name: `list_projects`, or + `search` if the list is long. If several match, ask which; if none was + named, ask. +2. **Read what happened.** `get_project_timeline` for the recent activity. + `get_project` for its dock, then `list_messages` on its message board for + the latest discussions. Open what matters with `get_by_url` on its + `app_url`: a decision, a long thread, a document that changed. +3. **Who's asking.** `get_me` names the person, so you can tell what's + theirs. + +Reply in four short parts, linking every item to its `app_url`: + +- **What changed** in about the last week, or since the date they give: the + handful of things that matter, not every event. +- **Decided:** each decision, quoting the line that settled it. +- **Open:** unanswered questions, overdue or unassigned to-dos, stuck cards. +- **Needs you:** what's assigned to them, mentions them, or waits on their + answer. + +This is a read: post nothing and change nothing. Offer to dig into any +thread. diff --git a/plugins/basecamp/commands/draft-check-in.md b/plugins/basecamp/commands/draft-check-in.md new file mode 100644 index 0000000..4f1f5c6 --- /dev/null +++ b/plugins/basecamp/commands/draft-check-in.md @@ -0,0 +1,26 @@ +--- +description: Draft your answer to a Basecamp automatic check-in, like "What did you work on this week?", from your own recent activity. +argument-hint: "[check-in question link or project]" +--- + +# Draft a check-in answer + +Which check-in: $ARGUMENTS + +1. **Find the question.** A link: `get_by_url`. A project: `get_project` + for its dock, then `get_questionnaire` and `list_questions`. If they + didn't say, ask which check-in. +2. **Read their voice.** `get_me`, then `get_answers_by_person` with the + question and their `person_id` for their last few answers: length, tone, + format. +3. **Gather what they did** over the question's period (a week, unless the + question says otherwise): `get_catchup` with `days`, + `get_my_completed_assignments`, and `get_project_timeline` on the projects + they were active in. + +Draft the answer the way they write: what they finished, what's in +progress, what's next, and anything blocked, linking to the work. Show the +draft and ask what to change. + +Post only when they say to: `create_answer` with the question and the +answer as HTML in `content`. Then link to it. diff --git a/plugins/basecamp/commands/meeting-prep.md b/plugins/basecamp/commands/meeting-prep.md new file mode 100644 index 0000000..7d0ba3c --- /dev/null +++ b/plugins/basecamp/commands/meeting-prep.md @@ -0,0 +1,34 @@ +--- +description: Prep for a meeting about a Basecamp project — recent decisions, open questions, what's late, and a draft agenda. +argument-hint: +--- + +# Prep for a meeting + +The project and the meeting: $ARGUMENTS + +1. **Find the project.** A link: `get_by_url`. A name: `list_projects` or + `search`. If none was named, ask. +2. **Read the state of things.** + - `get_project_timeline` for what happened since the last meeting (a + week, unless they say). + - `get_project` for its dock, then `list_messages` on its message board, + and `get_by_url` on the threads that matter. + - Open work: `list_todolists` and `list_todos`, or `get_card_table`, for + what's overdue, unassigned, or stuck. + - `list_schedule_entries` on its schedule (`schedule_id` from the dock) + for dates coming up. + - `search` for the meeting's topic, to find the docs and threads behind + it. + +Reply with a one-page brief, linking every item to its `app_url`: + +- **Since last time:** decisions and changes, a line each. +- **Open questions:** each with who raised it. +- **At risk:** what's late or blocked, and who owns it. +- **Agenda:** five or fewer items, in order, each with the decision the + meeting needs to make. + +If they want it in Basecamp, offer to save the agenda as a document +(`create_document` in the project's Docs & Files, `vault_id` from the +dock), as a draft unless they say to publish it. diff --git a/plugins/basecamp/commands/plan-project.md b/plugins/basecamp/commands/plan-project.md new file mode 100644 index 0000000..e147dc7 --- /dev/null +++ b/plugins/basecamp/commands/plan-project.md @@ -0,0 +1,31 @@ +--- +description: Turn a goal, brief, or meeting notes into a Basecamp plan — to-do lists and to-dos with owners and dates, or cards on a Card Table. +argument-hint: +--- + +# Plan it in Basecamp + +What to plan, and where: $ARGUMENTS + +1. **Find the project.** A link: `get_by_url`. A name: `list_projects` or + `search`. If none fits, offer to create one with `create_project`, and + only create it once they agree. +2. **Read its dock.** `get_project` lists its tools. Plan into to-dos unless + they asked for cards or the project works from a Card Table + (`get_card_table` shows its columns). +3. **Find the people.** `list_project_people` for who can own each piece. + Don't guess an owner they didn't name. +4. **Propose the plan first**, as an outline: lists (or columns) with their + to-dos (or cards), each with an owner and a due date where one is clear. + Ask what to change. +5. **Create it once they approve.** + - To-dos: `create_todolist` in the project's to-do set (`todoset_id` from + the dock), then `create_todo` for each, with `assignee_ids` and + `due_on` (`YYYY-MM-DD`). + - Cards: `create_card` in the right column (`column_id`), then + `update_card` to set `assignee_ids`. + Leave `notify` off unless they want people notified now. + +Reply with what was created, linked, and anything you skipped and why. The +`posting-and-mentions` topic of `get_basecamp_guide` says who each post +notifies. diff --git a/plugins/basecamp/commands/triage-hey.md b/plugins/basecamp/commands/triage-hey.md new file mode 100644 index 0000000..0ac82ba --- /dev/null +++ b/plugins/basecamp/commands/triage-hey.md @@ -0,0 +1,27 @@ +--- +description: Triage your Basecamp Hey! menu — sort what needs a reply from what's just FYI, draft replies, and clear what's handled. +--- + +# Triage my Hey! + +1. `get_my_notifications`: unreads first. Use `page` for more if the first + page is all unread. +2. Open anything you can't judge from its excerpt with `get_by_url` on its + `app_url`. +3. Sort each unread into one of three groups: + - **Needs you:** a direct question, a mention asking for something, an + assignment, a decision waiting on them. + - **Worth knowing:** announcements and updates on their work. + - **Nothing to do:** boosts, automatic notices, threads that already + moved on. + +Reply with the three groups, each item a line with who, the project, the +gist, and a link. For **Needs you**, offer a short draft reply for each. + +Act only when the person says so: + +- **Reply:** `get_by_url` on the item gives the `reply_target`; post the + reply there, quoting it back to them first. Use `mentions` with person IDs + to @-mention someone. No `reply_target` means a reply can't be posted from + here; say so. +- **Clear:** `mark_as_read` with the `readables` of the items they choose. diff --git a/plugins/basecamp/commands/weekly-recap.md b/plugins/basecamp/commands/weekly-recap.md new file mode 100644 index 0000000..43ce587 --- /dev/null +++ b/plugins/basecamp/commands/weekly-recap.md @@ -0,0 +1,29 @@ +--- +description: Recap your week in Basecamp — what you finished, what moved across your projects, and what's coming up next week. +argument-hint: "[days back, up to 14]" +--- + +# Recap my week + +How far back: $ARGUMENTS (a number of days, up to 14; a week if nothing was +given). + +1. `get_catchup` with `days` for announcements, to-dos assigned to and + completed by the person, their overdue to-dos, and activity by project. +2. `get_my_completed_assignments` for what they finished, and + `get_my_assignments` for what's still on their plate. +3. `get_upcoming_schedule` with `start_date` today and `end_date` a week out + for what's next. +4. For the two or three busiest projects in the catch-up, `get_project_timeline` + to say what actually moved, not just how much. + +Reply with: + +- **Done:** what they finished, grouped by project. +- **Moved:** the notable changes in their projects, a line each. +- **Still open:** overdue first, then what's due next week. +- **Coming up:** schedule entries next week. + +Link every item to its `app_url`, name the project, and keep it to what a +person would read in two minutes. If they want to share it, offer to draft +it as a check-in answer or a message, and post only once they say so. diff --git a/plugins/basecamp/skills/basecamp/SKILL.md b/plugins/basecamp/skills/basecamp/SKILL.md new file mode 100644 index 0000000..04bb55c --- /dev/null +++ b/plugins/basecamp/skills/basecamp/SKILL.md @@ -0,0 +1,77 @@ +--- +name: basecamp +description: Work with Basecamp through the Basecamp tools — find, read, create, and update projects, to-dos, cards, messages, comments, documents, schedules, and Campfire chats. Use for any Basecamp question or action, and for any basecamp.com link. +--- + +# Basecamp + +Every Basecamp action is its own tool, named for what it does: `get_*` and +`list_*` read; `create_*`, `update_*`, `complete_*`, `move_*`, `trash_*` and +the rest change data. Everything runs as the signed-in person and sees only +what they can see. + +The server keeps the reference notes on how Basecamp works: +`get_basecamp_guide` returns the core concepts with no `topic`, or one +topic — `concepts` (accounts, projects, the dock, and the IDs each tool +takes), `finding-things`, `my-work`, `posting-and-mentions` (HTML, mentions, +who is notified), and `deleting-and-trash`. Read the topic before a task it +covers that you have not done in this conversation. + +## Where to start + +- **A Basecamp link?** Pass it to `get_by_url` as `url`. It returns the IDs + the link names, the record itself, and, for anything that takes comments + when this connection can post them, `reply_target`: the tool and IDs a + reply goes to. A link to a comment answers with the comment and its + parent as the reply target, since comments are not threaded. No + `reply_target` means no reply can be posted from here. Don't read IDs out + of the link yourself. +- **By keyword?** `search`, then `get_by_url` with a result's `app_url` for + the full record. +- **Mine?** `get_my_assignments` (priorities first), `get_my_day` (today's + schedule and due to-dos), `get_overdue_todos`, `get_upcoming_schedule`, + `get_my_notifications` (the Hey! menu: unreads first), `get_catchup` + (what happened while away). +- **In a project?** `list_projects` or `search` to find it, then + `get_project` for its dock: each dock tool's ID is what that tool's + `list_*` and `create_*` calls take. +- **Long results** come a page at a time. A result carrying `next_cursor` + or `next_page` has more: pass that value back as `cursor` or `page` to + continue. + +## Accounts and projects + +Each connection works in one Basecamp account; `get_me` names the +signed-in person and that account. A link into another account is out of +reach from this connection — `get_by_url` says so; tell the person rather +than guess. When a request needs a project and doesn't name one, ask which +— don't pick one for them. + +## Writing + +- **Rich text** — message bodies, comments, documents, to-do and card notes + — is HTML; titles, to-do names and Campfire lines are plain text. The + `posting-and-mentions` topic lists the tags Basecamp keeps. +- **@-mentions.** `create_comment` takes `mentions`: person IDs from + `list_pingable_people` or `list_project_people`. +- **Assigning.** `assignee_ids` replaces the whole list; include everyone + who stays assigned. Dates are `YYYY-MM-DD`. +- **Notifications.** Posting can notify people, but not always: a message + or document created without `status: "active"` is an unposted draft, and + a to-do or event notifies only when `notify` is true. The + `posting-and-mentions` topic says who each post notifies; report what was + actually sent. Confirm with the person before posting on their behalf to + others, and quote what will be posted. + +## Removing things + +`trash_*` tools move an item to the Basecamp trash, where it can be +restored. Tools whose description says "Permanently" cannot be undone: run +one only when the person asked for exactly that deletion. The +`deleting-and-trash` topic covers the difference, and archiving. + +## Replying well + +Link to what you mention: every item carries an `app_url`. Name the project +and, when the person has several accounts, the account. Say plainly when +something could not be found or read rather than filling the gap. From 9baea1efb96bc40fc38da1211cbf58c01ccb4b6a Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 5 Oct 2026 16:27:41 -0700 Subject: [PATCH 2/5] Rename the CLI plugin basecamp-cli and leave basecamp unlisted for now basecamp-cli's setup now treats any installed basecamp@37signals as the old CLI plugin and moves it to basecamp-cli. Listing the hosted plugin as basecamp at the same time would get it uninstalled by setup, and would hand it to anyone still on the old install at their next update. So the marketplace drops the name for a deprecation window. plugins/basecamp stays here for Anthropic's directory, unlisted. bin/check-marketplace, run in CI, fails on duplicate names or on a basecamp entry, and says when that rule can go: after the window, and after a CLI release that stops treating basecamp@37signals as stale. --- .claude-plugin/marketplace.json | 10 -------- .github/workflows/check.yml | 18 +++++++++++++ README.md | 43 ++++++++++++++++--------------- bin/check-marketplace | 45 +++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/check.yml create mode 100755 bin/check-marketplace diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 1265456..cd1a7d0 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,16 +6,6 @@ "email": "support@37signals.com" }, "plugins": [ - { - "name": "basecamp", - "description": "Basecamp for Claude, through Basecamp's hosted connector. Works in chat, Cowork, and Claude Code with no CLI to install: catch up on projects, recap your week, triage Hey!, plan work, draft check-ins.", - "source": { - "source": "git-subdir", - "url": "https://github.com/basecamp/claude-plugins.git", - "path": "plugins/basecamp" - }, - "category": "productivity" - }, { "name": "basecamp-cli", "description": "CLI-based Basecamp for Claude Code and Codex, driven by the basecamp command. Create todos, track work, link code to projects.", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..4ab7259 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,18 @@ +name: Check + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + marketplace: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - run: bin/check-marketplace diff --git a/README.md b/README.md index eb204ea..9cab2b6 100644 --- a/README.md +++ b/README.md @@ -14,30 +14,18 @@ claude plugin install | Plugin | Description | |--------|-------------| -| [basecamp](plugins/basecamp) | Basecamp through its hosted connector, for claude.ai chat, Cowork, and Claude Code. Six workflows: catch up, weekly recap, triage Hey!, plan a project, draft a check-in, prep a meeting. | | [basecamp-cli](https://github.com/basecamp/basecamp-cli/tree/main/.claude-plugin) | Basecamp through the `basecamp` command, for Claude Code and Codex. Todos, cards, messages, files, schedule, check-ins, timeline, recordings, templates, webhooks, subscriptions, lineup, and campfire; links code to projects. | | [hey](https://github.com/basecamp/hey-cli) | Read email, manage todos, track time. | | [fizzy](https://github.com/basecamp/fizzy-cli) | Manage boards and cards, track work, link code to projects. | -### Which Basecamp plugin? +### `basecamp` is now `basecamp-cli` -- **`basecamp`** talks to Basecamp's hosted connector at - `https://mcp.basecamp.com/mcp`. Nothing to install beyond the plugin: you - connect your Basecamp account from the plugin's Connectors tab. It's the - same plugin Anthropic's directory lists, and the only one that works in - claude.ai chat and Cowork. -- **`basecamp-cli`** drives the [`basecamp` CLI](https://github.com/basecamp/basecamp-cli) - on your machine. Pick it for terminal work in Claude Code or Codex: - scripting, full API coverage, and linking commits and branches to - Basecamp. It needs the CLI installed and signed in. - -You can have both; they don't share state. - -### Moving from the old `basecamp` plugin - -Until October 2026, `basecamp@37signals` was the CLI plugin. It's now -`basecamp-cli@37signals`, and `basecamp@37signals` is the hosted plugin. If -you installed the CLI plugin before the rename, switch to its new name: +The CLI plugin used to be `basecamp@37signals`. It's now +`basecamp-cli@37signals`, with the same source, skills and hooks. The +`basecamp` command itself keeps its name. An install under the old name +stops loading ("Plugin basecamp not found in marketplace 37signals") once +Claude Code refreshes this marketplace. `basecamp setup` in a current CLI +moves it for you. To do it by hand: ``` claude plugin marketplace update 37signals @@ -45,7 +33,19 @@ claude plugin uninstall basecamp@37signals claude plugin install basecamp-cli@37signals ``` -Then install `basecamp@37signals` as well if you want the hosted one. +### The hosted Basecamp plugin + +`plugins/basecamp` is a second Basecamp plugin. It uses Basecamp's hosted +connector at `https://mcp.basecamp.com/mcp` rather than the CLI, so it also +works in claude.ai chat and Cowork. This is the folder Anthropic's plugin +directory publishes as `basecamp`. + +This marketplace doesn't list it yet. After a deprecation window for the +old `basecamp@37signals` name, the marketplace will add it as `basecamp`. +Waiting means nobody still on the old install gets a different plugin +under the same id on their next update. `bin/check-marketplace`, which CI +runs, fails if a `basecamp` entry appears before then. The script says when +it can go. ## Development plugins @@ -62,7 +62,8 @@ This repo is mostly a thin marketplace index. Plugin source code (skills, hooks, commands) lives in each product's own repo. To contribute, open issues or PRs there. -The one plugin with files here, `plugins/basecamp`, is a mirror. Its source +The one plugin with files here, `plugins/basecamp` (the hosted plugin, not +yet listed in the marketplace), is a mirror. Its source of truth is `plugins/claude/basecamp` in the (private) Basecamp connector repository, which tests it against the tools the connector actually serves. Don't edit the mirror; change it upstream, then run diff --git a/bin/check-marketplace b/bin/check-marketplace new file mode 100755 index 0000000..979f910 --- /dev/null +++ b/bin/check-marketplace @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Checks .claude-plugin/marketplace.json. Run by CI on every push and pull request. +# +# 1. It parses, and every plugin name is unique. +# 2. It does not list a plugin named "basecamp". +# +# Rule 2 exists because "basecamp@37signals" used to be the CLI plugin, now +# "basecamp-cli". basecamp-cli's `basecamp setup` treats any installed +# basecamp@37signals as that old plugin and uninstalls it (basecamp/basecamp-cli#840). +# Listing the hosted plugin (plugins/basecamp) as "basecamp" now would get it +# uninstalled by setup, and would hand the hosted plugin to anyone still on the +# old CLI install at their next update. +# +# Remove rule 2 only when both hold: +# - the deprecation window for the old basecamp@37signals name has passed, and +# - a basecamp-cli release has shipped that no longer treats basecamp@37signals as stale. +set -euo pipefail + +root="$(cd "$(dirname "$0")/.." && pwd)" +marketplace="$root/.claude-plugin/marketplace.json" + +python3 - "$marketplace" <<'PY' +import collections, json, sys + +with open(sys.argv[1]) as f: + names = [p["name"] for p in json.load(f)["plugins"]] + +failed = False +for name, count in collections.Counter(names).items(): + if count > 1: + print(f"error: plugin name {name!r} is listed {count} times", file=sys.stderr) + failed = True + +if "basecamp" in names: + print( + "error: the marketplace lists a plugin named 'basecamp'. That name stays unlisted until the\n" + "old basecamp@37signals deprecation window has passed and a basecamp-cli release no\n" + "longer treats basecamp@37signals as stale. See bin/check-marketplace.", + file=sys.stderr, + ) + failed = True + +sys.exit(1 if failed else 0) +PY +echo "marketplace.json OK" From e04657c41ae5e9673ae3d757419dcd2ffbc1d672 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 5 Oct 2026 17:03:06 -0700 Subject: [PATCH 3/5] Keep basecamp as an alias for basecamp-cli through the deprecation window Dropping the name broke every existing install: once the marketplace refreshed, basecamp@37signals failed to load. Now basecamp stays listed, with exactly basecamp-cli's source, so those installs keep loading and updating, and basecamp setup moves them over at the user's pace. bin/check-marketplace allows a basecamp entry only as that alias. Pointing it anywhere else, such as the hosted plugin, still fails until the window ends (30 days proposed) and a CLI release stops treating basecamp@37signals as the old CLI plugin. Removing the alias is its own later PR. --- .claude-plugin/marketplace.json | 9 +++++++ README.md | 29 +++++++++++--------- bin/check-marketplace | 48 ++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index cd1a7d0..2426006 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,6 +6,15 @@ "email": "support@37signals.com" }, "plugins": [ + { + "name": "basecamp", + "description": "Deprecated alias for basecamp-cli, kept so existing installs keep working. Install basecamp-cli instead, or run `basecamp setup` to switch.", + "source": { + "source": "url", + "url": "https://github.com/basecamp/basecamp-cli.git" + }, + "category": "productivity" + }, { "name": "basecamp-cli", "description": "CLI-based Basecamp for Claude Code and Codex, driven by the basecamp command. Create todos, track work, link code to projects.", diff --git a/README.md b/README.md index 9cab2b6..61447db 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,23 @@ claude plugin install The CLI plugin used to be `basecamp@37signals`. It's now `basecamp-cli@37signals`, with the same source, skills and hooks. The -`basecamp` command itself keeps its name. An install under the old name -stops loading ("Plugin basecamp not found in marketplace 37signals") once -Claude Code refreshes this marketplace. `basecamp setup` in a current CLI -moves it for you. To do it by hand: +`basecamp` command itself keeps its name. + +For a deprecation window (proposed: 30 days), `basecamp` stays in this +marketplace as an alias with the same source as `basecamp-cli`. Existing +installs keep loading and updating. From the basecamp-cli release with the +rename on, a plugin installed under the old name says once that it has a +new name. `basecamp setup` moves you to `basecamp-cli`. To +do it by hand: ``` claude plugin marketplace update 37signals -claude plugin uninstall basecamp@37signals claude plugin install basecamp-cli@37signals +claude plugin uninstall basecamp@37signals ``` +The alias goes away in its own later PR, after the window. + ### The hosted Basecamp plugin `plugins/basecamp` is a second Basecamp plugin. It uses Basecamp's hosted @@ -40,12 +46,11 @@ connector at `https://mcp.basecamp.com/mcp` rather than the CLI, so it also works in claude.ai chat and Cowork. This is the folder Anthropic's plugin directory publishes as `basecamp`. -This marketplace doesn't list it yet. After a deprecation window for the -old `basecamp@37signals` name, the marketplace will add it as `basecamp`. -Waiting means nobody still on the old install gets a different plugin -under the same id on their next update. `bin/check-marketplace`, which CI -runs, fails if a `basecamp` entry appears before then. The script says when -it can go. +This marketplace doesn't list it. `basecamp` here is the CLI alias until +the window ends, and only after that can the name point at the hosted +plugin. `bin/check-marketplace`, which CI runs, holds that rule: a +`basecamp` entry passes only with exactly the same source as +`basecamp-cli`. The script says when the rule can change. ## Development plugins @@ -63,7 +68,7 @@ hooks, commands) lives in each product's own repo. To contribute, open issues or PRs there. The one plugin with files here, `plugins/basecamp` (the hosted plugin, not -yet listed in the marketplace), is a mirror. Its source +listed in the marketplace), is a mirror. Its source of truth is `plugins/claude/basecamp` in the (private) Basecamp connector repository, which tests it against the tools the connector actually serves. Don't edit the mirror; change it upstream, then run diff --git a/bin/check-marketplace b/bin/check-marketplace index 979f910..bec9255 100755 --- a/bin/check-marketplace +++ b/bin/check-marketplace @@ -2,18 +2,23 @@ # Checks .claude-plugin/marketplace.json. Run by CI on every push and pull request. # # 1. It parses, and every plugin name is unique. -# 2. It does not list a plugin named "basecamp". +# 2. A plugin named "basecamp", if listed, has exactly the same source as +# "basecamp-cli": a deprecated alias for it. # -# Rule 2 exists because "basecamp@37signals" used to be the CLI plugin, now -# "basecamp-cli". basecamp-cli's `basecamp setup` treats any installed -# basecamp@37signals as that old plugin and uninstalls it (basecamp/basecamp-cli#840). -# Listing the hosted plugin (plugins/basecamp) as "basecamp" now would get it -# uninstalled by setup, and would hand the hosted plugin to anyone still on the -# old CLI install at their next update. +# "basecamp@37signals" used to be the CLI plugin, now "basecamp-cli". During the +# deprecation window the alias keeps existing installs loading, and +# basecamp-cli's `basecamp setup` moves them to basecamp-cli by key: it treats an +# installed basecamp@37signals as the CLI plugin (basecamp/basecamp-cli#840). +# Pointing "basecamp" anywhere else, such as the hosted plugin in +# plugins/basecamp, would swap a different plugin in under existing installs, +# and setup would uninstall it. # -# Remove rule 2 only when both hold: -# - the deprecation window for the old basecamp@37signals name has passed, and -# - a basecamp-cli release has shipped that no longer treats basecamp@37signals as stale. +# Rule 2 changes only in its own PR, once both hold: +# - the deprecation window has passed (proposed: 30 days from the rename), and +# - a basecamp-cli release has shipped that no longer treats basecamp@37signals +# as the old CLI plugin. +# That PR first removes the alias. Pointing "basecamp" at the hosted plugin is a +# later decision still. set -euo pipefail root="$(cd "$(dirname "$0")/.." && pwd)" @@ -23,7 +28,8 @@ python3 - "$marketplace" <<'PY' import collections, json, sys with open(sys.argv[1]) as f: - names = [p["name"] for p in json.load(f)["plugins"]] + plugins = json.load(f)["plugins"] +names = [p["name"] for p in plugins] failed = False for name, count in collections.Counter(names).items(): @@ -31,14 +37,18 @@ for name, count in collections.Counter(names).items(): print(f"error: plugin name {name!r} is listed {count} times", file=sys.stderr) failed = True -if "basecamp" in names: - print( - "error: the marketplace lists a plugin named 'basecamp'. That name stays unlisted until the\n" - "old basecamp@37signals deprecation window has passed and a basecamp-cli release no\n" - "longer treats basecamp@37signals as stale. See bin/check-marketplace.", - file=sys.stderr, - ) - failed = True +by_name = {p["name"]: p for p in plugins} +alias = by_name.get("basecamp") +if alias is not None: + target = by_name.get("basecamp-cli") + if target is None or alias.get("source") != target.get("source"): + print( + "error: the 'basecamp' entry must be a deprecated alias with exactly the same source as\n" + "'basecamp-cli' until the deprecation window ends and a basecamp-cli release stops\n" + "treating basecamp@37signals as the old CLI plugin. See bin/check-marketplace.", + file=sys.stderr, + ) + failed = True sys.exit(1 if failed else 0) PY From 386631c6df9d948afd4cc6932bb7cb2ca8d390a3 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 5 Oct 2026 17:34:32 -0700 Subject: [PATCH 4/5] Set the basecamp alias window to 7 days --- README.md | 2 +- bin/check-marketplace | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 61447db..036072c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The CLI plugin used to be `basecamp@37signals`. It's now `basecamp-cli@37signals`, with the same source, skills and hooks. The `basecamp` command itself keeps its name. -For a deprecation window (proposed: 30 days), `basecamp` stays in this +For a deprecation window (7 days from the CLI release that ships the rename), `basecamp` stays in this marketplace as an alias with the same source as `basecamp-cli`. Existing installs keep loading and updating. From the basecamp-cli release with the rename on, a plugin installed under the old name says once that it has a diff --git a/bin/check-marketplace b/bin/check-marketplace index bec9255..2c53564 100755 --- a/bin/check-marketplace +++ b/bin/check-marketplace @@ -14,7 +14,7 @@ # and setup would uninstall it. # # Rule 2 changes only in its own PR, once both hold: -# - the deprecation window has passed (proposed: 30 days from the rename), and +# - the deprecation window has passed (7 days from the CLI release that ships the rename), and # - a basecamp-cli release has shipped that no longer treats basecamp@37signals # as the old CLI plugin. # That PR first removes the alias. Pointing "basecamp" at the hosted plugin is a From 5c19ad6bebee9657419420c2ecfa90d2f0c901e3 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Tue, 6 Oct 2026 18:20:20 -0700 Subject: [PATCH 5/5] Sync the hosted basecamp plugin from basecamp-mcp-server main Mirrored from basecamp/basecamp-mcp-server@b65ae99e4f1870fa7e56331199c4d91f44bf60c6, which contains #195 (merged at 47e67f0). --- plugins/basecamp/commands/catch-up.md | 7 +++++++ plugins/basecamp/commands/draft-check-in.md | 13 ++++++++++--- plugins/basecamp/commands/meeting-prep.md | 9 ++++++--- plugins/basecamp/commands/plan-project.md | 5 ++++- plugins/basecamp/commands/triage-hey.md | 5 +++-- plugins/basecamp/commands/weekly-recap.md | 14 ++++++++++---- plugins/basecamp/skills/basecamp/SKILL.md | 9 +++++++++ 7 files changed, 49 insertions(+), 13 deletions(-) diff --git a/plugins/basecamp/commands/catch-up.md b/plugins/basecamp/commands/catch-up.md index 81024b5..2bd47c3 100644 --- a/plugins/basecamp/commands/catch-up.md +++ b/plugins/basecamp/commands/catch-up.md @@ -11,11 +11,18 @@ The project to catch up on: $ARGUMENTS `search` if the list is long. If several match, ask which; if none was named, ask. 2. **Read what happened.** `get_project_timeline` for the recent activity. + It returns the latest 100 events and can't page, so if its oldest event + is newer than the start of the window, say the catch-up starts there. `get_project` for its dock, then `list_messages` on its message board for the latest discussions. Open what matters with `get_by_url` on its `app_url`: a decision, a long thread, a document that changed. 3. **Who's asking.** `get_me` names the person, so you can tell what's theirs. +4. **What's open now.** The timeline only shows what moved recently, so read + current state too, keeping this project's group from each: + `list_everything_todos` with `state` set to overdue, then to unassigned; + `list_everything_cards` with `state` set to overdue; and `get_my_assignments` + for what's on them. Reply in four short parts, linking every item to its `app_url`: diff --git a/plugins/basecamp/commands/draft-check-in.md b/plugins/basecamp/commands/draft-check-in.md index 4f1f5c6..88856f1 100644 --- a/plugins/basecamp/commands/draft-check-in.md +++ b/plugins/basecamp/commands/draft-check-in.md @@ -14,9 +14,16 @@ Which check-in: $ARGUMENTS question and their `person_id` for their last few answers: length, tone, format. 3. **Gather what they did** over the question's period (a week, unless the - question says otherwise): `get_catchup` with `days`, - `get_my_completed_assignments`, and `get_project_timeline` on the projects - they were active in. + question says otherwise): `get_catchup` with `days` (its finished to-dos + are scoped to that window), and `get_project_timeline` on the projects + they were active in. Timelines and the catch-up's activity counts include + teammates' work: count an event as theirs only when its creator is the + person from `get_me`, and use the rest as context. `get_catchup` reaches back 14 days at most, so for a + longer period, such as a monthly check-in, say the draft covers only the + last 14 days. `get_my_completed_assignments` carries no completion dates, + so don't count its items as done in this period. For what's in + progress, next, and blocked, read `get_my_assignments`: current work + with no recent activity shows up only there. Draft the answer the way they write: what they finished, what's in progress, what's next, and anything blocked, linking to the work. Show the diff --git a/plugins/basecamp/commands/meeting-prep.md b/plugins/basecamp/commands/meeting-prep.md index 7d0ba3c..08fec1c 100644 --- a/plugins/basecamp/commands/meeting-prep.md +++ b/plugins/basecamp/commands/meeting-prep.md @@ -11,13 +11,16 @@ The project and the meeting: $ARGUMENTS `search`. If none was named, ask. 2. **Read the state of things.** - `get_project_timeline` for what happened since the last meeting (a - week, unless they say). + week, unless they say). It returns the latest 100 events and can't + page; if they don't reach back that far, say where the brief starts. - `get_project` for its dock, then `list_messages` on its message board, and `get_by_url` on the threads that matter. - Open work: `list_todolists` and `list_todos`, or `get_card_table`, for what's overdue, unassigned, or stuck. - - `list_schedule_entries` on its schedule (`schedule_id` from the dock) - for dates coming up. + - `get_upcoming_schedule` from today to two weeks out for dates coming + up, keeping this project's entries and recurring occurrences. + `list_schedule_entries` lists a recurring series once, at its first + date, so it misses the next meeting of a series. - `search` for the meeting's topic, to find the docs and threads behind it. diff --git a/plugins/basecamp/commands/plan-project.md b/plugins/basecamp/commands/plan-project.md index e147dc7..864e456 100644 --- a/plugins/basecamp/commands/plan-project.md +++ b/plugins/basecamp/commands/plan-project.md @@ -24,7 +24,10 @@ What to plan, and where: $ARGUMENTS `due_on` (`YYYY-MM-DD`). - Cards: `create_card` in the right column (`column_id`), then `update_card` to set `assignee_ids`. - Leave `notify` off unless they want people notified now. + For to-dos, leave `notify` off unless they want people notified now. + Cards can't do that: `create_card` takes no assignees, so its `notify` + reaches no one, and `update_card` has no `notify`. If they want card + owners told, say so and offer to post or comment instead. Reply with what was created, linked, and anything you skipped and why. The `posting-and-mentions` topic of `get_basecamp_guide` says who each post diff --git a/plugins/basecamp/commands/triage-hey.md b/plugins/basecamp/commands/triage-hey.md index 0ac82ba..56ec5a6 100644 --- a/plugins/basecamp/commands/triage-hey.md +++ b/plugins/basecamp/commands/triage-hey.md @@ -4,8 +4,9 @@ description: Triage your Basecamp Hey! menu — sort what needs a reply from wha # Triage my Hey! -1. `get_my_notifications`: unreads first. Use `page` for more if the first - page is all unread. +1. `get_my_notifications`: unreads first. Unreads come back in one list, + capped at 100; `page` pages only the read items, so it can't fetch more + unreads. If the list is full, say there may be more. 2. Open anything you can't judge from its excerpt with `get_by_url` on its `app_url`. 3. Sort each unread into one of three groups: diff --git a/plugins/basecamp/commands/weekly-recap.md b/plugins/basecamp/commands/weekly-recap.md index 43ce587..6f6c1b9 100644 --- a/plugins/basecamp/commands/weekly-recap.md +++ b/plugins/basecamp/commands/weekly-recap.md @@ -10,16 +10,22 @@ given). 1. `get_catchup` with `days` for announcements, to-dos assigned to and completed by the person, their overdue to-dos, and activity by project. -2. `get_my_completed_assignments` for what they finished, and - `get_my_assignments` for what's still on their plate. +2. `get_my_assignments` for what's still on their plate. For what they + finished, use only the catch-up's completed to-dos: they're the person's + own and scoped to the window. Project timelines include teammates' + completions, and `get_my_completed_assignments` has no dates. The + catch-up's to-dos carry no `app_url`: `get_todo` on each one you list + gives the link. 3. `get_upcoming_schedule` with `start_date` today and `end_date` a week out - for what's next. + for what's next. It covers the whole account, so keep only entries and + recurring occurrences whose participants include the person (`get_me` + names them). 4. For the two or three busiest projects in the catch-up, `get_project_timeline` to say what actually moved, not just how much. Reply with: -- **Done:** what they finished, grouped by project. +- **Done:** what they finished in the window, grouped by project. - **Moved:** the notable changes in their projects, a line each. - **Still open:** overdue first, then what's due next week. - **Coming up:** schedule entries next week. diff --git a/plugins/basecamp/skills/basecamp/SKILL.md b/plugins/basecamp/skills/basecamp/SKILL.md index 04bb55c..c135cb9 100644 --- a/plugins/basecamp/skills/basecamp/SKILL.md +++ b/plugins/basecamp/skills/basecamp/SKILL.md @@ -75,3 +75,12 @@ one only when the person asked for exactly that deletion. The Link to what you mention: every item carries an `app_url`. Name the project and, when the person has several accounts, the account. Say plainly when something could not be found or read rather than filling the gap. + +**Say what your reads covered.** Every summary, recap, brief or triage +states what its reads did and didn't cover, and never claims more than was +fetched. Reads stop short in known ways: a result marked truncated, a +timeline that ends at its latest 100 events, a window capped at 14 days, an +undated list, a thread whose comments you didn't open, records that include +other people's work. When one applies, say so in the reply ("Done covers +to-dos only", "the timeline reaches back to Tuesday") instead of presenting +the section as complete.