opencode-discord-presence 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @fileoverview Main plugin implementation for OpenCode Discord Presence
3
+ * @module opencode-discord-presence/plugin
4
+ *
5
+ * This is the core plugin that integrates with OpenCode to display
6
+ * Rich Presence in Discord, showing:
7
+ * - Current AI agent being used
8
+ * - Current model
9
+ * - Activity status (active/idle)
10
+ * - Optional: session time, token usage, project name
11
+ */
12
+ import type { Plugin } from "@opencode-ai/plugin";
13
+ /**
14
+ * OpenCode Discord Presence Plugin
15
+ *
16
+ * Displays your current OpenCode session status in Discord Rich Presence.
17
+ * Integrates with OpenCode's event system to track agent changes,
18
+ * model usage, and session state.
19
+ *
20
+ * @param ctx - Plugin context from OpenCode
21
+ * @returns Plugin hooks object
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // In opencode.json
26
+ * {
27
+ * "plugins": ["opencode-discord-presence"],
28
+ * "discordPresence": {
29
+ * "enabled": true,
30
+ * "showSessionTime": true,
31
+ * "showTokenUsage": true,
32
+ * "showProjectName": true
33
+ * }
34
+ * }
35
+ * ```
36
+ */
37
+ export declare const OpenCodeDiscordPresence: Plugin;
38
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAQjD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,uBAAuB,EAAE,MA0HrC,CAAA"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @fileoverview Discord RPC Service for Rich Presence
3
+ * @module opencode-discord-presence/services/discord-rpc
4
+ *
5
+ * Provides a singleton service for managing Discord Rich Presence connection.
6
+ * Features:
7
+ * - Singleton pattern for single connection per application
8
+ * - Automatic reconnection with exponential backoff
9
+ * - Debounced presence updates to avoid rate limiting
10
+ * - Event-driven connection management
11
+ */
12
+ import type { PresenceState } from "../types";
13
+ /**
14
+ * Singleton service for managing Discord Rich Presence
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const rpc = DiscordRPCService.getInstance("your-client-id")
19
+ * await rpc.connect()
20
+ * await rpc.updatePresence({
21
+ * details: "Working on project",
22
+ * state: "Using Claude",
23
+ * largeImageKey: "opencode-logo"
24
+ * })
25
+ * ```
26
+ */
27
+ export declare class DiscordRPCService {
28
+ private static instance;
29
+ private static currentClientId;
30
+ private client;
31
+ private _isConnected;
32
+ private reconnectAttempts;
33
+ private debounceTimer;
34
+ private pendingPresence;
35
+ /**
36
+ * Private constructor - use getInstance() instead
37
+ */
38
+ private constructor();
39
+ /**
40
+ * Get the singleton instance of DiscordRPCService
41
+ *
42
+ * @param clientId - Discord Application ID
43
+ * @returns The singleton instance
44
+ */
45
+ static getInstance(clientId: string): DiscordRPCService;
46
+ /**
47
+ * Reset the singleton instance (mainly for testing)
48
+ */
49
+ static resetInstance(): void;
50
+ /**
51
+ * Whether the service is currently connected to Discord
52
+ */
53
+ get isConnected(): boolean;
54
+ /**
55
+ * Set up event handlers for the Discord RPC client
56
+ */
57
+ private setupEventHandlers;
58
+ /**
59
+ * Attempt to reconnect with exponential backoff
60
+ */
61
+ private attemptReconnect;
62
+ /**
63
+ * Connect to Discord RPC
64
+ *
65
+ * @throws Error if connection fails and not retrying
66
+ */
67
+ connect(): Promise<void>;
68
+ /**
69
+ * Disconnect from Discord RPC
70
+ */
71
+ disconnect(): Promise<void>;
72
+ /**
73
+ * Update the Rich Presence with debouncing
74
+ *
75
+ * @param presence - The presence state to set
76
+ */
77
+ updatePresence(presence: PresenceState): Promise<void>;
78
+ /**
79
+ * Clear the Rich Presence
80
+ */
81
+ clearPresence(): Promise<void>;
82
+ }
83
+ //# sourceMappingURL=discord-rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord-rpc.d.ts","sourceRoot":"","sources":["../../src/services/discord-rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAS7C;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,MAAM,CAAC,eAAe,CAAsB;IAEpD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,eAAe,CAA2B;IAElD;;OAEG;IACH,OAAO;IAKP;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB;IAQvD;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;YACW,gBAAgB;IAwB9B;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAcjC;;;;OAIG;IACG,cAAc,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC5D;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAgBrC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileoverview Type definitions for OpenCode Discord Presence plugin
3
+ * @module opencode-discord-presence/types
4
+ */
5
+ /**
6
+ * Supported languages for the plugin UI
7
+ * - "auto": Detect from system locale
8
+ * - "ko": Korean
9
+ * - "en": English
10
+ * - "ja": Japanese
11
+ * - "zh": Chinese (Simplified)
12
+ */
13
+ export type SupportedLanguage = "auto" | "ko" | "en" | "ja" | "zh";
14
+ /**
15
+ * Configuration options for the Discord Rich Presence plugin
16
+ */
17
+ export interface DiscordPresenceConfig {
18
+ /** Whether the plugin is enabled */
19
+ enabled: boolean;
20
+ /** Discord Application ID for Rich Presence */
21
+ applicationId: string;
22
+ /** Show elapsed time since session started */
23
+ showSessionTime: boolean;
24
+ /** Show token usage count */
25
+ showTokenUsage: boolean;
26
+ /** Show current project name */
27
+ showProjectName: boolean;
28
+ /** Language for presence messages (default: "auto") */
29
+ language: SupportedLanguage;
30
+ }
31
+ /**
32
+ * Token count structure for tracking usage
33
+ */
34
+ export interface TokenCount {
35
+ /** Input tokens consumed */
36
+ input: number;
37
+ /** Output tokens generated */
38
+ output: number;
39
+ }
40
+ /**
41
+ * Model information from OpenCode
42
+ */
43
+ export interface ModelInfo {
44
+ /** Provider ID (e.g., "anthropic", "openai") */
45
+ providerID: string;
46
+ /** Model ID (e.g., "claude-sonnet-4-20250514") */
47
+ modelID: string;
48
+ }
49
+ /**
50
+ * Presence state for Discord Rich Presence
51
+ */
52
+ export interface PresenceState {
53
+ /** Main text line (details) */
54
+ details: string;
55
+ /** Secondary text line (state) */
56
+ state?: string;
57
+ /** Timestamp when activity started */
58
+ startTimestamp?: number;
59
+ /** Large image asset key */
60
+ largeImageKey: string;
61
+ /** Tooltip for large image */
62
+ largeImageText?: string;
63
+ /** Small image asset key */
64
+ smallImageKey?: string;
65
+ /** Tooltip for small image */
66
+ smallImageText?: string;
67
+ }
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAElE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAA;IACrB,8CAA8C;IAC9C,eAAe,EAAE,OAAO,CAAA;IACxB,6BAA6B;IAC7B,cAAc,EAAE,OAAO,CAAA;IACvB,gCAAgC;IAChC,eAAe,EAAE,OAAO,CAAA;IACxB,uDAAuD;IACvD,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAA;IAClB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B;IAC5B,aAAa,EAAE,MAAM,CAAA;IACrB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Formatting utilities for tokens and model names
3
+ * @module opencode-discord-presence/utils/format
4
+ *
5
+ * Provides formatting functions for displaying token counts and model names
6
+ * in a user-friendly way for Discord Rich Presence.
7
+ */
8
+ import type { ModelInfo, TokenCount } from "../types";
9
+ /**
10
+ * Format token count for display
11
+ *
12
+ * @param tokens - Token count object with input and output
13
+ * @returns Formatted string like "12.5k tokens" or "700 tokens"
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * formatTokens({ input: 8200, output: 4300 }) // "12.5k tokens"
18
+ * formatTokens({ input: 500, output: 200 }) // "700 tokens"
19
+ * formatTokens({ input: 0, output: 0 }) // "0 tokens"
20
+ * ```
21
+ */
22
+ export declare function formatTokens(tokens: TokenCount): string;
23
+ /**
24
+ * Format model name for display
25
+ *
26
+ * Converts model IDs like "claude-sonnet-4-20250514" to human-readable
27
+ * names like "Claude Sonnet 4".
28
+ *
29
+ * @param model - Model info with providerID and modelID
30
+ * @returns Human-readable model name
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * formatModelName({ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" })
35
+ * // "Claude Sonnet 4"
36
+ *
37
+ * formatModelName({ providerID: "openai", modelID: "gpt-4o" })
38
+ * // "GPT-4o"
39
+ *
40
+ * formatModelName({ providerID: "unknown", modelID: "custom-model" })
41
+ * // "custom-model"
42
+ * ```
43
+ */
44
+ export declare function formatModelName(model: ModelInfo | undefined): string;
45
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAevD;AAgCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAuBpE"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview Korean particle utility for proper grammar
3
+ * @module opencode-discord-presence/utils/particle
4
+ *
5
+ * Korean particles (조사) change based on whether the preceding syllable
6
+ * ends with a consonant (받침) or not.
7
+ *
8
+ * - 을/를 (object marker): 을 after 받침, 를 after no 받침
9
+ * - 은/는 (topic marker): 은 after 받침, 는 after no 받침
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * withParticle("빌드", "을/를") // "빌드를" (ㄷ has 받침)
14
+ * withParticle("프로메테우스", "을/를") // "프로메테우스를" (스 has no 받침)
15
+ * withParticle("oracle", "은/는") // "oracle은" (e is consonant-like)
16
+ * ```
17
+ */
18
+ /** Supported particle types */
19
+ export type ParticleType = "을/를" | "은/는";
20
+ /**
21
+ * Adds the correct Korean particle to a word based on its final character
22
+ *
23
+ * @param word - The word to add a particle to
24
+ * @param particle - The particle type ("을/를" or "은/는")
25
+ * @returns The word with the correct particle appended
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * withParticle("Prometheus", "을/를") // "Prometheus를"
30
+ * withParticle("build", "은/는") // "build는"
31
+ * ```
32
+ */
33
+ export declare function withParticle(word: string, particle: ParticleType): string;
34
+ //# sourceMappingURL=particle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particle.d.ts","sourceRoot":"","sources":["../../src/utils/particle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,CAAA;AAExC;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CAOzE"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @fileoverview Project name detection utility
3
+ * @module opencode-discord-presence/utils/project
4
+ *
5
+ * Detects the current project name from Git remote URL or directory name.
6
+ */
7
+ /**
8
+ * Get the project name from Git remote URL or directory path
9
+ *
10
+ * Priority:
11
+ * 1. Git remote URL (if provided) - extracts repo name
12
+ * 2. Directory path (if provided) - extracts folder name
13
+ * 3. Fallback - "Unknown Project"
14
+ *
15
+ * @param remoteUrl - Git remote URL (optional)
16
+ * @param directory - Current working directory path (optional)
17
+ * @returns Project name
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * getProjectName("git@github.com:user/my-repo.git")
22
+ * // "my-repo"
23
+ *
24
+ * getProjectName(undefined, "/Users/dev/my-project")
25
+ * // "my-project"
26
+ *
27
+ * getProjectName(undefined, undefined)
28
+ * // "Unknown Project"
29
+ * ```
30
+ */
31
+ export declare function getProjectName(remoteUrl?: string, directory?: string): string;
32
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/utils/project.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0CH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAe7E"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "opencode-discord-presence",
3
+ "version": "0.1.0",
4
+ "description": "Discord Rich Presence plugin for OpenCode - Display your current AI agent, model, and status in Discord",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "dev": "bun run --watch src/index.ts",
21
+ "build": "bun build src/index.ts --outdir dist --target node && bun run build:types",
22
+ "build:types": "tsc -p tsconfig.build.json",
23
+ "typecheck": "tsc --noEmit",
24
+ "lint": "biome check src/",
25
+ "lint:fix": "biome check --write src/",
26
+ "format": "biome format --write src/",
27
+ "test": "bun test",
28
+ "test:watch": "bun test --watch",
29
+ "prepublishOnly": "bun run build"
30
+ },
31
+ "keywords": [
32
+ "opencode",
33
+ "discord",
34
+ "rich-presence",
35
+ "rpc",
36
+ "claude",
37
+ "ai",
38
+ "plugin"
39
+ ],
40
+ "author": "",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/Puri12/opencode-rich-presence"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/Puri12/opencode-rich-presence/issues"
48
+ },
49
+ "homepage": "https://github.com/Puri12/opencode-rich-presence#readme",
50
+ "engines": {
51
+ "node": ">=18.0.0",
52
+ "bun": ">=1.1.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@opencode-ai/plugin": ">=0.1.0",
56
+ "typescript": "^5"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "@opencode-ai/plugin": {
60
+ "optional": false
61
+ }
62
+ },
63
+ "dependencies": {
64
+ "@xhayper/discord-rpc": "^1.3.0"
65
+ },
66
+ "devDependencies": {
67
+ "@biomejs/biome": "^2.3.13",
68
+ "@types/bun": "^1.3.8",
69
+ "typescript": "^5.9.3"
70
+ }
71
+ }