Compare commits

..

5 Commits

Author SHA1 Message Date
Dickson Tsai
986deab6a1 fix: invoke .sh hooks via bash prefix; add version for cache invalidation
Fixes #993 (Permission denied on hook scripts) without relying on
client-side +x preservation.

The hook executor spawns commands via /bin/sh -c, which requires +x
to execute a script directly. Prefixing with bash reads the script
as data — mode bits are irrelevant. This works on all Claude Code
versions, whereas the client-side chmod fix (claude-cli #24666) only
shipped in v2.1.86.

All 3 scripts declare #!/bin/bash and use bashisms ([[ ]], =~), so
bash (not sh) is the correct interpreter.

The version field forces a fresh cache path (1.0.0/ instead of
unknown/), ensuring the new hooks.json reaches users with stale
caches.
2026-03-28 08:40:34 -07:00
russell-coleman
548bfa8375 Merge pull request #1008 from anthropics/russell/imessage-conversational-format
feat(imessage): conversational format for chat_messages
2026-03-27 14:24:14 -07:00
Russell Coleman
a1ffbcc771 feat(imessage): add IMESSAGE_DB_PATH env var for testing
Mirrors the existing IMESSAGE_STATE_DIR override. Lets a mock sqlite
chat.db stand in for ~/Library/Messages/chat.db so chat_messages can be
tested without macOS + Full Disk Access + real iMessage history.
2026-03-27 13:45:56 -07:00
Noah Zweben
451c481b2b Update flint plugin sha to f3d56e3 (#1077)
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-27 09:51:27 -07:00
Russell Coleman
28f7434384 feat(imessage): conversational format for chat_messages
Reformat chat_messages output from flat per-message lines to grouped
conversation threads. Each thread gets a header labelling it DM or Group
with its participant list, date-separator lines when the calendar day
rolls over, and [HH:MM] local-time stamps instead of full ISO.

chat_guid is now optional — omit to dump every allowlisted chat at once
for a quick multi-thread overview. Default limit raised 20→100 per chat,
capped at 500.

New queries: qChatParticipants (handle list per chat) and qChatInfo
(display_name + style to distinguish DM/group). renderMsg replaced by
conversationHeader + renderConversation.
2026-03-25 16:50:00 -07:00
30 changed files with 90 additions and 57 deletions

View File

@@ -464,7 +464,7 @@
"source": {
"source": "url",
"url": "https://github.com/tryflint/claude-code-plugin.git",
"sha": "8f57ef474f59817c7e5930dd6c295232d43c3711"
"sha": "f3d56e33ed2fb3ed9b4f02e0fc65d0a79b24bf4d"
},
"homepage": "https://www.tryflint.com/docs/claude-code-plugin"
},

View File

@@ -1,6 +1,5 @@
{
"name": "asana",
"version": "1.0.0",
"description": "Asana project management integration. Create and manage tasks, search projects, update assignments, track progress, and integrate your development workflow with Asana's work management platform.",
"author": {
"name": "Asana"

View File

@@ -1,6 +1,5 @@
{
"name": "context7",
"version": "1.0.0",
"description": "Upstash Context7 MCP server for up-to-date documentation lookup. Pull version-specific documentation and code examples directly from source repositories into your LLM context.",
"author": {
"name": "Upstash"

View File

@@ -1,6 +1,5 @@
{
"name": "firebase",
"version": "1.0.0",
"description": "Google Firebase MCP integration. Manage Firestore databases, authentication, cloud functions, hosting, and storage. Build and manage your Firebase backend directly from your development workflow.",
"author": {
"name": "Google"

View File

@@ -1,6 +1,5 @@
{
"name": "github",
"version": "1.0.0",
"description": "Official GitHub MCP server for repository management. Create issues, manage pull requests, review code, search repositories, and interact with GitHub's full API directly from Claude Code.",
"author": {
"name": "GitHub"

View File

@@ -1,6 +1,5 @@
{
"name": "gitlab",
"version": "1.0.0",
"description": "GitLab DevOps platform integration. Manage repositories, merge requests, CI/CD pipelines, issues, and wikis. Full access to GitLab's comprehensive DevOps lifecycle tools.",
"author": {
"name": "GitLab"

View File

@@ -1,17 +1,10 @@
{
"name": "greptile",
"version": "1.0.0",
"description": "AI code review agent for GitHub and GitLab. View and resolve Greptile's PR review comments directly from Claude Code.",
"author": {
"name": "Greptile",
"url": "https://greptile.com"
},
"homepage": "https://greptile.com/docs",
"keywords": [
"code-review",
"pull-requests",
"github",
"gitlab",
"ai"
]
"keywords": ["code-review", "pull-requests", "github", "gitlab", "ai"]
}

View File

@@ -76,7 +76,7 @@ Quick reference: IDs are **handle addresses** (`+15551234567` or `someone@icloud
| Tool | Purpose |
| --- | --- |
| `reply` | Send to a chat. `chat_id` + `text`, optional `files` (absolute paths). Auto-chunks text; files send as separate messages. |
| `chat_messages` | Fetch recent history from a chat (oldest-first). Reads `chat.db` directly — full native history. Scoped to allowlisted chats. |
| `chat_messages` | Fetch recent history as conversation threads. Each thread is labelled **DM** or **Group** with its participant list, then timestamped messages (oldest-first). Omit `chat_guid` to see every allowlisted chat at once, or pass one to drill in. Default 100 messages per chat. Reads `chat.db` directly — full native history. |
## What you don't get

View File

@@ -33,7 +33,8 @@ import { join, basename, sep } from 'path'
const STATIC = process.env.IMESSAGE_ACCESS_MODE === 'static'
const APPEND_SIGNATURE = process.env.IMESSAGE_APPEND_SIGNATURE !== 'false'
const SIGNATURE = '\nSent by Claude'
const CHAT_DB = join(homedir(), 'Library', 'Messages', 'chat.db')
const CHAT_DB =
process.env.IMESSAGE_DB_PATH ?? join(homedir(), 'Library', 'Messages', 'chat.db')
const STATE_DIR = process.env.IMESSAGE_STATE_DIR ?? join(homedir(), '.claude', 'channels', 'imessage')
const ACCESS_FILE = join(STATE_DIR, 'access.json')
@@ -141,6 +142,21 @@ const qChatsForHandle = db.query<{ guid: string }, [string]>(`
WHERE c.style = 45 AND LOWER(h.id) = ?
`)
// Participants of a chat (other than yourself). For DMs this is one handle;
// for groups it's everyone in chat_handle_join.
const qChatParticipants = db.query<{ id: string }, [string]>(`
SELECT DISTINCT h.id FROM handle h
JOIN chat_handle_join chj ON chj.handle_id = h.ROWID
JOIN chat c ON c.ROWID = chj.chat_id
WHERE c.guid = ?
`)
// Group-chat display name and style. display_name is NULL for DMs and
// unnamed groups; populated when the user has named the group in Messages.
const qChatInfo = db.query<{ display_name: string | null; style: number }, [string]>(`
SELECT display_name, style FROM chat WHERE guid = ?
`)
type AttRow = { filename: string | null; mime_type: string | null; transfer_name: string | null }
const qAttachments = db.query<AttRow, [number]>(`
SELECT a.filename, a.mime_type, a.transfer_name
@@ -476,15 +492,43 @@ function messageText(r: Row): string {
return r.text ?? parseAttributedBody(r.attributedBody) ?? ''
}
function renderMsg(r: Row): string {
const who = r.is_from_me ? 'me' : (r.handle_id ?? 'unknown')
const ts = appleDate(r.date).toISOString()
const atts = r.cache_has_attachments ? ' +att' : ''
// Tool results are newline-joined; a multi-line message would forge
// adjacent rows. chat_messages is allowlist-scoped, but a configured group
// can still have untrusted members.
const text = messageText(r).replace(/[\r\n]+/g, ' ⏎ ')
return `[${ts}] ${who}: ${text} (id: ${r.guid}${atts})`
// Build a human-readable header for one conversation. Labels DM vs group and
// lists participants so the assistant can tell threads apart at a glance.
function conversationHeader(guid: string): string {
const info = qChatInfo.get(guid)
const participants = qChatParticipants.all(guid).map(p => p.id)
const who = participants.length > 0 ? participants.join(', ') : guid
if (info?.style === 43) {
const name = info.display_name ? `"${info.display_name}" ` : ''
return `=== Group ${name}(${who}) ===`
}
return `=== DM with ${who} ===`
}
// Render one chat's messages as a conversation block: header, then one line
// per message with a local-time stamp. A date line is inserted whenever the
// calendar day rolls over so long histories stay readable without repeating
// the full date on every row.
function renderConversation(guid: string, rows: Row[]): string {
const lines: string[] = [conversationHeader(guid)]
let lastDay = ''
for (const r of rows) {
const d = appleDate(r.date)
const day = d.toDateString()
if (day !== lastDay) {
lines.push(`-- ${day} --`)
lastDay = day
}
const hhmm = d.toTimeString().slice(0, 5)
const who = r.is_from_me ? 'me' : (r.handle_id ?? 'unknown')
const atts = r.cache_has_attachments ? ' [attachment]' : ''
// Tool results are newline-joined; a multi-line message would forge
// adjacent rows. chat_messages is allowlist-scoped, but a configured group
// can still have untrusted members.
const text = messageText(r).replace(/[\r\n]+/g, ' ⏎ ')
lines.push(`[${hhmm}] ${who}: ${text}${atts}`)
}
return lines.join('\n')
}
// --- mcp ---------------------------------------------------------------------
@@ -584,14 +628,19 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
{
name: 'chat_messages',
description:
'Fetch recent messages from an iMessage chat. Reads chat.db directly — full native history. Scoped to allowlisted chats only.',
'Fetch recent iMessage history as readable conversation threads. Each thread is labelled DM or Group with its participant list, followed by timestamped messages. Omit chat_guid to see all allowlisted chats at once; pass a specific chat_guid to drill into one thread. Reads chat.db directly — full native history, scoped to allowlisted chats only.',
inputSchema: {
type: 'object',
properties: {
chat_guid: { type: 'string', description: 'The chat_id from the inbound message.' },
limit: { type: 'number', description: 'Max messages (default 20).' },
chat_guid: {
type: 'string',
description: 'A specific chat_id to read. Omit to read from every allowlisted chat.',
},
limit: {
type: 'number',
description: 'Max messages per chat (default 100, max 500).',
},
},
required: ['chat_guid'],
},
},
],
@@ -639,13 +688,25 @@ mcp.setRequestHandler(CallToolRequestSchema, async req => {
return { content: [{ type: 'text', text: sent === 1 ? 'sent' : `sent ${sent} parts` }] }
}
case 'chat_messages': {
const guid = args.chat_guid as string
const limit = (args.limit as number) ?? 20
if (!allowedChatGuids().has(guid)) {
const guid = args.chat_guid as string | undefined
const limit = Math.min((args.limit as number) ?? 100, 500)
const allowed = allowedChatGuids()
const targets = guid == null ? [...allowed] : [guid]
if (guid != null && !allowed.has(guid)) {
throw new Error(`chat ${guid} is not allowlisted — add via /imessage:access`)
}
const rows = qHistory.all(guid, limit).reverse()
const out = rows.length === 0 ? '(no messages)' : rows.map(renderMsg).join('\n')
if (targets.length === 0) {
return { content: [{ type: 'text', text: '(no allowlisted chats — configure via /imessage:access)' }] }
}
const blocks: string[] = []
for (const g of targets) {
const rows = qHistory.all(g, limit).reverse()
if (rows.length === 0 && guid == null) continue
blocks.push(rows.length === 0
? `${conversationHeader(g)}\n(no messages)`
: renderConversation(g, rows))
}
const out = blocks.length === 0 ? '(no messages)' : blocks.join('\n\n')
return { content: [{ type: 'text', text: out }] }
}
default:

View File

@@ -1,6 +1,5 @@
{
"name": "laravel-boost",
"version": "1.0.0",
"description": "Laravel development toolkit MCP server. Provides intelligent assistance for Laravel applications including Artisan commands, Eloquent queries, routing, migrations, and framework-specific code generation.",
"author": {
"name": "Laravel"

View File

@@ -1,6 +1,5 @@
{
"name": "linear",
"version": "1.0.0",
"description": "Linear issue tracking integration. Create issues, manage projects, update statuses, search across workspaces, and streamline your software development workflow with Linear's modern issue tracker.",
"author": {
"name": "Linear"

View File

@@ -1,6 +1,5 @@
{
"name": "playwright",
"version": "1.0.0",
"description": "Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, click elements, and perform automated browser testing workflows.",
"author": {
"name": "Microsoft"

View File

@@ -1,6 +1,5 @@
{
"name": "serena",
"version": "1.0.0",
"description": "Semantic code analysis MCP server providing intelligent code understanding, refactoring suggestions, and codebase navigation through language server protocol integration.",
"author": {
"name": "Oraios"

View File

@@ -1,6 +1,5 @@
{
"name": "supabase",
"version": "1.0.0",
"description": "Supabase MCP integration for database operations, authentication, storage, and real-time subscriptions. Manage your Supabase projects, run SQL queries, and interact with your backend directly.",
"author": {
"name": "Supabase"

View File

@@ -1,6 +1,5 @@
{
"name": "terraform",
"version": "1.0.0",
"description": "The Terraform MCP Server provides seamless integration with Terraform ecosystem, enabling advanced automation and interaction capabilities for Infrastructure as Code (IaC) development.",
"author": {
"name": "HashiCorp"

View File

@@ -1,6 +1,5 @@
{
"name": "agent-sdk-dev",
"version": "1.0.0",
"description": "Claude Agent SDK Development Plugin",
"author": {
"name": "Anthropic",

View File

@@ -1,9 +1,9 @@
{
"name": "code-review",
"version": "1.0.0",
"description": "Automated code review for pull requests using multiple specialized agents with confidence-based scoring",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"
}
}

View File

@@ -1,9 +1,9 @@
{
"name": "commit-commands",
"version": "1.0.0",
"description": "Streamline your git workflow with simple commands for committing, pushing, and creating pull requests",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"
}
}

View File

@@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh"
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh\""
}
]
}

View File

@@ -1,6 +1,5 @@
{
"name": "feature-dev",
"version": "1.0.0",
"description": "Comprehensive feature development workflow with specialized agents for codebase exploration, architecture design, and quality review",
"author": {
"name": "Anthropic",

View File

@@ -1,6 +1,5 @@
{
"name": "frontend-design",
"version": "1.0.0",
"description": "Frontend design skill for UI/UX implementation",
"author": {
"name": "Anthropic",

View File

@@ -1,6 +1,5 @@
{
"name": "hookify",
"version": "1.0.0",
"description": "Easily create hooks to prevent unwanted behaviors by analyzing conversation patterns",
"author": {
"name": "Anthropic",

View File

@@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh"
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh\""
}
]
}

View File

@@ -1,7 +1,6 @@
{
"name": "mcp-server-dev",
"version": "1.0.0",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude \u2014 guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
"description": "Skills for designing and building MCP servers that work seamlessly with Claude — guides you through deployment models (remote HTTP, MCPB, local), tool design patterns, auth, and interactive MCP apps.",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"

View File

@@ -1,7 +1,6 @@
{
"name": "playground",
"version": "1.0.0",
"description": "Creates interactive HTML playgrounds \u2014 self-contained single-file explorers with visual controls, live preview, and prompt output with copy button",
"description": "Creates interactive HTML playgrounds — self-contained single-file explorers with visual controls, live preview, and prompt output with copy button",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"

View File

@@ -1,6 +1,5 @@
{
"name": "plugin-dev",
"version": "1.0.0",
"description": "Plugin development toolkit with skills for creating agents, commands, hooks, MCP integrations, and comprehensive plugin structure guidance",
"author": {
"name": "Anthropic",

View File

@@ -1,6 +1,5 @@
{
"name": "pr-review-toolkit",
"version": "1.0.0",
"description": "Comprehensive PR review agents specializing in comments, tests, error handling, type design, code quality, and code simplification",
"author": {
"name": "Anthropic",

View File

@@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh\""
}
]
}

View File

@@ -1,6 +1,5 @@
{
"name": "security-guidance",
"version": "1.0.0",
"description": "Security reminder hook that warns about potential security issues when editing files, including command injection, XSS, and unsafe code patterns",
"author": {
"name": "Anthropic",

View File

@@ -1,6 +1,5 @@
{
"name": "skill-creator",
"version": "1.0.0",
"description": "Create new skills, improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, or benchmark skill performance with variance analysis.",
"author": {
"name": "Anthropic",