@openthread/claude-code-plugin 0.1.1 → 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.
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +64 -24
- package/bin/cli.sh +95 -7
- package/bin/postinstall.js +34 -1
- package/package.json +1 -1
- package/scripts/auth.sh +3 -3
- package/scripts/share.sh +21 -13
- package/scripts/token.sh +2 -2
- package/skills/share-thread/SKILL.md +3 -3
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @openthread/claude-code-plugin
|
|
2
2
|
|
|
3
|
-
Share Claude Code conversations to [OpenThread](https://openthread.
|
|
3
|
+
Share Claude Code conversations to [OpenThread](https://openthread.me) -- a Reddit-like platform for AI conversation threads.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@openthread/claude-code-plugin)
|
|
6
|
-
[](https://
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](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.dev) -- a Red
|
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
Community: Coding with AI
|
|
25
|
+
Tags: typescript, authentication, debugging
|
|
26
26
|
|
|
27
|
-
|
|
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.dev/c/ClaudeCode/posts/abc123
|
|
|
40
79
|
### Quick mode
|
|
41
80
|
|
|
42
81
|
```
|
|
43
|
-
/share
|
|
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 (
|
|
58
|
-
3. Tags (
|
|
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` |
|
|
65
|
-
| `openthread-claude uninstall` | Remove the plugin from Claude Code |
|
|
66
|
-
| `openthread-claude status` | Show
|
|
67
|
-
| `openthread-claude update` |
|
|
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
|
|
|
@@ -72,16 +111,17 @@ Environment variables override the default endpoints. Set them in your shell pro
|
|
|
72
111
|
|
|
73
112
|
| Variable | Default | Description |
|
|
74
113
|
| --- | --- | --- |
|
|
75
|
-
| `OPENTHREAD_API_URL` | `https://
|
|
76
|
-
| `OPENTHREAD_WEB_URL` | `https://openthread.
|
|
114
|
+
| `OPENTHREAD_API_URL` | `https://openthread.me/api` | Backend API base URL |
|
|
115
|
+
| `OPENTHREAD_WEB_URL` | `https://openthread.me` | Web app base URL (used for post links) |
|
|
77
116
|
|
|
78
117
|
## Manual Install
|
|
79
118
|
|
|
80
119
|
If you prefer not to use npm:
|
|
81
120
|
|
|
82
|
-
1. Download the latest `.zip` release from [openthread.
|
|
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.
|
|
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
|
|
14
|
-
echo " uninstall Remove plugin from
|
|
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 "
|
|
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
|
-
|
|
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 "✓
|
|
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 "✗
|
|
141
|
+
echo "✗ Registered: no (Claude Code won't detect the plugin)"
|
|
54
142
|
echo " Run: openthread-claude install"
|
|
55
143
|
fi
|
|
56
144
|
}
|
package/bin/postinstall.js
CHANGED
|
@@ -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(`
|
|
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
package/scripts/auth.sh
CHANGED
|
@@ -7,8 +7,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
7
7
|
# shellcheck source=token.sh
|
|
8
8
|
source "$SCRIPT_DIR/token.sh"
|
|
9
9
|
|
|
10
|
-
API_BASE="${OPENTHREAD_API_URL:-https://
|
|
11
|
-
WEB_BASE="${OPENTHREAD_WEB_URL:-https://openthread.
|
|
10
|
+
API_BASE="${OPENTHREAD_API_URL:-https://openthread.me/api}"
|
|
11
|
+
WEB_BASE="${OPENTHREAD_WEB_URL:-https://openthread.me}"
|
|
12
12
|
EXTENSION_ID="claude-code"
|
|
13
13
|
CALLBACK_PORT=18923
|
|
14
14
|
REDIRECT_URI="http://localhost:${CALLBACK_PORT}/callback"
|
|
@@ -149,7 +149,7 @@ exchange_code() {
|
|
|
149
149
|
|
|
150
150
|
echo "Exchanging authorization code for tokens..." >&2
|
|
151
151
|
|
|
152
|
-
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/
|
|
152
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/auth/extension/token" \
|
|
153
153
|
-H "Content-Type: application/json" \
|
|
154
154
|
-d "{
|
|
155
155
|
\"code\": \"${auth_code}\",
|
package/scripts/share.sh
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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>
|
|
5
|
-
# bash share.sh import <session_file> <title> <community_id> <tags> <token>
|
|
6
|
-
# bash share.sh import-export <export_file> <title> <community_id> <tags> <token>
|
|
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)"
|
|
10
|
-
API_BASE="${OPENTHREAD_API_URL:-https://
|
|
10
|
+
API_BASE="${OPENTHREAD_API_URL:-https://openthread.me/api}"
|
|
11
11
|
|
|
12
12
|
# --- Parse JSONL session file into compact markdown body ---
|
|
13
13
|
# Extracts only text blocks from assistant messages, skips thinking/tool_use/tool_result/etc.
|
|
@@ -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
|
|
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,10 +132,10 @@ 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
|
-
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/
|
|
138
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/posts/import" \
|
|
135
139
|
-H "Authorization: Bearer ${access_token}" \
|
|
136
140
|
-H "Content-Type: application/json" \
|
|
137
141
|
-d "$payload")
|
|
@@ -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,10 +197,10 @@ 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
|
-
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/
|
|
203
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/posts/import" \
|
|
196
204
|
-H "Authorization: Bearer ${access_token}" \
|
|
197
205
|
-H "Content-Type: application/json" \
|
|
198
206
|
-d "$payload")
|
|
@@ -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
|
package/scripts/token.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# bash token.sh check — exit 0 if authenticated, 1 if not
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
10
|
-
API_BASE="${OPENTHREAD_API_URL:-https://
|
|
10
|
+
API_BASE="${OPENTHREAD_API_URL:-https://openthread.me/api}"
|
|
11
11
|
EXTENSION_ID="claude-code"
|
|
12
12
|
CREDENTIALS_DIR="$HOME/.config/openthread"
|
|
13
13
|
CREDENTIALS_FILE="$CREDENTIALS_DIR/credentials.json"
|
|
@@ -75,7 +75,7 @@ refresh_token() {
|
|
|
75
75
|
local current_refresh
|
|
76
76
|
current_refresh=$(get_refresh_token) || return 1
|
|
77
77
|
|
|
78
|
-
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/
|
|
78
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${API_BASE}/auth/extension/refresh" \
|
|
79
79
|
-H "Content-Type: application/json" \
|
|
80
80
|
-d "{
|
|
81
81
|
\"refreshToken\": \"${current_refresh}\",
|
|
@@ -10,8 +10,8 @@ This skill shares the current Claude Code conversation to OpenThread in 4 steps.
|
|
|
10
10
|
## Configuration
|
|
11
11
|
|
|
12
12
|
```
|
|
13
|
-
API_BASE="${OPENTHREAD_API_URL:-
|
|
14
|
-
WEB_BASE="${OPENTHREAD_WEB_URL:-
|
|
13
|
+
API_BASE="${OPENTHREAD_API_URL:-https://openthread.me/api}"
|
|
14
|
+
WEB_BASE="${OPENTHREAD_WEB_URL:-https://openthread.me}"
|
|
15
15
|
PLUGIN_DIR=<directory containing this skill — find it relative to this file, e.g. apps/claude-code-plugin or ~/.claude/plugins/openthread-share>
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -93,7 +93,7 @@ First, check if the `/share-thread` command was invoked with **arguments** (any
|
|
|
93
93
|
|
|
94
94
|
1. **Fetch communities**:
|
|
95
95
|
```bash
|
|
96
|
-
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$API_BASE/
|
|
96
|
+
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$API_BASE/communities?limit=50"
|
|
97
97
|
```
|
|
98
98
|
Response format: `{"data": [{"id": "uuid", "name": "Name", "slug": "slug"}, ...], "pagination": {...}}`
|
|
99
99
|
|