documentation/RPC.md · main · 2026-09-02

GitSocial JSON-RPC Protocol

JSON-RPC 2.0 interface for editor and client integration. Launched via gitsocial rpc.

Table of Contents


1. Transport

Communication uses JSON-RPC 2.0 over stdio (stdin/stdout). Each message is a single line of JSON terminated by \n. Stderr is reserved for logging.

Client (editor)                    gitsocial rpc
     │                                    │
     │ ── request (stdin) ──────────────► │
     │                                    │
     │ ◄── response (stdout) ─────────── │
     │                                    │
     │ ◄── notification (stdout) ──────── │  (server-initiated, no id)

1.1. Message Format

Requests and responses follow JSON-RPC 2.0. All messages MUST be valid JSON on a single line.

Request:

{"jsonrpc":"2.0","id":1,"method":"social.getPosts","params":{"scope":"timeline","limit":50}}

Success response:

{"jsonrpc":"2.0","id":1,"result":[...]}

Error response:

{"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":"not found","data":{"appCode":"NOT_FOUND"}}}

Server notification (no id):

{"jsonrpc":"2.0","method":"notifications.changed","params":{"unreadCount":3}}

1.2. Batching

Clients MAY send JSON-RPC batch requests (array of request objects). The server MUST respond with a batch response in the same order.


2. Lifecycle

2.1. Startup

The client spawns gitsocial rpc as a subprocess. The server reads from stdin and writes to stdout. The server MUST NOT produce output before receiving initialize.

2.2. Initialize

The first request MUST be initialize. The server opens the cache, resolves the workspace, and returns server capabilities.

Method: initialize

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | workdir | string | yes | Absolute path to the git repository working directory | | cacheDir | string | no | Cache directory (default: ~/.cache/gitsocial) | | clientName | string | no | Client identifier (e.g., "vscode", "neovim") | | clientVersion | string | no | Client version |

Result:

{
  "version": "0.1.0",
  "repoURL": "https://github.com/user/repo",
  "extensions": {
    "social": {"initialized": true, "branch": "gitmsg/social"},
    "pm": {"initialized": true, "branch": "gitmsg/pm"},
    "review": {"initialized": false, "branch": ""},
    "release": {"initialized": false, "branch": ""}
  }
}

2.3. Shutdown

Method: shutdown

Params: none

The server closes the cache, flushes pending writes, and exits with code 0. Clients SHOULD send shutdown before killing the process.

2.4. Ping

Method: ping

Params: none

Result: "pong"

For keepalive and health checks.


3. Error Codes

JSON-RPC 2.0 standard errors:

CodeMeaning
-32700Parse error
-32600Invalid request
-32601Method not found
-32602Invalid params
-32603Internal error

Application errors use the -32000 to -32099 range:

CodeApp CodeMeaning
-32000INTERNALUnexpected server error
-32001NOT_FOUNDItem not found
-32002NOT_A_REPOSITORYWorkdir is not a git repository
-32003NOT_INITIALIZEDExtension not initialized
-32004INVALID_ARGUMENTInvalid parameter value
-32005PERMISSION_DENIEDOperation not permitted
-32006NETWORK_ERRORNetwork operation failed
-32007CONFLICTConcurrent modification conflict
-32010NOT_READYServer not yet initialized

Error responses include the application code in data.appCode for programmatic handling:

{"code":-32001,"message":"post not found","data":{"appCode":"NOT_FOUND"}}

4. Methods

Methods are namespaced as namespace.method. The workdir set during initialize is implicit — individual methods do not accept it.

4.1. Social

social.getPosts

Returns posts for a given scope.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | scope | string | yes | "timeline", "workspace", "mine", "repo:<url>", "list:<id>", "post:<ref>", "thread:<ref>" | | limit | int | no | Max posts to return (0 = all) | | types | string[] | no | Filter by type: "post", "comment", "repost", "quote" | | since | string | no | ISO 8601 timestamp lower bound | | until | string | no | ISO 8601 timestamp upper bound | | sort | string | no | Sort order: "newest" (default), "oldest" |

Result: Post[]

[{
  "id": "#commit:abc123456789@gitmsg/social",
  "repository": "https://github.com/user/repo",
  "branch": "gitmsg/social",
  "author": {"name": "Alice", "email": "[email protected]"},
  "timestamp": "2025-01-06T10:00:00Z",
  "content": "Hello world",
  "type": "post",
  "interactions": {"comments": 2, "reposts": 1, "quotes": 0},
  "isEdited": false,
  "isRetracted": false
}]

social.createPost

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | content | string | yes | Post body |

Result: Post

social.editPost

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Post ref to edit | | content | string | yes | New content |

Result: Post

social.retractPost

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Post ref to retract |

Result: true

social.createComment

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | target | string | yes | Ref of post to comment on | | content | string | yes | Comment body |

Result: Post

social.createRepost

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | target | string | yes | Ref of post to repost |

Result: Post

social.createQuote

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | target | string | yes | Ref of post to quote | | content | string | yes | Quote body |

Result: Post

social.getLists

Params: none

Result: List[]

social.getList

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | id | string | yes | List ID |

Result: List

social.createList

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | id | string | yes | List ID (slug) | | name | string | yes | Display name |

Result: List

social.deleteList

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | id | string | yes | List ID |

Result: true

social.addToList

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | listId | string | yes | List ID | | repoURL | string | yes | Repository URL to add | | branch | string | no | Branch (uses default if omitted) | | allBranches | boolean | no | Follow all branches (stores branch:*). Mutually exclusive with branch. |

Result: string (added repo URL)

social.removeFromList

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | listId | string | yes | List ID | | repoURL | string | yes | Repository URL to remove |

Result: true

social.getRepositories

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | scope | string | no | "all" (default), "list:<id>" |

Result: Repository[]

social.getLogs

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | scope | string | no | Scope filter | | limit | int | no | Max entries | | types | string[] | no | Filter by log entry type | | author | string | no | Filter by author | | after | string | no | ISO 8601 lower bound | | before | string | no | ISO 8601 upper bound |

Result: LogEntry[]


4.2. PM

pm.getIssues

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | no | Repository URL (default: workspace) | | branch | string | no | Branch | | states | string[] | no | Filter: "open", "closed", "canceled" | | limit | int | no | Max results |

Result: Issue[]

pm.getIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Issue ref |

Result: Issue

pm.createIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | subject | string | yes | Issue title | | body | string | no | Issue description | | state | string | no | Initial state (default: "open") | | assignees | string[] | no | Assignee emails | | due | string | no | ISO 8601 due date | | milestone | string | no | Milestone ref | | sprint | string | no | Sprint ref | | parent | string | no | Parent issue ref. root is derived from it per GITPM.md §1.7, so a client normally sends this alone. | | root | string | no | Top-level ancestor ref. Only send this to override the derivation; sending parent alone is the usual case. | | labels | Label[] | no | [{"scope":"priority","value":"high"}] |

Result: Issue

pm.updateIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Issue ref | | subject | string | no | New title | | body | string | no | New description | | state | string | no | New state | | assignees | string[] | no | New assignees | | due | string | no | New due date | | milestone | string | no | New milestone ref | | sprint | string | no | New sprint ref | | parent | string | no | New parent ref. Sending it without root re-derives the root; sending "" clears both. | | root | string | no | Top-level ancestor ref. Only send this to override the derivation. | | labels | Label[] | no | New labels |

Result: Issue

pm.closeIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Issue ref |

Result: Issue

pm.reopenIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Issue ref |

Result: Issue

pm.retractIssue

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Issue ref |

Result: true

pm.getMilestones

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | no | Repository URL (default: workspace) | | branch | string | no | Branch | | states | string[] | no | Filter by state | | limit | int | no | Max results |

Result: Milestone[]

pm.getMilestone

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Milestone ref |

Result: Milestone

pm.createMilestone

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | yes | Milestone title | | body | string | no | Description | | state | string | no | Initial state | | due | string | no | ISO 8601 due date |

Result: Milestone

pm.updateMilestone

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Milestone ref | | title | string | no | New title | | body | string | no | New description | | state | string | no | New state | | due | string | no | New due date |

Result: Milestone

pm.closeMilestone / pm.reopenMilestone / pm.cancelMilestone

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Milestone ref |

Result: Milestone

pm.retractMilestone

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Milestone ref |

Result: true

pm.getMilestoneIssues

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Milestone ref | | states | string[] | no | Filter by state |

Result: Issue[]

pm.getSprints

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | no | Repository URL (default: workspace) | | branch | string | no | Branch | | states | string[] | no | Filter: "planned", "active", "completed", "canceled" | | limit | int | no | Max results |

Result: Sprint[]

pm.getSprint

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Sprint ref |

Result: Sprint

pm.createSprint

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | yes | Sprint title | | body | string | no | Description | | state | string | no | Initial state (default: "planned") | | start | string | no | ISO 8601 start date | | end | string | no | ISO 8601 end date |

Result: Sprint

pm.updateSprint

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Sprint ref | | title | string | no | New title | | body | string | no | New description | | state | string | no | New state | | start | string | no | New start date | | end | string | no | New end date |

Result: Sprint

pm.activateSprint / pm.completeSprint / pm.cancelSprint

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Sprint ref |

Result: Sprint

pm.retractSprint

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Sprint ref |

Result: true

pm.getSprintIssues

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Sprint ref | | states | string[] | no | Filter by state |

Result: Issue[]

pm.getBoardView

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | boardId | string | no | Board ID (default: first configured board) |

Result: BoardView

{
  "id": "default",
  "name": "Default Board",
  "columns": [
    {"name": "open", "label": "Open", "wip": null, "issues": [...]},
    {"name": "closed", "label": "Done", "wip": null, "issues": [...]}
  ]
}

pm.commentOnItem

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Item ref (issue, milestone, or sprint) | | content | string | yes | Comment body |

Result: Post (social comment)

pm.getItemComments

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Item ref |

Result: Post[]

Returns the link graph around an item: what it blocks, what blocks it, and what it relates to.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Item ref |

Result: {"blocks": Issue[], "blockedBy": Issue[], "related": Issue[]}

pm.isBlocked

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Item ref |

Result: bool


4.3. Review

review.getPullRequests

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | no | Repository URL (default: workspace) | | branch | string | no | Branch | | states | string[] | no | Filter: "open", "merged", "closed" | | includeForks | bool | no | Include PRs from registered forks | | limit | int | no | Max results |

Result: PullRequest[]

review.getPR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PullRequest

review.createPR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | subject | string | yes | PR title | | body | string | no | PR description | | base | string | yes | Base branch ref | | head | string | yes | Head branch ref | | closes | string[] | no | Issue refs to close on merge | | reviewers | string[] | no | Reviewer emails |

Result: PullRequest

review.updatePR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | subject | string | no | New title | | body | string | no | New description | | state | string | no | New state | | base | string | no | New base | | head | string | no | New head | | closes | string[] | no | New close refs | | reviewers | string[] | no | New reviewers |

Result: PullRequest

review.mergePR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | strategy | string | no | Merge strategy: ff (default), squash, rebase, merge |

Result: PullRequest

review.markReady

Takes a draft PR out of draft state.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PullRequest

review.convertToDraft

Puts an open PR back into draft state.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PullRequest

review.closePR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PullRequest

review.retractPR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: true

review.getFeedbackForPR

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: Feedback[]

review.createFeedback

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | content | string | yes | Feedback body | | pullRequest | string | yes | PR ref | | commit | string | no | Commit hash (12 chars) | | file | string | no | File path | | oldLine | int | no | Line in old file | | newLine | int | no | Line in new file | | oldLineEnd | int | no | End line in old file | | newLineEnd | int | no | End line in new file | | reviewState | string | no | "approved" or "changes-requested" | | suggestion | bool | no | Body contains suggestion block |

Result: Feedback

review.updateFeedback

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Feedback ref | | content | string | no | New content | | reviewState | string | no | New review state |

Result: Feedback

review.retractFeedback

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Feedback ref |

Result: true

review.applySuggestion

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Feedback ref containing suggestion |

Result: string (applied file path)

review.getDiff

Returns the diff between a PR's base and head.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: FileDiff[]

[{
  "oldFile": "a/theme.go",
  "newFile": "b/theme.go",
  "hunks": [{
    "oldStart": 10, "oldCount": 5,
    "newStart": 10, "newCount": 8,
    "lines": [
      {"type": "context", "content": "func init() {", "oldLine": 10, "newLine": 10},
      {"type": "delete", "content": "\told := theme()", "oldLine": 11},
      {"type": "add", "content": "\tnewTheme := darkTheme()", "newLine": 11}
    ]
  }]
}]

review.getDiffStats

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: DiffStats

{"filesChanged": 5, "insertions": 120, "deletions": 45}

review.getFileDiff

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | file | string | yes | File path |

Result: FileDiff

review.getFileContent

Returns file content at a specific ref.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | file | string | yes | File path | | side | string | yes | "base" or "head" |

Result: string (file contents)

review.getPRComments

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: Post[]

review.getForks

Returns registered fork URLs (stored in core config, shared across all extensions).

Params: none

Result: string[] (fork URLs)

review.addFork

Registers a fork URL in the core config (shared across all extensions).

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | url | string | yes | Fork repository URL |

Result: true

review.removeFork

Removes a fork URL from the core config.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | url | string | yes | Fork repository URL |

Result: true

review.updatePRTips

Re-snapshots the PR's base and head tips from the live branches.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PullRequest

review.syncPRBranch

Brings the head branch up to date with the base, then re-snapshots the tips.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | strategy | string | no | rebase (default) or merge |

Result: PullRequest

review.getPRVersions

Lists every version of the PR, oldest first.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: PRVersion[]

review.comparePRVersions

Range-diffs two versions of the PR.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref | | from | int | yes | Version number to compare from | | to | int | yes | Version number to compare to |

Result: string (the range-diff)

review.getVersionAwareReviews

Each reviewer's latest review tagged with the version it was left against, so a client can tell a stale approval from a current one.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | PR ref |

Result: VersionAwareReview[]


4.4. Release

release.getReleases

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | no | Repository URL (default: workspace) | | branch | string | no | Branch | | limit | int | no | Max results |

Result: Release[]

release.getRelease

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref |

Result: Release

release.createRelease

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | subject | string | yes | Release title | | body | string | no | Release notes | | tag | string | no | Git tag | | version | string | no | Version string | | prerelease | bool | no | Pre-release flag | | artifacts | string[] | no | Artifact names | | artifactURL | string | no | Download URL | | checksums | string | no | Checksum data | | signedBy | string | no | GPG signer | | sbom | string | no | SBOM filename (e.g., sbom.spdx.json) |

Result: Release

release.editRelease

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref | | subject | string | no | New title | | body | string | no | New notes | | tag | string | no | New tag | | version | string | no | New version | | prerelease | bool | no | New pre-release flag | | artifacts | string[] | no | New artifacts | | artifactURL | string | no | New download URL | | checksums | string | no | New checksums | | signedBy | string | no | New signer | | sbom | string | no | New SBOM filename |

Result: Release

release.retractRelease

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref |

Result: true

release.getReleaseComments

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref |

Result: Post[]

release.getSBOM

Returns parsed SBOM summary for a release (format, package count, licenses, generator).

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref |

Result: SBOMSummary

release.getSBOMRaw

Returns the raw SBOM file content as a JSON string.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Release ref |

Result: string (raw SBOM JSON content)


4.5. Core

core.fetch

Fetches updates from all subscribed repositories. Returns immediately with a fetch ID. Progress and completion are reported via server notifications.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | listId | string | no | Fetch only repositories in this list |

Result:

{"fetchId": "f-1"}

The server sends fetch.progress and fetch.complete notifications for this fetchId (see Section 5).

core.push

Pushes local changes to the remote.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | extensions | string[] | no | Extensions to push (default: all initialized) |

Result:

{"pushed": ["social", "pm"]}

core.status

Returns workspace and extension status.

Params: none

Result:

{
  "workdir": "/path/to/repo",
  "repoURL": "https://github.com/user/repo",
  "extensions": {
    "social": {"initialized": true, "branch": "gitmsg/social", "unpushed": 3},
    "pm": {"initialized": true, "branch": "gitmsg/pm", "unpushed": 0},
    "review": {"initialized": false},
    "release": {"initialized": false}
  }
}

core.getConfig

Reads extension configuration.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | extension | string | yes | Extension name |

Result: object (extension-specific config JSON)

core.setConfig

Writes extension configuration.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | extension | string | yes | Extension name | | config | object | yes | Config object to write |

Result: true

core.initExtension

Initializes an extension in the workspace.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | extension | string | yes | Extension name | | branch | string | no | Custom branch name |

Result: true

core.getNotifications

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | unreadOnly | bool | no | Only unread (default: false) | | types | string[] | no | Filter by type | | limit | int | no | Max results |

Result: Notification[]

[{
  "repoURL": "https://github.com/user/repo",
  "hash": "abc123456789",
  "branch": "gitmsg/social",
  "type": "comment",
  "source": "social",
  "actor": {"name": "Bob", "email": "[email protected]"},
  "timestamp": "2025-01-06T10:00:00Z",
  "isRead": false
}]

core.getUnreadCount

Params: none

Result: int

core.markAsRead

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | repoURL | string | yes | Notification repo URL | | hash | string | yes | Notification hash | | branch | string | yes | Notification branch |

Result: true

core.markAllAsRead

Params: none

Result: true

core.getHistory

Returns edit history for any item (post, issue, PR, release, etc.).

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | ref | string | yes | Item ref |

Result: MessageVersion[]

[
  {"hash": "abc123456789", "timestamp": "2025-01-06T10:00:00Z", "author": {...}, "content": "v1"},
  {"hash": "def234567890", "timestamp": "2025-01-06T11:00:00Z", "author": {...}, "content": "v2 (edited)"}
]

core.getSettings

Params: none

Result: KeyValue[]

[
  {"key": "fetch.parallel", "value": "4"},
  {"key": "log.level", "value": "info"}
]

core.setSetting

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | key | string | yes | Setting key | | value | string | yes | Setting value |

Result: true


search / social.search

Cross-extension search (posts, issues, PRs, releases, feedback). search is the real name: it spans every extension, so filing it under social. would misdescribe it. social.search is registered as an alias for clients written against the name this document used to give, and dispatches to the same handler.

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | query | string | no | Free-text query | | author | string | no | Filter by author email | | repo | string | no | Filter by repository URL | | type | string | no | Filter by type: post, comment, repost, quote, issue, milestone, sprint, pr, feedback, release | | hash | string | no | Filter by commit-hash prefix | | after | string | no | ISO 8601 timestamp lower bound | | before | string | no | ISO 8601 timestamp upper bound | | limit | int | no | Max results (default: 20) | | scope | string | no | timeline (default), list:<id>, repository:<url>, repos:<csv> | | sort | string | no | score (default) or date |

Result: SearchResult

{
  "query": "dark mode",
  "results": [{"repo_url": "https://github.com/user/repo", "hash": "abc123456789", "branch": "gitmsg/social", "content": "dark mode toggle", "type": "post", "extension": "social", "score": 8.5}],
  "total": 3,
  "total_searched": 1240,
  "has_more": false,
  "execution_time_ms": 12
}

5. Server Notifications

Server-initiated notifications (no id field) pushed to the client. Clients opt in by sending subscribe after initialization.

5.1. Subscribe

Method: subscribe

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | events | string[] | yes | Events to subscribe to: "fetch", "notifications", "workspace" |

Result: true

5.2. Unsubscribe

Method: unsubscribe

Params: | Field | Type | Required | Description | |-------|------|----------|-------------| | events | string[] | yes | Events to unsubscribe from |

Result: true

5.3. Fetch Events

fetch.progress

{"jsonrpc":"2.0","method":"fetch.progress","params":{
  "fetchId": "f-1",
  "repository": "https://github.com/user/repo",
  "processed": 3,
  "total": 10
}}

fetch.complete

{"jsonrpc":"2.0","method":"fetch.complete","params":{
  "fetchId": "f-1",
  "repositories": 10,
  "newCommits": 42,
  "errors": 0
}}

fetch.error

{"jsonrpc":"2.0","method":"fetch.error","params":{
  "fetchId": "f-1",
  "repository": "https://github.com/user/repo",
  "message": "network timeout"
}}

5.4. Notification Events

notifications.changed

Sent when the unread notification count changes (after fetch, after new local commits, or after mark-as-read).

{"jsonrpc":"2.0","method":"notifications.changed","params":{
  "unreadCount": 5
}}

5.5. Workspace Events

workspace.changed

Sent when the server detects changes to gitmsg branches in the workspace (via filesystem watch on .git/refs/heads/gitmsg/).

{"jsonrpc":"2.0","method":"workspace.changed","params":{
  "branches": ["gitmsg/social", "gitmsg/pm"]
}}

6. Type Reference

Types returned by methods. JSON field names use camelCase. Null/absent fields are omitted.

Post

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "content": "string",
  "type": "post | comment | repost | quote",
  "interactions": {"comments": 0, "reposts": 0, "quotes": 0},
  "originalPostId": "string (ref, optional)",
  "parentCommentId": "string (ref, optional)",
  "isEdited": false,
  "isRetracted": false,
  "isVirtual": false
}

Issue

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "subject": "string",
  "body": "string",
  "state": "open | closed | canceled",
  "assignees": ["string (email)"],
  "due": "string (ISO 8601, optional)",
  "milestone": {"repoURL": "string", "hash": "string", "branch": "string"},
  "sprint": {"repoURL": "string", "hash": "string", "branch": "string"},
  "labels": [{"scope": "string", "value": "string"}],
  "isEdited": false,
  "isRetracted": false,
  "comments": 0
}

Milestone

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "title": "string",
  "body": "string",
  "state": "open | closed | canceled",
  "due": "string (ISO 8601, optional)",
  "isEdited": false,
  "isRetracted": false,
  "issueCount": 0,
  "closedCount": 0
}

Sprint

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "title": "string",
  "body": "string",
  "state": "planned | active | completed | canceled",
  "start": "string (ISO 8601)",
  "end": "string (ISO 8601)",
  "isEdited": false,
  "isRetracted": false,
  "issueCount": 0,
  "closedCount": 0
}

PullRequest

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "subject": "string",
  "body": "string",
  "state": "open | merged | closed",
  "base": "string (branch ref)",
  "baseTip": "string (12-char hash, base branch tip at creation/update)",
  "head": "string (branch ref)",
  "headTip": "string (12-char hash, head branch tip at creation/update)",
  "closes": ["string (issue ref)"],
  "reviewers": ["string (email)"],
  "labels": ["string"],
  "isEdited": false,
  "isRetracted": false,
  "comments": 0,
  "reviewSummary": {
    "approved": 0,
    "changesRequested": 0,
    "pending": 0,
    "isBlocked": false,
    "isApproved": false
  },
  "mergeBase": "string (12-char hash, merge-base at merge time, optional)",
  "mergeHead": "string (12-char hash, head at merge time, optional)",
  "mergedBy": {"name": "string", "email": "string"} | null,
  "mergedAt": "string (ISO 8601)" | null,
  "closedBy": {"name": "string", "email": "string"} | null,
  "closedAt": "string (ISO 8601)" | null,
  "originalAuthor": {"name": "string", "email": "string"} | null
}

Feedback

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "content": "string",
  "pullRequest": {"repoURL": "string", "hash": "string", "branch": "string"},
  "commit": "string (optional)",
  "file": "string (optional)",
  "oldLine": 0,
  "newLine": 0,
  "reviewState": "approved | changes-requested (optional)",
  "suggestion": false,
  "isEdited": false,
  "isRetracted": false,
  "comments": 0
}

PRVersion

{
  "number": 0,
  "label": "string",
  "commit_hash": "string",
  "repo_url": "string (URL)",
  "branch": "string",
  "author_name": "string",
  "author_email": "string",
  "timestamp": "string (ISO 8601)",
  "subject": "string (optional)",
  "body": "string (optional)",
  "base_tip": "string (optional)",
  "head_tip": "string (optional)",
  "state": "open | merged | closed",
  "is_retracted": false
}

VersionAwareReview

{
  "reviewer_name": "string",
  "reviewer_email": "string",
  "state": "approved | changes-requested",
  "reviewed_at": "string (ISO 8601)",
  "reviewed_version": 0,
  "reviewed_label": "string",
  "current_version": 0,
  "current_label": "string",
  "head_changed": false,
  "code_changed": false,
  "stale": false
}

Release

{
  "id": "string (ref)",
  "repository": "string (URL)",
  "branch": "string",
  "author": {"name": "string", "email": "string"},
  "timestamp": "string (ISO 8601)",
  "subject": "string",
  "body": "string",
  "version": "string",
  "tag": "string",
  "prerelease": false,
  "artifacts": ["string"],
  "artifactURL": "string (optional)",
  "checksums": "string (optional)",
  "signedBy": "string (optional)",
  "sbom": "string (optional, e.g. sbom.spdx.json)",
  "isEdited": false,
  "isRetracted": false,
  "comments": 0
}

SBOMSummary

{
  "format": "spdx | cyclonedx | syft",
  "packages": 127,
  "generator": "string (optional, e.g. syft-1.0.0)",
  "licenses": {"MIT": 42, "Apache-2.0": 15},
  "generated": "string (ISO 8601, optional)",
  "items": [
    {"name": "string", "version": "string", "license": "string"}
  ]
}

Notification

{
  "repoURL": "string",
  "hash": "string",
  "branch": "string",
  "type": "string",
  "source": "social | pm | review | core",
  "actor": {"name": "string", "email": "string"},
  "actorRepo": "string (optional)",
  "timestamp": "string (ISO 8601)",
  "isRead": false
}

7. Implementation Notes

7.1. Concurrency

The server MUST handle concurrent requests. Long-running operations (core.fetch) run asynchronously and report progress via notifications. Read operations (getPosts, getIssues, etc.) MUST NOT block on writes.

The cache layer already serializes DB access via ExecLocked/QueryLocked. The RPC server adds no additional locking.

7.2. Workspace Scope

All methods operate on the workspace set during initialize. To switch workspaces, the client shuts down and spawns a new server. Multi-root editors spawn one server per workspace.

7.3. Serialization

7.4. Extension Registration

Methods are registered per extension. If an extension is not initialized, its methods return -32003 NOT_INITIALIZED. Clients check initialize response to know which extensions are available.

7.5. Package Structure

library/rpc/
├── server.go           # Stdio read loop, JSON-RPC dispatch
├── handler.go          # Method registration, param unmarshaling
├── methods_social.go   # social.* method handlers
├── methods_pm.go       # pm.* method handlers
├── methods_review.go   # review.* method handlers
├── methods_release.go  # release.* method handlers
├── methods_core.go     # core.* + lifecycle method handlers
├── methods_search.go   # top-level search method handler
└── types.go            # Request/response param structs, subscription events

Each method handler is a function that unmarshals params, calls the existing extension API, and returns the result. No business logic lives in the RPC layer.