codedthemes-cli 0.1.15__tar.gz → 0.1.17__tar.gz
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.
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/PKG-INFO +10 -4
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/README.md +9 -3
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/cli.py +119 -33
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/PKG-INFO +10 -4
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/pyproject.toml +1 -1
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/__init__.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/config.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/mcp_client.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/patch_utils.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/repo_utils.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes/sync_manager.py +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/SOURCES.txt +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/dependency_links.txt +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/entry_points.txt +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/requires.txt +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/top_level.txt +0 -0
- {codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codedthemes-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: CLI tool for Code Theme and Integration
|
|
5
5
|
Author: codedthemes
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -46,15 +46,21 @@ Log out from the current device to free up a license slot on the server.
|
|
|
46
46
|
codedthemes logout
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
### 3. Initialization
|
|
50
|
-
|
|
49
|
+
### 3. Initialization
|
|
51
50
|
Initialize your repository to establish a baseline for synchronization. This is recommended for first-time use in a project.
|
|
52
51
|
|
|
53
52
|
```bash
|
|
54
53
|
codedthemes init
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
###
|
|
56
|
+
### 4. Reactivation
|
|
57
|
+
If your project workspace has been evicted due to inactivity (30+ minutes), use `reinit` to quickly restore it.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codedthemes reinit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 5. Applying Changes
|
|
58
64
|
|
|
59
65
|
Describe the changes you want to make in natural language. The CLI will analyze your repository, plan the changes, and ask for your approval before patching.
|
|
60
66
|
|
|
@@ -36,15 +36,21 @@ Log out from the current device to free up a license slot on the server.
|
|
|
36
36
|
codedthemes logout
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
### 3. Initialization
|
|
40
|
-
|
|
39
|
+
### 3. Initialization
|
|
41
40
|
Initialize your repository to establish a baseline for synchronization. This is recommended for first-time use in a project.
|
|
42
41
|
|
|
43
42
|
```bash
|
|
44
43
|
codedthemes init
|
|
45
44
|
```
|
|
46
45
|
|
|
47
|
-
###
|
|
46
|
+
### 4. Reactivation
|
|
47
|
+
If your project workspace has been evicted due to inactivity (30+ minutes), use `reinit` to quickly restore it.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
codedthemes reinit
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 5. Applying Changes
|
|
48
54
|
|
|
49
55
|
Describe the changes you want to make in natural language. The CLI will analyze your repository, plan the changes, and ask for your approval before patching.
|
|
50
56
|
|
|
@@ -240,10 +240,81 @@ def handle_init():
|
|
|
240
240
|
sys.exit(1)
|
|
241
241
|
|
|
242
242
|
|
|
243
|
+
def handle_reinit():
|
|
244
|
+
"""
|
|
245
|
+
Reactivates an evicted workspace by re-uploading the repository to the cloud.
|
|
246
|
+
"""
|
|
247
|
+
try:
|
|
248
|
+
repo_root = detect_repo_root()
|
|
249
|
+
print(f"🔍 Found repository at {repo_root}")
|
|
250
|
+
|
|
251
|
+
client = MCPClient()
|
|
252
|
+
if not client.token:
|
|
253
|
+
print("✖ Not logged in. Please run 'codedthemes login' first.")
|
|
254
|
+
sys.exit(1)
|
|
255
|
+
|
|
256
|
+
user_email = "unknown_user"
|
|
257
|
+
try:
|
|
258
|
+
decoded = jwt.decode(client.token, options={"verify_signature": False})
|
|
259
|
+
user_email = decoded.get("email", "unknown_user")
|
|
260
|
+
except:
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
repo_abs_path = os.path.abspath(repo_root)
|
|
264
|
+
repo_key = repo_abs_path.lower().replace(os.sep, "/")
|
|
265
|
+
|
|
266
|
+
print("🚀 Reactivating repository (this may take a moment)...")
|
|
267
|
+
upload_result = client.upload_workspace(repo_abs_path, user_email)
|
|
268
|
+
workspace_id = upload_result.get("workspace_id")
|
|
269
|
+
|
|
270
|
+
if not workspace_id:
|
|
271
|
+
print("✖ Reactivation failed: No workspace ID returned.")
|
|
272
|
+
sys.exit(1)
|
|
273
|
+
|
|
274
|
+
config = load_config()
|
|
275
|
+
last_workspaces = config.get("workspaces", {})
|
|
276
|
+
last_workspaces[repo_key] = workspace_id
|
|
277
|
+
config["workspaces"] = last_workspaces
|
|
278
|
+
save_config(config)
|
|
279
|
+
|
|
280
|
+
sync_manager = SyncManager()
|
|
281
|
+
sync_manager.update_sync_state(repo_abs_path, workspace_id, user_email)
|
|
282
|
+
|
|
283
|
+
print(f"✔ Workspace reactivated successfully.")
|
|
284
|
+
print(f"✔ Workspace ID: {workspace_id}")
|
|
285
|
+
print("\n" + "="*60)
|
|
286
|
+
print("Your repository is now synced and active in the cloud!")
|
|
287
|
+
print("You can now return to your IDE and continue using the AI assistant.")
|
|
288
|
+
print("="*60)
|
|
289
|
+
|
|
290
|
+
except Exception as e:
|
|
291
|
+
print(f"✖ Error during reactivation: {e}")
|
|
292
|
+
sys.exit(1)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def clean_remote_path(p: str) -> str:
|
|
296
|
+
"""
|
|
297
|
+
Strips the deeply nested remote container path if present.
|
|
298
|
+
e.g., 'home/runner/work/<owner>/<repo>/src/...' -> 'src/...'
|
|
299
|
+
"""
|
|
300
|
+
if not p: return p
|
|
301
|
+
p = p.replace('\\', '/')
|
|
302
|
+
if p.startswith('/'): p = p[1:]
|
|
303
|
+
if p.startswith('home/runner/work/'):
|
|
304
|
+
parts = p.split('/')
|
|
305
|
+
if len(parts) > 4: # e.g. ['home', 'runner', 'work', 'owner', 'repo', 'src', ...]
|
|
306
|
+
return '/'.join(parts[5:])
|
|
307
|
+
return p
|
|
308
|
+
|
|
243
309
|
def handle_apply(query: str):
|
|
244
310
|
"""
|
|
245
311
|
Plans and executes changes based on a natural language query.
|
|
246
312
|
"""
|
|
313
|
+
if not query or not query.strip():
|
|
314
|
+
print("✖ Error: Query cannot be empty.")
|
|
315
|
+
print("💡 Example: codedthemes apply \"Update the primary color to deep purple\"")
|
|
316
|
+
return
|
|
317
|
+
|
|
247
318
|
try:
|
|
248
319
|
repo_root = detect_repo_root()
|
|
249
320
|
repo_abs_path = os.path.abspath(repo_root)
|
|
@@ -335,10 +406,10 @@ def handle_apply(query: str):
|
|
|
335
406
|
print(f"✖ Planning failed: {plan.get('message', 'Unknown error')}")
|
|
336
407
|
return
|
|
337
408
|
|
|
338
|
-
target_files = plan.get("files_to_modify", [])
|
|
409
|
+
target_files = [clean_remote_path(f) for f in plan.get("files_to_modify", [])]
|
|
339
410
|
instructions = plan.get("plan_instructions", [])
|
|
340
|
-
files_to_create = plan.get("files_to_create", [])
|
|
341
|
-
files_to_delete = plan.get("files_to_delete", [])
|
|
411
|
+
files_to_create = [clean_remote_path(f) for f in plan.get("files_to_create", [])]
|
|
412
|
+
files_to_delete = [clean_remote_path(f) for f in plan.get("files_to_delete", [])]
|
|
342
413
|
|
|
343
414
|
if not any([target_files, files_to_create, files_to_delete, instructions]):
|
|
344
415
|
print("Information: No changes planned.")
|
|
@@ -396,41 +467,52 @@ def handle_apply(query: str):
|
|
|
396
467
|
updates = res_data.get("updates_for_local", [])
|
|
397
468
|
deletes = res_data.get("deleted_files", [])
|
|
398
469
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
470
|
+
# 1. Apply local updates
|
|
471
|
+
for item in updates:
|
|
472
|
+
rel_path = clean_remote_path(item.get("path"))
|
|
473
|
+
code = item.get("code")
|
|
474
|
+
if not rel_path or code is None: continue
|
|
475
|
+
abs_path = os.path.abspath(os.path.join(repo_abs_path, rel_path.replace('/', os.sep)))
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
|
|
479
|
+
with open(abs_path, 'w', encoding='utf-8') as f:
|
|
480
|
+
f.write(code)
|
|
481
|
+
print(f"✔ Updated: {rel_path}")
|
|
482
|
+
except Exception as e:
|
|
483
|
+
print(f"✖ Error writing {rel_path}: {e}")
|
|
404
484
|
|
|
485
|
+
# 2. Apply local deletions
|
|
486
|
+
for rel_path_raw in (deletes or []):
|
|
487
|
+
rel_path = clean_remote_path(rel_path_raw)
|
|
488
|
+
abs_path = os.path.normpath(os.path.join(repo_abs_path, rel_path.replace('/', os.sep)))
|
|
489
|
+
if os.path.exists(abs_path) and abs_path.startswith(os.path.abspath(repo_abs_path)):
|
|
405
490
|
try:
|
|
406
|
-
os.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
for rel_path in (deletes or []):
|
|
414
|
-
abs_path = os.path.normpath(os.path.join(repo_abs_path, rel_path.replace('/', os.sep)))
|
|
415
|
-
if os.path.exists(abs_path) and abs_path.startswith(os.path.abspath(repo_abs_path)):
|
|
416
|
-
try:
|
|
417
|
-
if os.path.isfile(abs_path): os.remove(abs_path)
|
|
418
|
-
elif os.path.isdir(abs_path): shutil.rmtree(abs_path)
|
|
419
|
-
print(f"✔ Deleted: {rel_path}")
|
|
420
|
-
except: pass
|
|
421
|
-
|
|
491
|
+
if os.path.isfile(abs_path): os.remove(abs_path)
|
|
492
|
+
elif os.path.isdir(abs_path): shutil.rmtree(abs_path)
|
|
493
|
+
print(f"✔ Deleted: {rel_path}")
|
|
494
|
+
except: pass
|
|
495
|
+
|
|
496
|
+
# 3. Synchronize state
|
|
497
|
+
if updates or deletes:
|
|
422
498
|
sync_manager.update_sync_state(repo_abs_path, workspace_id, user_email)
|
|
423
499
|
print("✔ Local sync state updated.")
|
|
424
500
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
501
|
+
# 4. Show Integration Report (Always show errors or details)
|
|
502
|
+
_details_raw = res_data.get("details", [])
|
|
503
|
+
details_list = _details_raw.get("report", []) if isinstance(_details_raw, dict) else (_details_raw if isinstance(_details_raw, list) else [])
|
|
504
|
+
if details_list:
|
|
505
|
+
print("\n📊 Integration Report:")
|
|
506
|
+
for item in details_list:
|
|
507
|
+
status_icon = "✔" if item.get("success") else "✖"
|
|
508
|
+
print(f" [{status_icon}] {item.get('file_path')}: {item.get('message', '')}")
|
|
509
|
+
|
|
510
|
+
# 5. Output Final Message if no updates were applied
|
|
511
|
+
if not updates and not deletes:
|
|
512
|
+
msg = res_data.get('message', 'Changes applied successfully.')
|
|
513
|
+
if isinstance(msg, str) and "AI MUST now sync" in msg:
|
|
514
|
+
msg = msg.split("AI MUST now sync")[0].strip()
|
|
515
|
+
print(f"✔ {msg}")
|
|
434
516
|
|
|
435
517
|
except Exception as e:
|
|
436
518
|
print(f"✖ Error: {e}")
|
|
@@ -457,9 +539,11 @@ def main():
|
|
|
457
539
|
apply_parser.add_argument("query", help="Description of changes to make")
|
|
458
540
|
|
|
459
541
|
subparsers.add_parser("init", help="Initialize repository and sync to cloud")
|
|
542
|
+
subparsers.add_parser("reinit", help="Reactivate an evicted workspace and sync to cloud")
|
|
460
543
|
subparsers.add_parser("logout", help="Log out from current device")
|
|
461
544
|
|
|
462
545
|
|
|
546
|
+
|
|
463
547
|
args = parser.parse_args()
|
|
464
548
|
|
|
465
549
|
if not args.command:
|
|
@@ -470,6 +554,8 @@ def main():
|
|
|
470
554
|
handle_login(args.server)
|
|
471
555
|
elif args.command == "init":
|
|
472
556
|
handle_init()
|
|
557
|
+
elif args.command == "reinit":
|
|
558
|
+
handle_reinit()
|
|
473
559
|
elif args.command == "apply":
|
|
474
560
|
handle_apply(args.query)
|
|
475
561
|
elif args.command == "logout":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codedthemes-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: CLI tool for Code Theme and Integration
|
|
5
5
|
Author: codedthemes
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -46,15 +46,21 @@ Log out from the current device to free up a license slot on the server.
|
|
|
46
46
|
codedthemes logout
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
### 3. Initialization
|
|
50
|
-
|
|
49
|
+
### 3. Initialization
|
|
51
50
|
Initialize your repository to establish a baseline for synchronization. This is recommended for first-time use in a project.
|
|
52
51
|
|
|
53
52
|
```bash
|
|
54
53
|
codedthemes init
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
###
|
|
56
|
+
### 4. Reactivation
|
|
57
|
+
If your project workspace has been evicted due to inactivity (30+ minutes), use `reinit` to quickly restore it.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codedthemes reinit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 5. Applying Changes
|
|
58
64
|
|
|
59
65
|
Describe the changes you want to make in natural language. The CLI will analyze your repository, plan the changes, and ask for your approval before patching.
|
|
60
66
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codedthemes_cli-0.1.15 → codedthemes_cli-0.1.17}/codedthemes_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|