@openthread/claude-code-plugin 0.1.2 → 0.1.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "openthread-share",
3
- "version": "0.1.2",
2
+ "name": "ot",
3
+ "version": "0.1.3",
4
4
  "description": "Share Claude Code conversations to OpenThread",
5
5
  "icon": "icon.svg",
6
6
  "author": {
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Share Claude Code conversations to [OpenThread](https://openthread.me) -- a Reddit-like platform for AI conversation threads.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@openthread/claude-code-plugin)](https://www.npmjs.com/package/@openthread/claude-code-plugin)
6
- [![license](https://img.shields.io/npm/l/@openthread/claude-code-plugin)](https://github.com/nicholasgriffintn/openthread/blob/main/LICENSE)
6
+ [![license](https://img.shields.io/npm/l/@openthread/claude-code-plugin)](https://opensource.org/licenses/MIT)
7
7
  [![downloads](https://img.shields.io/npm/dm/@openthread/claude-code-plugin)](https://www.npmjs.com/package/@openthread/claude-code-plugin)
8
8
 
9
9
  ## Install
@@ -12,27 +12,66 @@ Share Claude Code conversations to [OpenThread](https://openthread.me) -- a Redd
12
12
  npm i -g @openthread/claude-code-plugin
13
13
  ```
14
14
 
15
- The postinstall script registers the plugin with Claude Code automatically.
15
+ The postinstall script registers the plugin with Claude Code automatically. Restart Claude Code after installing.
16
16
 
17
17
  ## Quick Start
18
18
 
19
19
  ```
20
- You> /share fixing auth bug
20
+ > /ot:share fixing auth bug
21
21
 
22
22
  Sharing conversation to OpenThread...
23
- Authenticated as @yourname
24
23
  Auto-generated title: "Debugging PKCE token refresh in auth middleware"
25
- Posted to c/ClaudeCode
24
+ Community: Coding with AI
25
+ Tags: typescript, authentication, debugging
26
26
 
27
- https://openthread.me/c/ClaudeCode/posts/abc123
27
+ Post shared successfully!
28
+ View it at: https://openthread.me/post/abc123
28
29
  ```
29
30
 
31
+ ## Examples
32
+
33
+ ### Share instantly with quick mode
34
+
35
+ ```
36
+ > /ot:share building a REST API with Hono
37
+ ```
38
+
39
+ Auto-generates title, picks the best community, adds tags, and posts -- no questions asked.
40
+
41
+ ### Share interactively
42
+
43
+ ```
44
+ > /ot:share
45
+
46
+ ? Title: Building a REST API with Hono on Bun
47
+ ? Community:
48
+ > Coding with AI
49
+ Prompt Engineering
50
+ OpenThread Meta
51
+ ? Tags: typescript, hono, api
52
+
53
+ Post shared successfully!
54
+ View it at: https://openthread.me/post/def456
55
+ ```
56
+
57
+ ### Share after a long debugging session
58
+
59
+ ```
60
+ > /ot:share tracked down a memory leak in the worker pool
61
+ ```
62
+
63
+ The plugin reads the full conversation, generates a summary, and shares it as a post others can learn from.
64
+
65
+ ### Share from any project
66
+
67
+ The plugin auto-detects the current Claude Code session file. Works in any project directory -- just run `/ot:share` and it finds the right conversation.
68
+
30
69
  ## Features
31
70
 
32
- - **One-command sharing** -- run `/share` inside any Claude Code session to publish the conversation to OpenThread.
33
- - **Quick mode** -- `/share <description>` auto-generates a title, selects a community, and posts immediately.
34
- - **Interactive mode** -- `/share` with no arguments prompts you to choose a title, community, and tags.
35
- - **Secure auth** -- PKCE OAuth flow with automatic token refresh. Credentials are stored locally.
71
+ - **One-command sharing** -- run `/ot:share` inside any Claude Code session to publish the conversation to OpenThread.
72
+ - **Quick mode** -- `/ot:share <description>` auto-generates a title, selects a community, and posts immediately.
73
+ - **Interactive mode** -- `/ot:share` with no arguments prompts you to choose a title, community, and tags.
74
+ - **Secure auth** -- PKCE OAuth flow with automatic token refresh. Credentials are stored locally at `~/.config/openthread/`.
36
75
  - **CLI management** -- `openthread-claude` binary for install, uninstall, status checks, and updates.
37
76
 
38
77
  ## Usage
@@ -40,31 +79,31 @@ https://openthread.me/c/ClaudeCode/posts/abc123
40
79
  ### Quick mode
41
80
 
42
81
  ```
43
- /share fixing auth bug
82
+ /ot:share <short description of what you worked on>
44
83
  ```
45
84
 
46
- Provide a short description after `/share`. The plugin generates a title from the conversation context, picks the best-matching community, and posts without further prompts.
85
+ Provide a short description after `/ot:share`. The plugin generates a title from the conversation context, picks the best-matching community, and posts without further prompts.
47
86
 
48
87
  ### Interactive mode
49
88
 
50
89
  ```
51
- /share
90
+ /ot:share
52
91
  ```
53
92
 
54
93
  Run with no arguments to step through each field:
55
94
 
56
95
  1. Title (suggested from conversation, editable)
57
- 2. Community (searchable list)
58
- 3. Tags (optional, comma-separated)
96
+ 2. Community (selectable from list)
97
+ 3. Tags (suggested, accept or modify)
59
98
 
60
99
  ## CLI Commands
61
100
 
62
101
  | Command | Description |
63
102
  | --- | --- |
64
- | `openthread-claude install` | Register the plugin with Claude Code |
65
- | `openthread-claude uninstall` | Remove the plugin from Claude Code |
66
- | `openthread-claude status` | Show current auth and plugin state |
67
- | `openthread-claude update` | Pull the latest plugin version |
103
+ | `openthread-claude install` | Install and register the plugin with Claude Code |
104
+ | `openthread-claude uninstall` | Remove the plugin and deregister from Claude Code |
105
+ | `openthread-claude status` | Show plugin installation and registration state |
106
+ | `openthread-claude update` | Reinstall plugin (update to current version) |
68
107
 
69
108
  ## Configuration
70
109
 
@@ -81,7 +120,8 @@ If you prefer not to use npm:
81
120
 
82
121
  1. Download the latest `.zip` release from [openthread.me/extensions](https://openthread.me/extensions).
83
122
  2. Extract it to `~/.claude/plugins/openthread-share/`.
84
- 3. Restart Claude Code. The plugin will be detected on next launch.
123
+ 3. Run `openthread-claude install` to register with Claude Code.
124
+ 4. Restart Claude Code.
85
125
 
86
126
  ```bash
87
127
  mkdir -p ~/.claude/plugins/openthread-share
package/bin/cli.sh CHANGED
@@ -5,17 +5,78 @@ set -euo pipefail
5
5
  PLUGIN_NAME="openthread-share"
6
6
  PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
7
7
  DEST_DIR="$HOME/.claude/plugins/$PLUGIN_NAME"
8
+ SETTINGS_FILE="$HOME/.claude/settings.json"
8
9
 
9
10
  usage() {
10
11
  echo "Usage: openthread-claude <command>"
11
12
  echo ""
12
13
  echo "Commands:"
13
- echo " install Install plugin to ~/.claude/plugins/"
14
- echo " uninstall Remove plugin from ~/.claude/plugins/"
15
- echo " status Check if plugin is installed"
14
+ echo " install Install and register plugin with Claude Code"
15
+ echo " uninstall Remove plugin and deregister from Claude Code"
16
+ echo " status Check if plugin is installed and registered"
16
17
  echo " update Reinstall plugin (update to current version)"
17
18
  }
18
19
 
20
+ register_plugin() {
21
+ # Register the plugin in ~/.claude/settings.json so Claude Code loads it
22
+ python3 -c "
23
+ import json, os
24
+
25
+ settings_path = '$SETTINGS_FILE'
26
+ dest_dir = '$DEST_DIR'
27
+
28
+ settings = {}
29
+ if os.path.exists(settings_path):
30
+ try:
31
+ with open(settings_path) as f:
32
+ settings = json.load(f)
33
+ except:
34
+ settings = {}
35
+
36
+ if not isinstance(settings.get('enabledPlugins'), list):
37
+ settings['enabledPlugins'] = []
38
+
39
+ # Check if already registered
40
+ already = any(
41
+ p.get('source', {}).get('path') == dest_dir
42
+ for p in settings['enabledPlugins']
43
+ )
44
+
45
+ if not already:
46
+ settings['enabledPlugins'].append({
47
+ 'source': {'type': 'local', 'path': dest_dir}
48
+ })
49
+ os.makedirs(os.path.dirname(settings_path), exist_ok=True)
50
+ with open(settings_path, 'w') as f:
51
+ json.dump(settings, f, indent=2)
52
+ f.write('\n')
53
+ "
54
+ }
55
+
56
+ deregister_plugin() {
57
+ # Remove the plugin from ~/.claude/settings.json
58
+ if [ -f "$SETTINGS_FILE" ]; then
59
+ python3 -c "
60
+ import json
61
+
62
+ settings_path = '$SETTINGS_FILE'
63
+ dest_dir = '$DEST_DIR'
64
+
65
+ with open(settings_path) as f:
66
+ settings = json.load(f)
67
+
68
+ if isinstance(settings.get('enabledPlugins'), list):
69
+ settings['enabledPlugins'] = [
70
+ p for p in settings['enabledPlugins']
71
+ if p.get('source', {}).get('path') != dest_dir
72
+ ]
73
+ with open(settings_path, 'w') as f:
74
+ json.dump(settings, f, indent=2)
75
+ f.write('\n')
76
+ "
77
+ fi
78
+ }
79
+
19
80
  install_plugin() {
20
81
  mkdir -p "$DEST_DIR"
21
82
 
@@ -31,26 +92,53 @@ install_plugin() {
31
92
 
32
93
  chmod +x "$DEST_DIR/scripts/"*.sh 2>/dev/null || true
33
94
 
95
+ register_plugin
96
+
34
97
  VERSION=$(python3 -c "import json; print(json.load(open('$DEST_DIR/.claude-plugin/plugin.json'))['version'])")
35
98
  echo "✓ OpenThread plugin v$VERSION installed to $DEST_DIR"
36
- echo " Use /share in Claude Code to share conversations."
99
+ echo " Registered in $SETTINGS_FILE"
100
+ echo " Use /ot:share in Claude Code to share conversations."
37
101
  }
38
102
 
39
103
  uninstall_plugin() {
40
104
  if [ -d "$DEST_DIR" ]; then
41
105
  rm -rf "$DEST_DIR"
42
- echo "✓ OpenThread plugin removed from $DEST_DIR"
106
+ deregister_plugin
107
+ echo "✓ OpenThread plugin removed and deregistered"
43
108
  else
44
109
  echo "Plugin is not installed."
45
110
  fi
46
111
  }
47
112
 
48
113
  check_status() {
114
+ local installed=false
115
+ local registered=false
116
+
49
117
  if [ -d "$DEST_DIR" ] && [ -f "$DEST_DIR/.claude-plugin/plugin.json" ]; then
118
+ installed=true
119
+ fi
120
+
121
+ if [ -f "$SETTINGS_FILE" ]; then
122
+ registered=$(python3 -c "
123
+ import json
124
+ with open('$SETTINGS_FILE') as f:
125
+ s = json.load(f)
126
+ plugins = s.get('enabledPlugins', [])
127
+ print('true' if any(p.get('source',{}).get('path') == '$DEST_DIR' for p in plugins) else 'false')
128
+ " 2>/dev/null || echo "false")
129
+ fi
130
+
131
+ if [ "$installed" = true ]; then
50
132
  VERSION=$(python3 -c "import json; print(json.load(open('$DEST_DIR/.claude-plugin/plugin.json'))['version'])")
51
- echo "✓ OpenThread plugin v$VERSION is installed at $DEST_DIR"
133
+ echo "✓ Plugin files: v$VERSION at $DEST_DIR"
134
+ else
135
+ echo "✗ Plugin files: not installed"
136
+ fi
137
+
138
+ if [ "$registered" = "true" ]; then
139
+ echo "✓ Registered: yes (in $SETTINGS_FILE)"
52
140
  else
53
- echo "✗ OpenThread plugin is not installed."
141
+ echo "✗ Registered: no (Claude Code won't detect the plugin)"
54
142
  echo " Run: openthread-claude install"
55
143
  fi
56
144
  }
@@ -48,9 +48,42 @@ try {
48
48
  }
49
49
  }
50
50
 
51
+ // Register plugin in ~/.claude/settings.json so Claude Code detects it
52
+ const settingsPath = join(homedir(), ".claude", "settings.json");
53
+ const pluginEntry = {
54
+ source: {
55
+ type: "local",
56
+ path: pluginDest,
57
+ },
58
+ };
59
+
60
+ let settings = {};
61
+ if (existsSync(settingsPath)) {
62
+ try {
63
+ settings = JSON.parse(readFileSync(settingsPath, "utf8"));
64
+ } catch {
65
+ settings = {};
66
+ }
67
+ }
68
+
69
+ if (!Array.isArray(settings.enabledPlugins)) {
70
+ settings.enabledPlugins = [];
71
+ }
72
+
73
+ // Only add if not already registered
74
+ const alreadyRegistered = settings.enabledPlugins.some(
75
+ (p) => p.source && p.source.path === pluginDest
76
+ );
77
+ if (!alreadyRegistered) {
78
+ settings.enabledPlugins.push(pluginEntry);
79
+ mkdirSync(join(homedir(), ".claude"), { recursive: true });
80
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
81
+ }
82
+
51
83
  const version = JSON.parse(readFileSync(join(pluginSrc, "package.json"), "utf8")).version;
52
84
  console.log(`\x1b[32m✓\x1b[0m OpenThread plugin v${version} installed to ${pluginDest}`);
53
- console.log(` Use \x1b[1m/share\x1b[0m in Claude Code to share conversations.`);
85
+ console.log(` Registered in ${settingsPath}`);
86
+ console.log(` Use \x1b[1m/ot:share\x1b[0m in Claude Code to share conversations.`);
54
87
  } catch (err) {
55
88
  console.error(`Warning: Could not auto-install plugin: ${err.message}`);
56
89
  console.error(`You can manually install by copying plugin files to ${pluginDest}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openthread/claude-code-plugin",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Share Claude Code conversations to OpenThread",
5
5
  "bin": {
6
6
  "openthread-claude": "bin/cli.sh"
package/scripts/share.sh CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  # Share a Claude Code session to OpenThread
3
3
  # Usage:
4
- # bash share.sh parse <session_file> — output compact markdown from JSONL
5
- # bash share.sh import <session_file> <title> <community_id> <tags> <token> — parse JSONL & import as compact post
6
- # bash share.sh import-export <export_file> <title> <community_id> <tags> <token> — send /export text as body & import
4
+ # bash share.sh parse <session_file> — output compact markdown from JSONL
5
+ # bash share.sh import <session_file> <title> <community_id> <tags> <token> [summary] — parse JSONL & import as compact post
6
+ # bash share.sh import-export <export_file> <title> <community_id> <tags> <token> [summary] — send /export text as body & import
7
7
  set -euo pipefail
8
8
 
9
9
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -94,6 +94,7 @@ import_post() {
94
94
  local community_id="$3"
95
95
  local tags_csv="$4"
96
96
  local access_token="$5"
97
+ local summary="${6:-}"
97
98
 
98
99
  # Parse the session into compact markdown
99
100
  local compact_body
@@ -114,11 +115,14 @@ print(json.dumps(tags))
114
115
  " "$tags_csv")
115
116
  fi
116
117
 
117
- # Build the import payload with body (no rawThread)
118
+ # Build the import payload prepend summary to body if provided
118
119
  local payload
119
120
  payload=$(python3 -c "
120
121
  import json, sys
121
122
  body_text = sys.argv[1]
123
+ summary = sys.argv[5] if len(sys.argv) > 5 and sys.argv[5] else ''
124
+ if summary:
125
+ body_text = '> ' + summary.replace('\n', '\n> ') + '\n\n---\n\n' + body_text
122
126
  payload = {
123
127
  'title': sys.argv[2],
124
128
  'communityId': sys.argv[3],
@@ -128,7 +132,7 @@ payload = {
128
132
  'provider': 'claude',
129
133
  }
130
134
  print(json.dumps(payload))
131
- " "$compact_body" "$title" "$community_id" "$tags_json")
135
+ " "$compact_body" "$title" "$community_id" "$tags_json" "$summary")
132
136
 
133
137
  # POST to import endpoint
134
138
  RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/posts/import" \
@@ -156,6 +160,7 @@ import_export_post() {
156
160
  local community_id="$3"
157
161
  local tags_csv="$4"
158
162
  local access_token="$5"
163
+ local summary="${6:-}"
159
164
 
160
165
  if [ ! -f "$export_file" ]; then
161
166
  echo "ERROR: Export file not found: $export_file" >&2
@@ -180,6 +185,9 @@ with open(sys.argv[1], 'r') as f:
180
185
  export_text = f.read()
181
186
  # Truncate to 500000 chars
182
187
  export_text = export_text[:500000]
188
+ summary = sys.argv[5] if len(sys.argv) > 5 and sys.argv[5] else ''
189
+ if summary:
190
+ export_text = '> ' + summary.replace('\n', '\n> ') + '\n\n---\n\n' + export_text
183
191
  payload = {
184
192
  'title': sys.argv[2],
185
193
  'communityId': sys.argv[3],
@@ -189,7 +197,7 @@ payload = {
189
197
  'provider': 'claude',
190
198
  }
191
199
  print(json.dumps(payload))
192
- " "$export_file" "$title" "$community_id" "$tags_json")
200
+ " "$export_file" "$title" "$community_id" "$tags_json" "$summary")
193
201
 
194
202
  # POST to import endpoint
195
203
  RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/posts/import" \
@@ -221,17 +229,17 @@ case "${1:-}" in
221
229
  ;;
222
230
  import)
223
231
  if [ -z "${6:-}" ]; then
224
- echo "Usage: share.sh import <session_file> <title> <community_id> <tags> <token>" >&2
232
+ echo "Usage: share.sh import <session_file> <title> <community_id> <tags> <token> [summary]" >&2
225
233
  exit 1
226
234
  fi
227
- import_post "$2" "$3" "$4" "${5:--}" "$6"
235
+ import_post "$2" "$3" "$4" "${5:--}" "$6" "${7:-}"
228
236
  ;;
229
237
  import-export)
230
238
  if [ -z "${6:-}" ]; then
231
- echo "Usage: share.sh import-export <export_file> <title> <community_id> <tags> <token>" >&2
239
+ echo "Usage: share.sh import-export <export_file> <title> <community_id> <tags> <token> [summary]" >&2
232
240
  exit 1
233
241
  fi
234
- import_export_post "$2" "$3" "$4" "${5:--}" "$6"
242
+ import_export_post "$2" "$3" "$4" "${5:--}" "$6" "${7:-}"
235
243
  ;;
236
244
  *)
237
245
  echo "Usage: share.sh {parse|import|import-export} ..." >&2