refineo-cli 0.0.1__py3-none-any.whl

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,16 @@
1
+ """Refineo AI Text Humanizer CLI."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .api import humanize, get_usage, start_device_code_flow, poll_for_token
6
+ from .config import load_credentials, save_credentials, clear_credentials
7
+
8
+ __all__ = [
9
+ "humanize",
10
+ "get_usage",
11
+ "start_device_code_flow",
12
+ "poll_for_token",
13
+ "load_credentials",
14
+ "save_credentials",
15
+ "clear_credentials",
16
+ ]
refineo_cli/api.py ADDED
@@ -0,0 +1,237 @@
1
+ """API client for Refineo."""
2
+
3
+ import json
4
+ import time
5
+ import urllib.request
6
+ import urllib.error
7
+ from typing import Optional, Callable, TypedDict, Any
8
+
9
+ from .config import (
10
+ API_BASE_URL,
11
+ Credentials,
12
+ load_credentials,
13
+ save_credentials,
14
+ is_token_expired,
15
+ get_platform_info,
16
+ )
17
+
18
+
19
+ class DeviceCodeResponse(TypedDict):
20
+ device_code: str
21
+ user_code: str
22
+ verification_uri: str
23
+ verification_uri_complete: str
24
+ expires_in: int
25
+ interval: int
26
+
27
+
28
+ class HumanizeResult(TypedDict):
29
+ humanizedText: str
30
+ wordCount: int
31
+ model: str
32
+
33
+
34
+ class UsageStats(TypedDict):
35
+ tier: str
36
+ used: int
37
+ limit: int
38
+ remaining: int
39
+ resetDate: Optional[str]
40
+ wordLimit: int
41
+ rateLimit: Optional[int]
42
+
43
+
44
+ USER_AGENT = get_platform_info()
45
+
46
+
47
+ def _make_request(
48
+ path: str,
49
+ method: str = "GET",
50
+ data: Optional[dict[str, Any]] = None,
51
+ headers: Optional[dict[str, str]] = None,
52
+ ) -> Any:
53
+ """Make an HTTP request."""
54
+ url = f"{API_BASE_URL}{path}"
55
+
56
+ req_headers = {
57
+ "Content-Type": "application/json",
58
+ "User-Agent": USER_AGENT,
59
+ }
60
+ if headers:
61
+ req_headers.update(headers)
62
+
63
+ body = None
64
+ if data is not None:
65
+ body = json.dumps(data).encode("utf-8")
66
+
67
+ req = urllib.request.Request(url, data=body, headers=req_headers, method=method)
68
+
69
+ try:
70
+ with urllib.request.urlopen(req, timeout=120) as response:
71
+ return json.loads(response.read().decode("utf-8"))
72
+ except urllib.error.HTTPError as e:
73
+ error_body = e.read().decode("utf-8")
74
+ try:
75
+ error_data = json.loads(error_body)
76
+ msg = (
77
+ error_data.get("message")
78
+ or error_data.get("error_description")
79
+ or error_data.get("error")
80
+ or f"HTTP {e.code}"
81
+ )
82
+ raise Exception(msg) from e
83
+ except json.JSONDecodeError:
84
+ raise Exception(f"HTTP {e.code}: {error_body}") from e
85
+
86
+
87
+ def _api_request(
88
+ path: str,
89
+ method: str = "GET",
90
+ data: Optional[dict[str, Any]] = None,
91
+ ) -> Any:
92
+ """Make an authenticated API request."""
93
+ credentials = load_credentials()
94
+
95
+ if not credentials:
96
+ raise Exception("Not logged in. Run: refineo login")
97
+
98
+ # Refresh token if expired
99
+ token = credentials["accessToken"]
100
+ if is_token_expired(credentials):
101
+ refreshed = _refresh_token(credentials["refreshToken"])
102
+ if refreshed:
103
+ token = refreshed["accessToken"]
104
+ else:
105
+ raise Exception("Session expired. Run: refineo login")
106
+
107
+ return _make_request(
108
+ path,
109
+ method=method,
110
+ data=data,
111
+ headers={"Authorization": f"Bearer {token}"},
112
+ )
113
+
114
+
115
+ def _refresh_token(refresh_token: str) -> Optional[Credentials]:
116
+ """Refresh access token."""
117
+ try:
118
+ data = _make_request(
119
+ "/api/auth/device/refresh",
120
+ method="POST",
121
+ data={
122
+ "refresh_token": refresh_token,
123
+ "grant_type": "refresh_token",
124
+ },
125
+ )
126
+
127
+ old_credentials = load_credentials()
128
+
129
+ credentials: Credentials = {
130
+ "accessToken": data["access_token"],
131
+ "refreshToken": data["refresh_token"],
132
+ "expiresAt": data["expires_at"],
133
+ "user": old_credentials["user"] if old_credentials else {"email": "", "tier": "", "name": None},
134
+ }
135
+
136
+ save_credentials(credentials)
137
+ return credentials
138
+ except Exception:
139
+ return None
140
+
141
+
142
+ def start_device_code_flow() -> DeviceCodeResponse:
143
+ """Start device code flow."""
144
+ return _make_request("/api/auth/device/code", method="POST")
145
+
146
+
147
+ def poll_for_token(
148
+ device_code: str,
149
+ interval: int,
150
+ expires_in: int,
151
+ on_poll: Optional[Callable[[], None]] = None,
152
+ ) -> Credentials:
153
+ """Poll for device code authorization."""
154
+ start_time = time.time()
155
+ timeout = expires_in
156
+
157
+ while time.time() - start_time < timeout:
158
+ if on_poll:
159
+ on_poll()
160
+
161
+ try:
162
+ data = _make_request(
163
+ "/api/auth/device/token",
164
+ method="POST",
165
+ data={
166
+ "device_code": device_code,
167
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
168
+ },
169
+ )
170
+
171
+ # Success!
172
+ credentials: Credentials = {
173
+ "accessToken": data["access_token"],
174
+ "refreshToken": data["refresh_token"],
175
+ "expiresAt": data["expires_at"],
176
+ "user": data.get("user", {"email": "", "tier": "", "name": None}),
177
+ }
178
+ save_credentials(credentials)
179
+ return credentials
180
+
181
+ except Exception as e:
182
+ error_msg = str(e)
183
+
184
+ if "authorization_pending" in error_msg:
185
+ time.sleep(interval)
186
+ continue
187
+
188
+ if "slow_down" in error_msg:
189
+ time.sleep(interval + 5)
190
+ continue
191
+
192
+ if "access_denied" in error_msg:
193
+ raise Exception(
194
+ "Access denied. CLI requires Pro or Ultra subscription."
195
+ ) from e
196
+
197
+ if "expired_token" in error_msg:
198
+ raise Exception("Login timed out. Please try again.") from e
199
+
200
+ raise
201
+
202
+ raise Exception("Login timed out. Please try again.")
203
+
204
+
205
+ def humanize(
206
+ text: str,
207
+ model: str = "enhanced",
208
+ ) -> HumanizeResult:
209
+ """Humanize text."""
210
+ api_model = "BALANCE" if model == "standard" else "ENHANCED"
211
+
212
+ result = _api_request(
213
+ "/api/humanize",
214
+ method="POST",
215
+ data={"text": text, "model": api_model},
216
+ )
217
+
218
+ return {
219
+ "humanizedText": result["data"]["humanizedText"],
220
+ "wordCount": result["data"]["wordCount"],
221
+ "model": result["data"]["model"],
222
+ }
223
+
224
+
225
+ def get_usage() -> UsageStats:
226
+ """Get usage stats."""
227
+ result = _api_request("/api/usage")
228
+
229
+ return {
230
+ "tier": result["tier"],
231
+ "used": result["used"],
232
+ "limit": result["limit"],
233
+ "remaining": result["remaining"],
234
+ "resetDate": result.get("resetDate"),
235
+ "wordLimit": result["wordLimit"],
236
+ "rateLimit": result.get("rateLimit"),
237
+ }
refineo_cli/cli.py ADDED
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env python3
2
+ """Refineo CLI entry point."""
3
+
4
+ import subprocess
5
+ import sys
6
+ import platform
7
+ from pathlib import Path
8
+
9
+ from .config import load_credentials, clear_credentials
10
+ from .api import start_device_code_flow, poll_for_token, humanize, get_usage
11
+
12
+ VERSION = "0.1.0"
13
+
14
+ # ANSI colors
15
+ RESET = "\033[0m"
16
+ BOLD = "\033[1m"
17
+ DIM = "\033[2m"
18
+ GREEN = "\033[32m"
19
+ YELLOW = "\033[33m"
20
+ BLUE = "\033[34m"
21
+ CYAN = "\033[36m"
22
+ RED = "\033[31m"
23
+
24
+
25
+ def print_error(message: str) -> None:
26
+ """Print error message."""
27
+ print(f"{RED}Error:{RESET} {message}", file=sys.stderr)
28
+
29
+
30
+ def print_success(message: str) -> None:
31
+ """Print success message."""
32
+ print(f"{GREEN}✓{RESET} {message}")
33
+
34
+
35
+ def open_browser(url: str) -> None:
36
+ """Open URL in default browser."""
37
+ system = platform.system()
38
+
39
+ try:
40
+ if system == "Darwin":
41
+ subprocess.run(["open", url], check=True, capture_output=True)
42
+ elif system == "Windows":
43
+ subprocess.run(["start", "", url], check=True, capture_output=True, shell=True)
44
+ else:
45
+ subprocess.run(["xdg-open", url], check=True, capture_output=True)
46
+ except Exception:
47
+ print(f"Please open this URL in your browser: {url}")
48
+
49
+
50
+ def login_command() -> None:
51
+ """Login command."""
52
+ existing = load_credentials()
53
+ if existing:
54
+ print(f"Already logged in as {CYAN}{existing['user']['email']}{RESET}")
55
+ print(f"Tier: {BOLD}{existing['user']['tier']}{RESET}")
56
+ print(f"\nRun {DIM}refineo logout{RESET} to switch accounts.")
57
+ return
58
+
59
+ print(f"{BOLD}Refineo CLI Login{RESET}\n")
60
+
61
+ try:
62
+ device_code = start_device_code_flow()
63
+
64
+ print(f"Your code: {BOLD}{CYAN}{device_code['user_code']}{RESET}\n")
65
+ print("Opening browser to authorize...")
66
+ print(f"{DIM}{device_code['verification_uri_complete']}{RESET}\n")
67
+
68
+ open_browser(device_code["verification_uri_complete"])
69
+
70
+ print("Waiting for authorization...", end="", flush=True)
71
+
72
+ dots = [0]
73
+
74
+ def on_poll() -> None:
75
+ dots[0] = (dots[0] % 3) + 1
76
+ print(f"\rWaiting for authorization{'.' * dots[0]} ", end="", flush=True)
77
+
78
+ credentials = poll_for_token(
79
+ device_code["device_code"],
80
+ device_code["interval"],
81
+ device_code["expires_in"],
82
+ on_poll,
83
+ )
84
+
85
+ print("\r \r", end="")
86
+ print_success(f"Logged in as {CYAN}{credentials['user']['email']}{RESET}")
87
+ print(f"Tier: {BOLD}{credentials['user']['tier']}{RESET}")
88
+
89
+ except Exception as e:
90
+ print()
91
+ print_error(str(e))
92
+ sys.exit(1)
93
+
94
+
95
+ def logout_command() -> None:
96
+ """Logout command."""
97
+ credentials = load_credentials()
98
+
99
+ if not credentials:
100
+ print("Not logged in.")
101
+ return
102
+
103
+ clear_credentials()
104
+ print_success(f"Logged out from {credentials['user']['email']}")
105
+
106
+
107
+ def stats_command() -> None:
108
+ """Stats command."""
109
+ credentials = load_credentials()
110
+
111
+ if not credentials:
112
+ print_error("Not logged in. Run: refineo login")
113
+ sys.exit(1)
114
+
115
+ try:
116
+ usage = get_usage()
117
+
118
+ print(f"{BOLD}Refineo Usage{RESET}\n")
119
+ print(f"Account: {CYAN}{credentials['user']['email']}{RESET}")
120
+ print(f"Plan: {BOLD}{usage['tier']}{RESET}")
121
+ print()
122
+
123
+ if usage["limit"] == -1:
124
+ print(f"Requests: {GREEN}Unlimited{RESET}")
125
+ if usage.get("rateLimit"):
126
+ print(f"Rate limit: {usage['rateLimit']} requests/hour")
127
+ else:
128
+ percentage = round((usage["used"] / usage["limit"]) * 100)
129
+ color = RED if percentage >= 90 else YELLOW if percentage >= 70 else GREEN
130
+ print(f"Requests: {color}{usage['used']}{RESET} / {usage['limit']} ({percentage}%)")
131
+ print(f"Remaining: {usage['remaining']}")
132
+
133
+ print(f"Word limit: {usage['wordLimit']} words/request")
134
+
135
+ if usage.get("resetDate"):
136
+ print(f"Resets: {usage['resetDate']}")
137
+
138
+ except Exception as e:
139
+ print_error(str(e))
140
+ sys.exit(1)
141
+
142
+
143
+ def humanize_command(args: list[str]) -> None:
144
+ """Humanize command."""
145
+ credentials = load_credentials()
146
+
147
+ if not credentials:
148
+ print_error("Not logged in. Run: refineo login")
149
+ sys.exit(1)
150
+
151
+ # Parse arguments
152
+ text = ""
153
+ model = "enhanced"
154
+ input_file = ""
155
+ output_file = ""
156
+
157
+ i = 0
158
+ while i < len(args):
159
+ arg = args[i]
160
+
161
+ if arg in ("--model", "-m"):
162
+ i += 1
163
+ if i < len(args):
164
+ value = args[i]
165
+ if value in ("standard", "enhanced"):
166
+ model = value
167
+ else:
168
+ print_error('Model must be "standard" or "enhanced"')
169
+ sys.exit(1)
170
+ elif arg in ("--file", "-f"):
171
+ i += 1
172
+ if i < len(args):
173
+ input_file = args[i]
174
+ elif arg in ("--output", "-o"):
175
+ i += 1
176
+ if i < len(args):
177
+ output_file = args[i]
178
+ elif not arg.startswith("-"):
179
+ text = arg
180
+
181
+ i += 1
182
+
183
+ # Read from file if specified
184
+ if input_file:
185
+ path = Path(input_file)
186
+ if not path.exists():
187
+ print_error(f"File not found: {input_file}")
188
+ sys.exit(1)
189
+ text = path.read_text()
190
+
191
+ # Read from stdin if no text provided
192
+ if not text:
193
+ if sys.stdin.isatty():
194
+ print_error(
195
+ 'No text provided. Usage: refineo humanize "your text" or echo "text" | refineo humanize'
196
+ )
197
+ sys.exit(1)
198
+ text = sys.stdin.read().strip()
199
+
200
+ if not text:
201
+ print_error("No text provided")
202
+ sys.exit(1)
203
+
204
+ try:
205
+ result = humanize(text, model)
206
+
207
+ if output_file:
208
+ Path(output_file).write_text(result["humanizedText"])
209
+ print_success(f"Output written to {output_file}")
210
+ else:
211
+ print(result["humanizedText"])
212
+
213
+ except Exception as e:
214
+ print_error(str(e))
215
+ sys.exit(1)
216
+
217
+
218
+ def help_command() -> None:
219
+ """Help command."""
220
+ print(f"""
221
+ {BOLD}Refineo CLI{RESET} - AI Text Humanizer
222
+ Version {VERSION}
223
+
224
+ {BOLD}Usage:{RESET}
225
+ refineo <command> [options]
226
+
227
+ {BOLD}Commands:{RESET}
228
+ login Authenticate with your Refineo account
229
+ logout Clear stored credentials
230
+ stats Show usage statistics
231
+ humanize <text> Humanize AI-generated text
232
+
233
+ {BOLD}Humanize Options:{RESET}
234
+ -m, --model <model> Model: "standard" or "enhanced" (default: enhanced)
235
+ -f, --file <path> Read input from file
236
+ -o, --output <path> Write output to file
237
+
238
+ {BOLD}Examples:{RESET}
239
+ {DIM}# Login to your account{RESET}
240
+ refineo login
241
+
242
+ {DIM}# Humanize text directly{RESET}
243
+ refineo humanize "The results indicate a significant correlation."
244
+
245
+ {DIM}# Use standard model{RESET}
246
+ refineo humanize "Text here" --model standard
247
+
248
+ {DIM}# Read from file{RESET}
249
+ refineo humanize --file input.txt --output output.txt
250
+
251
+ {DIM}# Pipe from stdin{RESET}
252
+ echo "AI-generated text" | refineo humanize
253
+
254
+ {DIM}# Check usage{RESET}
255
+ refineo stats
256
+
257
+ {BOLD}More Info:{RESET}
258
+ https://refineo.app/docs/cli
259
+ """)
260
+
261
+
262
+ def version_command() -> None:
263
+ """Version command."""
264
+ print(f"refineo {VERSION}")
265
+
266
+
267
+ def main() -> None:
268
+ """Main entry point."""
269
+ args = sys.argv[1:]
270
+ command = args[0] if args else None
271
+
272
+ if command == "login":
273
+ login_command()
274
+ elif command == "logout":
275
+ logout_command()
276
+ elif command == "stats":
277
+ stats_command()
278
+ elif command == "humanize":
279
+ humanize_command(args[1:])
280
+ elif command in ("help", "--help", "-h"):
281
+ help_command()
282
+ elif command in ("version", "--version", "-v"):
283
+ version_command()
284
+ elif command is None:
285
+ help_command()
286
+ else:
287
+ print_error(f"Unknown command: {command}")
288
+ print(f"Run {DIM}refineo help{RESET} for usage.")
289
+ sys.exit(1)
290
+
291
+
292
+ if __name__ == "__main__":
293
+ main()
refineo_cli/config.py ADDED
@@ -0,0 +1,80 @@
1
+ """Configuration and credentials management."""
2
+
3
+ import json
4
+ import os
5
+ import platform
6
+ from pathlib import Path
7
+ from typing import TypedDict, Optional
8
+
9
+
10
+ class UserInfo(TypedDict):
11
+ email: str
12
+ name: Optional[str]
13
+ tier: str
14
+
15
+
16
+ class Credentials(TypedDict):
17
+ accessToken: str
18
+ refreshToken: str
19
+ expiresAt: int
20
+ user: UserInfo
21
+
22
+
23
+ CONFIG_DIR = Path.home() / ".refineo"
24
+ CREDENTIALS_FILE = CONFIG_DIR / "credentials.json"
25
+
26
+ API_BASE_URL = os.environ.get("REFINEO_API_URL", "https://refineo.app")
27
+
28
+
29
+ def ensure_config_dir() -> None:
30
+ """Ensure config directory exists with proper permissions."""
31
+ CONFIG_DIR.mkdir(mode=0o700, parents=True, exist_ok=True)
32
+
33
+
34
+ def load_credentials() -> Optional[Credentials]:
35
+ """Load credentials from disk."""
36
+ try:
37
+ if not CREDENTIALS_FILE.exists():
38
+ return None
39
+ data = json.loads(CREDENTIALS_FILE.read_text())
40
+ return data
41
+ except Exception:
42
+ return None
43
+
44
+
45
+ def save_credentials(credentials: Credentials) -> None:
46
+ """Save credentials to disk."""
47
+ ensure_config_dir()
48
+ CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))
49
+ CREDENTIALS_FILE.chmod(0o600)
50
+
51
+
52
+ def clear_credentials() -> None:
53
+ """Clear credentials from disk."""
54
+ try:
55
+ if CREDENTIALS_FILE.exists():
56
+ CREDENTIALS_FILE.unlink()
57
+ except Exception:
58
+ pass
59
+
60
+
61
+ def is_token_expired(credentials: Credentials) -> bool:
62
+ """Check if credentials are expired (with 1 minute buffer)."""
63
+ import time
64
+ now = int(time.time())
65
+ return credentials["expiresAt"] <= now + 60
66
+
67
+
68
+ def get_platform_info() -> str:
69
+ """Get current platform info for User-Agent."""
70
+ system = platform.system()
71
+ arch = platform.machine()
72
+ py_version = platform.python_version()
73
+
74
+ os_name = {
75
+ "Darwin": "macOS",
76
+ "Windows": "Windows",
77
+ "Linux": "Linux",
78
+ }.get(system, "Unknown")
79
+
80
+ return f"refineo-cli/0.1.0 ({os_name}; {arch}) Python/{py_version}"
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: refineo-cli
3
+ Version: 0.0.1
4
+ Summary: Refineo AI Text Humanizer CLI - Transform AI-generated text into natural human writing
5
+ Project-URL: Homepage, https://refineo.ai
6
+ Project-URL: Repository, https://github.com/refineo/refineo-ai-tools
7
+ Project-URL: Issues, https://github.com/refineo/refineo-ai-tools/issues
8
+ Author: Refineo
9
+ License-Expression: MIT
10
+ Keywords: ai,cli,humanizer,mcp,refineo,text,writing
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Text Processing
21
+ Requires-Python: >=3.10
22
+ Provides-Extra: dev
23
+ Requires-Dist: mypy>=1.13.0; extra == 'dev'
24
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Refineo AI Tools
29
+
30
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/refineo-cli)](https://pypi.org/project/refineo-cli/)
31
+ [![npm Downloads](https://img.shields.io/npm/dm/@refineo/cli)](https://www.npmjs.com/package/@refineo/cli)
32
+
33
+ CLI and MCP tools for [Refineo](https://refineo.app) - Transform AI-generated text into natural human writing.
34
+
35
+ ## Quick Start
36
+
37
+ ### Node.js / TypeScript
38
+
39
+ ```bash
40
+ # Using bunx (recommended)
41
+ bunx @refineo/cli login
42
+
43
+ # Using npx
44
+ npx @refineo/cli login
45
+
46
+ # Global install
47
+ npm i -g @refineo/cli && refineo login
48
+ ```
49
+
50
+ ### Python
51
+
52
+ ```bash
53
+ # Using uvx (recommended)
54
+ uvx refineo-cli login
55
+
56
+ # Using pipx
57
+ pipx run refineo-cli login
58
+
59
+ # Global install
60
+ pip install refineo-cli && refineo login
61
+ ```
62
+
63
+ ## Commands
64
+
65
+ ```bash
66
+ refineo login # Authenticate with your account
67
+ refineo logout # Clear stored credentials
68
+ refineo stats # Show usage statistics
69
+ refineo humanize "text" # Humanize AI-generated text
70
+ ```
71
+
72
+ ### Humanize Options
73
+
74
+ ```bash
75
+ refineo humanize "text" --model enhanced # Use enhanced model (default)
76
+ refineo humanize "text" --model standard # Use standard model
77
+ refineo humanize --file input.txt # Read from file
78
+ refineo humanize --file input.txt --output output.txt # Write to file
79
+ echo "text" | refineo humanize # Read from stdin
80
+ ```
81
+
82
+ ## Requirements
83
+
84
+ - **Pro or Ultra subscription** - CLI/MCP access is a Pro+ feature
85
+ - Node.js 18+ (for Node CLI)
86
+ - Python 3.10+ (for Python CLI)
87
+
88
+ ## Authentication
89
+
90
+ The CLI uses device code flow for secure authentication:
91
+
92
+ 1. Run `refineo login`
93
+ 2. A browser opens to authorize the device
94
+ 3. Enter the code shown in your terminal
95
+ 4. Credentials are stored securely in `~/.refineo/`
96
+
97
+ ## MCP Integration
98
+
99
+ Refineo provides an MCP (Model Context Protocol) endpoint for integration with Claude, Cursor, and other AI tools.
100
+
101
+ ### Tools Available
102
+
103
+ - **humanize** - Transform AI-generated text into natural human writing
104
+ - **get_usage** - Check remaining quota for current billing period
105
+
106
+ ### Configuration
107
+
108
+ Add to your Claude/Cursor MCP config:
109
+
110
+ ```json
111
+ {
112
+ "mcpServers": {
113
+ "refineo": {
114
+ "url": "https://refineo.app/api/mcp",
115
+ "transport": "http",
116
+ "authentication": {
117
+ "type": "bearer",
118
+ "token": "<your-access-token>"
119
+ }
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ Get your access token by running `refineo login` and checking `~/.refineo/credentials.json`.
126
+
127
+ ## Development
128
+
129
+ ### Node CLI
130
+
131
+ ```bash
132
+ cd node
133
+ npm install
134
+ npm run build
135
+ npm run test
136
+ ```
137
+
138
+ ### Python CLI
139
+
140
+ ```bash
141
+ cd python
142
+ pip install -e ".[dev]"
143
+ pytest
144
+ ```
145
+
146
+ ## License
147
+
148
+ MIT
@@ -0,0 +1,8 @@
1
+ refineo_cli/__init__.py,sha256=BIzN5T8plbx6FDib_3JXo9kMzlk-8FskiOx0OeznLEI,385
2
+ refineo_cli/api.py,sha256=U_lrTu56pW-y33rvDsUb5npNEoyNpU_VQNKjlCB3o8E,6248
3
+ refineo_cli/cli.py,sha256=LOKuMWP0n2C5gVtMwTkyjT36oKLYr35Ujqc2wApYBkQ,8033
4
+ refineo_cli/config.py,sha256=EXiRtiaWbJ_NhFsGSDQD3igrtezpsmgwrd-Pi5V-n6A,1976
5
+ refineo_cli-0.0.1.dist-info/METADATA,sha256=S7xQgz1Zm6VOdBE59gpwjKAmxNTn-9TSCpF8n7XXJlQ,3714
6
+ refineo_cli-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
+ refineo_cli-0.0.1.dist-info/entry_points.txt,sha256=0ZfAhBoW3AWkwIZDD_ZXcHbB4KwsBI3-dWB_lHxO6_g,49
8
+ refineo_cli-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ refineo = refineo_cli.cli:main