aline-ai 0.5.3__py3-none-any.whl → 0.5.5__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.
- {aline_ai-0.5.3.dist-info → aline_ai-0.5.5.dist-info}/METADATA +1 -1
- aline_ai-0.5.5.dist-info/RECORD +93 -0
- realign/__init__.py +1 -1
- realign/adapters/antigravity.py +28 -20
- realign/adapters/base.py +46 -50
- realign/adapters/claude.py +14 -14
- realign/adapters/codex.py +7 -7
- realign/adapters/gemini.py +11 -11
- realign/adapters/registry.py +14 -10
- realign/claude_detector.py +2 -2
- realign/claude_hooks/__init__.py +3 -3
- realign/claude_hooks/permission_request_hook.py +35 -0
- realign/claude_hooks/permission_request_hook_installer.py +31 -32
- realign/claude_hooks/stop_hook.py +4 -1
- realign/claude_hooks/stop_hook_installer.py +30 -31
- realign/cli.py +24 -0
- realign/codex_detector.py +11 -11
- realign/commands/add.py +361 -35
- realign/commands/config.py +3 -12
- realign/commands/context.py +3 -1
- realign/commands/export_shares.py +86 -127
- realign/commands/import_shares.py +145 -155
- realign/commands/init.py +166 -30
- realign/commands/restore.py +18 -6
- realign/commands/search.py +14 -42
- realign/commands/upgrade.py +155 -11
- realign/commands/watcher.py +98 -219
- realign/commands/worker.py +29 -6
- realign/config.py +25 -20
- realign/context.py +1 -3
- realign/dashboard/app.py +4 -4
- realign/dashboard/screens/create_event.py +3 -1
- realign/dashboard/screens/event_detail.py +14 -6
- realign/dashboard/screens/session_detail.py +3 -1
- realign/dashboard/screens/share_import.py +7 -3
- realign/dashboard/tmux_manager.py +91 -22
- realign/dashboard/widgets/config_panel.py +85 -1
- realign/dashboard/widgets/events_table.py +3 -1
- realign/dashboard/widgets/header.py +1 -0
- realign/dashboard/widgets/search_panel.py +37 -27
- realign/dashboard/widgets/sessions_table.py +24 -15
- realign/dashboard/widgets/terminal_panel.py +207 -17
- realign/dashboard/widgets/watcher_panel.py +6 -2
- realign/dashboard/widgets/worker_panel.py +10 -1
- realign/db/__init__.py +1 -1
- realign/db/base.py +5 -15
- realign/db/locks.py +0 -1
- realign/db/migration.py +82 -76
- realign/db/schema.py +2 -6
- realign/db/sqlite_db.py +23 -41
- realign/events/__init__.py +0 -1
- realign/events/event_summarizer.py +27 -15
- realign/events/session_summarizer.py +29 -15
- realign/file_lock.py +1 -0
- realign/hooks.py +150 -60
- realign/logging_config.py +12 -15
- realign/mcp_server.py +30 -51
- realign/mcp_watcher.py +0 -1
- realign/models/event.py +29 -20
- realign/prompts/__init__.py +7 -7
- realign/prompts/presets.py +15 -11
- realign/redactor.py +99 -59
- realign/triggers/__init__.py +9 -9
- realign/triggers/antigravity_trigger.py +30 -28
- realign/triggers/base.py +4 -3
- realign/triggers/claude_trigger.py +104 -85
- realign/triggers/codex_trigger.py +15 -5
- realign/triggers/gemini_trigger.py +57 -47
- realign/triggers/next_turn_trigger.py +3 -1
- realign/triggers/registry.py +6 -2
- realign/triggers/turn_status.py +3 -1
- realign/watcher_core.py +306 -131
- realign/watcher_daemon.py +8 -8
- realign/worker_core.py +3 -1
- realign/worker_daemon.py +3 -1
- aline_ai-0.5.3.dist-info/RECORD +0 -93
- {aline_ai-0.5.3.dist-info → aline_ai-0.5.5.dist-info}/WHEEL +0 -0
- {aline_ai-0.5.3.dist-info → aline_ai-0.5.5.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.5.3.dist-info → aline_ai-0.5.5.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.5.3.dist-info → aline_ai-0.5.5.dist-info}/top_level.txt +0 -0
realign/commands/add.py
CHANGED
|
@@ -251,10 +251,233 @@ Use this skill when the user wants to:
|
|
|
251
251
|
- Send Slack updates about completed work
|
|
252
252
|
"""
|
|
253
253
|
|
|
254
|
+
# Aline Import History Sessions skill definition for Claude Code
|
|
255
|
+
# Installed to ~/.claude/skills/aline-import-history-sessions/SKILL.md
|
|
256
|
+
ALINE_IMPORT_HISTORY_SESSIONS_SKILL_MD = """---
|
|
257
|
+
name: aline-import-history-sessions
|
|
258
|
+
description: Guide users through importing Claude Code session history into Aline database. Use this for first-time setup, onboarding new users, or when users want to selectively import historical sessions. Provides interactive workflow with progress checking.
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
# Aline Import History Sessions Skill
|
|
262
|
+
|
|
263
|
+
This skill guides users through the process of importing Claude Code session history into Aline's database. It provides an interactive, step-by-step workflow to help users discover, select, and import their historical sessions.
|
|
264
|
+
|
|
265
|
+
## Workflow Overview
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
Analyze Unimported Sessions → Present Summary → User Selection → Import Sessions → Verify Success → Continue or Finish
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Step-by-Step Guide
|
|
272
|
+
|
|
273
|
+
### Step 1: Analyze Current Status
|
|
274
|
+
|
|
275
|
+
First, list all sessions to understand what hasn't been imported yet:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
aline watcher session list --detect-turns
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Internal analysis (do NOT expose status terminology to user):**
|
|
282
|
+
- Count sessions with status `new` → these are "unimported sessions"
|
|
283
|
+
- Count sessions with status `partial` → these have "updates available"
|
|
284
|
+
- Count sessions with status `tracked` → these are "already imported"
|
|
285
|
+
|
|
286
|
+
Parse the output to extract:
|
|
287
|
+
- Total number of unimported sessions
|
|
288
|
+
- Their session IDs (use these for import, NOT index numbers)
|
|
289
|
+
- Project paths they belong to
|
|
290
|
+
|
|
291
|
+
### Step 2: Present Summary to User
|
|
292
|
+
|
|
293
|
+
Present a user-friendly summary WITHOUT mentioning internal status labels:
|
|
294
|
+
|
|
295
|
+
Example:
|
|
296
|
+
> "I found **47 sessions** in your Claude Code history:
|
|
297
|
+
> - **12 sessions** haven't been imported yet
|
|
298
|
+
> - **3 sessions** have updates since last import
|
|
299
|
+
> - **32 sessions** are already fully imported
|
|
300
|
+
>
|
|
301
|
+
> The unimported sessions span these projects:
|
|
302
|
+
> - `/Users/you/Projects/ProjectA` (5 sessions)
|
|
303
|
+
> - `/Users/you/Projects/ProjectB` (7 sessions)"
|
|
304
|
+
|
|
305
|
+
### Step 3: Ask User Import Preferences
|
|
306
|
+
|
|
307
|
+
Use `AskUserQuestion` to understand what the user wants to import:
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"questions": [{
|
|
312
|
+
"header": "Import scope",
|
|
313
|
+
"question": "Which sessions would you like to import?",
|
|
314
|
+
"options": [
|
|
315
|
+
{"label": "All unimported (Recommended)", "description": "Import all 12 sessions that haven't been imported yet"},
|
|
316
|
+
{"label": "Include updates", "description": "Import unimported sessions + update 3 sessions with new content"},
|
|
317
|
+
{"label": "Select by project", "description": "Choose which project's sessions to import"},
|
|
318
|
+
{"label": "Select specific", "description": "I'll review and pick individual sessions"}
|
|
319
|
+
],
|
|
320
|
+
"multiSelect": false
|
|
321
|
+
}]
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Step 4: Handle User Selection
|
|
326
|
+
|
|
327
|
+
#### If "All unimported":
|
|
328
|
+
Confirm the import with session count:
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"questions": [{
|
|
332
|
+
"header": "Confirm",
|
|
333
|
+
"question": "Ready to import 12 sessions. Proceed?",
|
|
334
|
+
"options": [
|
|
335
|
+
{"label": "Yes, import", "description": "Start importing all unimported sessions"},
|
|
336
|
+
{"label": "Let me review first", "description": "Show me the session list to review"}
|
|
337
|
+
],
|
|
338
|
+
"multiSelect": false
|
|
339
|
+
}]
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
#### If "Select by project":
|
|
344
|
+
List the projects with unimported sessions and ask:
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"questions": [{
|
|
348
|
+
"header": "Project",
|
|
349
|
+
"question": "Which project's sessions should I import?",
|
|
350
|
+
"options": [
|
|
351
|
+
{"label": "ProjectA", "description": "5 unimported sessions"},
|
|
352
|
+
{"label": "ProjectB", "description": "7 unimported sessions"},
|
|
353
|
+
{"label": "Current directory", "description": "Import sessions from the current working directory"}
|
|
354
|
+
],
|
|
355
|
+
"multiSelect": true
|
|
356
|
+
}]
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### If "Select specific":
|
|
361
|
+
Show the session list with details (project path, turn count, last modified) and let user specify which ones. When user provides selection, map their choice back to session IDs.
|
|
362
|
+
|
|
363
|
+
### Step 5: Execute Import
|
|
364
|
+
|
|
365
|
+
**IMPORTANT: Always use session_id for imports, NOT index numbers.** Index numbers can change between list operations and cause wrong sessions to be imported.
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
# Import by session ID (PREFERRED - always use this)
|
|
369
|
+
aline watcher session import abc12345-6789-...
|
|
370
|
+
|
|
371
|
+
# Import multiple by session ID
|
|
372
|
+
aline watcher session import abc12345,def67890,ghi11111
|
|
373
|
+
|
|
374
|
+
# With force flag (re-import already tracked)
|
|
375
|
+
aline watcher session import abc12345 --force
|
|
376
|
+
|
|
377
|
+
# With regenerate flag (update summaries)
|
|
378
|
+
aline watcher session import abc12345 --regenerate
|
|
379
|
+
|
|
380
|
+
# Synchronous import to wait for completion
|
|
381
|
+
aline watcher session import abc12345 --sync
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
For importing multiple sessions, collect all the session IDs from your analysis and pass them comma-separated.
|
|
385
|
+
|
|
386
|
+
### Step 6: Verify Import Success
|
|
387
|
+
|
|
388
|
+
After import, check the status again:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
aline watcher session list --detect-turns
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Verify:
|
|
395
|
+
- Previously unimported sessions should now show as imported
|
|
396
|
+
- Check for any errors in the import output
|
|
397
|
+
- Report success/failure count to user
|
|
398
|
+
|
|
399
|
+
### Step 7: Ask If User Is Satisfied
|
|
400
|
+
|
|
401
|
+
```json
|
|
402
|
+
{
|
|
403
|
+
"questions": [{
|
|
404
|
+
"header": "Continue?",
|
|
405
|
+
"question": "Successfully imported X sessions. What would you like to do next?",
|
|
406
|
+
"options": [
|
|
407
|
+
{"label": "Import more", "description": "Select additional sessions to import"},
|
|
408
|
+
{"label": "View imported", "description": "Show details of imported sessions"},
|
|
409
|
+
{"label": "Done", "description": "Finish the import process"}
|
|
410
|
+
],
|
|
411
|
+
"multiSelect": false
|
|
412
|
+
}]
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
If user wants to import more, loop back to Step 1.
|
|
417
|
+
|
|
418
|
+
### Step 8: Final Summary
|
|
419
|
+
|
|
420
|
+
When the user is done, provide a summary:
|
|
421
|
+
- Total sessions imported in this session
|
|
422
|
+
- Sessions that were updated
|
|
423
|
+
- Any errors encountered
|
|
424
|
+
- Next steps: suggest `aline search` to explore their imported history
|
|
425
|
+
|
|
426
|
+
## Command Reference
|
|
427
|
+
|
|
428
|
+
| Command | Purpose |
|
|
429
|
+
|---------|---------|
|
|
430
|
+
| `aline watcher session list` | List all discovered sessions with status |
|
|
431
|
+
| `aline watcher session list --detect-turns` | Include turn counts in listing |
|
|
432
|
+
| `aline watcher session list -p N -n M` | Paginate: page N with M items per page |
|
|
433
|
+
| `aline watcher session import <session_id>` | Import by session ID (recommended) |
|
|
434
|
+
| `aline watcher session import <id1>,<id2>` | Import multiple sessions by ID |
|
|
435
|
+
| `aline watcher session import <id> -f` | Force re-import |
|
|
436
|
+
| `aline watcher session import <id> -r` | Regenerate LLM summaries |
|
|
437
|
+
| `aline watcher session import <id> --sync` | Synchronous import (wait for completion) |
|
|
438
|
+
| `aline watcher session show <session_id>` | View details of a specific session |
|
|
439
|
+
|
|
440
|
+
## Important: Use Session IDs, Not Index Numbers
|
|
441
|
+
|
|
442
|
+
**Always use session_id (UUID) for import operations.**
|
|
443
|
+
|
|
444
|
+
Why:
|
|
445
|
+
- Index numbers are assigned dynamically and can change between list commands
|
|
446
|
+
- Using wrong index could import unintended sessions
|
|
447
|
+
- Session IDs are stable and unique identifiers
|
|
448
|
+
|
|
449
|
+
Example session ID format: `e58f67bf-ebba-47bd-9371-5ef9e06697d3`
|
|
450
|
+
|
|
451
|
+
You can use UUID prefix for convenience: `e58f67bf` (first 8 characters)
|
|
452
|
+
|
|
453
|
+
## Error Handling
|
|
454
|
+
|
|
455
|
+
- **No sessions found**: Check if Claude Code history exists at `~/.claude/projects/`. Suggest running some Claude Code sessions first.
|
|
456
|
+
- **Import fails**: Check disk space, database permissions at `~/.aline/db/aline.db`
|
|
457
|
+
- **Partial import**: Some sessions may fail due to corrupted JSONL files. Report specific errors and suggest `--force` retry.
|
|
458
|
+
- **Watcher not running**: If async import doesn't complete, suggest `aline watcher start` or use `--sync` flag.
|
|
459
|
+
|
|
460
|
+
## Tips for Large Imports
|
|
461
|
+
|
|
462
|
+
- For many sessions (50+), import in batches to track progress
|
|
463
|
+
- Use `--sync` flag to see real-time progress for smaller batches
|
|
464
|
+
- Check `~/.aline/.logs/watcher.log` for detailed import logs
|
|
465
|
+
|
|
466
|
+
## When to Use This Skill
|
|
467
|
+
|
|
468
|
+
Use this skill when:
|
|
469
|
+
- User is setting up Aline for the first time
|
|
470
|
+
- User wants to import historical Claude Code sessions
|
|
471
|
+
- User asks "how do I import my history?" or similar
|
|
472
|
+
- User wants to selectively import specific project sessions
|
|
473
|
+
- User needs to re-import or update existing sessions
|
|
474
|
+
"""
|
|
475
|
+
|
|
254
476
|
# Registry of all skills to install
|
|
255
477
|
SKILLS_REGISTRY: dict[str, str] = {
|
|
256
478
|
"aline": ALINE_SKILL_MD,
|
|
257
479
|
"aline-share": ALINE_SHARE_SKILL_MD,
|
|
480
|
+
"aline-import-history-sessions": ALINE_IMPORT_HISTORY_SESSIONS_SKILL_MD,
|
|
258
481
|
}
|
|
259
482
|
|
|
260
483
|
|
|
@@ -293,32 +516,49 @@ def add_tmux_command() -> int:
|
|
|
293
516
|
_source_aline_tmux_conf(tmux_conf)
|
|
294
517
|
|
|
295
518
|
console.print(f"[green]✓[/green] tmux installed and config ready: [cyan]{tmux_conf}[/cyan]")
|
|
296
|
-
console.print(
|
|
519
|
+
console.print(
|
|
520
|
+
"[dim]Tip: in the Aline dashboard tmux session, mouse drag will copy to clipboard.[/dim]"
|
|
521
|
+
)
|
|
297
522
|
return 0
|
|
298
523
|
|
|
299
524
|
|
|
300
|
-
def
|
|
301
|
-
"""
|
|
525
|
+
def _ensure_symlink(target_link: Path, source_file: Path, force: bool = False) -> bool:
|
|
526
|
+
"""Create a symlink at target_link pointing to source_file.
|
|
302
527
|
|
|
303
528
|
Args:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
529
|
+
target_link: The path where the symlink should be created (e.g., ~/.claude/skills/aline/SKILL.md)
|
|
530
|
+
source_file: The actual file to link to (e.g., ~/.aline/skills/aline/SKILL.md)
|
|
531
|
+
force: Whether to overwrite existing files/links
|
|
307
532
|
|
|
308
533
|
Returns:
|
|
309
|
-
|
|
534
|
+
True if a new link was created or updated, False if skipped (already exists)
|
|
310
535
|
"""
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
536
|
+
if target_link.exists() or target_link.is_symlink():
|
|
537
|
+
if not force:
|
|
538
|
+
# Check if it already points to the right place
|
|
539
|
+
try:
|
|
540
|
+
if target_link.is_symlink() and target_link.resolve() == source_file.resolve():
|
|
541
|
+
return False # Already correct
|
|
542
|
+
except Exception:
|
|
543
|
+
pass
|
|
544
|
+
return False # Exists and not forced
|
|
545
|
+
|
|
546
|
+
# Force: remove existing
|
|
547
|
+
if target_link.is_dir() and not target_link.is_symlink():
|
|
548
|
+
shutil.rmtree(target_link)
|
|
549
|
+
else:
|
|
550
|
+
target_link.unlink()
|
|
551
|
+
|
|
552
|
+
target_link.parent.mkdir(parents=True, exist_ok=True)
|
|
553
|
+
target_link.symlink_to(source_file)
|
|
554
|
+
return True
|
|
316
555
|
|
|
317
556
|
|
|
318
557
|
def add_skills_command(force: bool = False) -> int:
|
|
319
|
-
"""Install Aline skills for Claude Code.
|
|
558
|
+
"""Install Aline skills for Claude Code and Codex.
|
|
320
559
|
|
|
321
|
-
|
|
560
|
+
1. Writes built-in skills to ~/.aline/skills/
|
|
561
|
+
2. Creates symlinks in ~/.claude/skills/ and ~/.codex/skills/
|
|
322
562
|
|
|
323
563
|
Args:
|
|
324
564
|
force: Overwrite existing skills if they exist
|
|
@@ -326,42 +566,128 @@ def add_skills_command(force: bool = False) -> int:
|
|
|
326
566
|
Returns:
|
|
327
567
|
Exit code (0 for success, 1 for failure)
|
|
328
568
|
"""
|
|
329
|
-
|
|
569
|
+
aline_skill_root = Path.home() / ".aline" / "skills"
|
|
570
|
+
targets = [
|
|
571
|
+
("Claude", Path.home() / ".claude" / "skills"),
|
|
572
|
+
("Codex", Path.home() / ".codex" / "skills"),
|
|
573
|
+
("OpenCode", Path.home() / ".config" / "opencode" / "skill"),
|
|
574
|
+
]
|
|
575
|
+
|
|
330
576
|
installed_skills: list[str] = []
|
|
331
577
|
skipped_skills: list[str] = []
|
|
332
578
|
failed_skills: list[tuple[str, str]] = []
|
|
333
579
|
|
|
334
580
|
for skill_name, skill_content in SKILLS_REGISTRY.items():
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
# Check if skill already exists
|
|
338
|
-
if skill_path.exists() and not force:
|
|
339
|
-
skipped_skills.append(skill_name)
|
|
340
|
-
continue
|
|
341
|
-
|
|
581
|
+
# 1. Update master copy in ~/.aline/skills
|
|
582
|
+
master_path = aline_skill_root / skill_name / "SKILL.md"
|
|
342
583
|
try:
|
|
343
|
-
|
|
344
|
-
|
|
584
|
+
master_path.parent.mkdir(parents=True, exist_ok=True)
|
|
585
|
+
master_path.write_text(skill_content, encoding="utf-8")
|
|
345
586
|
except Exception as e:
|
|
346
|
-
failed_skills.append((skill_name, str(e)))
|
|
587
|
+
failed_skills.append((f"{skill_name} (storage)", str(e)))
|
|
588
|
+
continue
|
|
589
|
+
|
|
590
|
+
# 2. Link to targets
|
|
591
|
+
for tool_name, tool_root in targets:
|
|
592
|
+
dest_path = tool_root / skill_name / "SKILL.md"
|
|
593
|
+
|
|
594
|
+
try:
|
|
595
|
+
updated = _ensure_symlink(dest_path, master_path, force)
|
|
596
|
+
if updated:
|
|
597
|
+
installed_skills.append(f"{tool_name}/{skill_name}")
|
|
598
|
+
else:
|
|
599
|
+
skipped_skills.append(f"{tool_name}/{skill_name}")
|
|
600
|
+
except Exception as e:
|
|
601
|
+
failed_skills.append((f"{tool_name}/{skill_name}", str(e)))
|
|
347
602
|
|
|
348
603
|
# Report results
|
|
349
|
-
for
|
|
350
|
-
|
|
351
|
-
console.print(f"[green]✓[/green] Installed: [cyan]{skill_path}[/cyan]")
|
|
604
|
+
for item in installed_skills:
|
|
605
|
+
console.print(f"[green]✓[/green] Installed: [cyan]{item}[/cyan]")
|
|
352
606
|
|
|
353
|
-
for
|
|
354
|
-
|
|
355
|
-
console.print(f"[yellow]⊘[/yellow] Already exists: [dim]{skill_path}[/dim]")
|
|
607
|
+
for item in skipped_skills:
|
|
608
|
+
console.print(f"[yellow]⊘[/yellow] Already exists: [dim]{item}[/dim]")
|
|
356
609
|
|
|
357
|
-
for
|
|
358
|
-
console.print(f"[red]✗[/red] Failed to install {
|
|
610
|
+
for item, error in failed_skills:
|
|
611
|
+
console.print(f"[red]✗[/red] Failed to install {item}: {error}")
|
|
359
612
|
|
|
360
613
|
if skipped_skills and not installed_skills:
|
|
361
614
|
console.print("[dim]Use --force to overwrite existing skills[/dim]")
|
|
362
615
|
elif installed_skills:
|
|
363
|
-
|
|
364
|
-
console.print(f"[dim]Restart Claude Code to activate: {skill_names}[/dim]")
|
|
616
|
+
console.print("[dim]Restart your AI tools to activate new skills[/dim]")
|
|
365
617
|
|
|
366
618
|
return 1 if failed_skills else 0
|
|
367
619
|
|
|
620
|
+
|
|
621
|
+
def add_skills_dev_command(force: bool = False) -> int:
|
|
622
|
+
"""Install developer skills from skill-dev/ directory.
|
|
623
|
+
|
|
624
|
+
Symlinks skills from ./skill-dev/ to ~/.claude/skills/ and ~/.codex/skills/
|
|
625
|
+
|
|
626
|
+
Args:
|
|
627
|
+
force: Overwrite existing skills if they exist
|
|
628
|
+
|
|
629
|
+
Returns:
|
|
630
|
+
Exit code (0 for success, 1 for failure)
|
|
631
|
+
"""
|
|
632
|
+
# Find skill-dev directory relative to this file's package location
|
|
633
|
+
package_root = Path(__file__).parent.parent.parent.parent
|
|
634
|
+
skill_dev_dir = package_root / "skill-dev"
|
|
635
|
+
|
|
636
|
+
if not skill_dev_dir.exists():
|
|
637
|
+
console.print(f"[red]skill-dev/ directory not found at:[/red] {skill_dev_dir}")
|
|
638
|
+
console.print("[dim]This command is for developer use only.[/dim]")
|
|
639
|
+
return 1
|
|
640
|
+
|
|
641
|
+
targets = [
|
|
642
|
+
("Claude", Path.home() / ".claude" / "skills"),
|
|
643
|
+
("Codex", Path.home() / ".codex" / "skills"),
|
|
644
|
+
("OpenCode", Path.home() / ".config" / "opencode" / "skill"),
|
|
645
|
+
]
|
|
646
|
+
|
|
647
|
+
installed_skills: list[str] = []
|
|
648
|
+
skipped_skills: list[str] = []
|
|
649
|
+
failed_skills: list[tuple[str, str]] = []
|
|
650
|
+
|
|
651
|
+
# Scan skill-dev for directories containing SKILL.md
|
|
652
|
+
for skill_dir in skill_dev_dir.iterdir():
|
|
653
|
+
if not skill_dir.is_dir():
|
|
654
|
+
continue
|
|
655
|
+
|
|
656
|
+
skill_file = skill_dir / "SKILL.md"
|
|
657
|
+
if not skill_file.exists():
|
|
658
|
+
continue
|
|
659
|
+
|
|
660
|
+
skill_name = skill_dir.name
|
|
661
|
+
|
|
662
|
+
for tool_name, tool_root in targets:
|
|
663
|
+
dest_path = tool_root / skill_name / "SKILL.md"
|
|
664
|
+
|
|
665
|
+
try:
|
|
666
|
+
updated = _ensure_symlink(dest_path, skill_file, force)
|
|
667
|
+
if updated:
|
|
668
|
+
installed_skills.append(f"{tool_name}/{skill_name}")
|
|
669
|
+
else:
|
|
670
|
+
skipped_skills.append(f"{tool_name}/{skill_name}")
|
|
671
|
+
except Exception as e:
|
|
672
|
+
failed_skills.append((f"{tool_name}/{skill_name}", str(e)))
|
|
673
|
+
|
|
674
|
+
if not installed_skills and not skipped_skills and not failed_skills:
|
|
675
|
+
console.print("[yellow]No skills found in skill-dev/[/yellow]")
|
|
676
|
+
return 0
|
|
677
|
+
|
|
678
|
+
# Report results
|
|
679
|
+
for item in installed_skills:
|
|
680
|
+
console.print(f"[green]✓[/green] Linked: [cyan]{item}[/cyan]")
|
|
681
|
+
|
|
682
|
+
for item in skipped_skills:
|
|
683
|
+
console.print(f"[yellow]⊘[/yellow] Already exists: [dim]{item}[/dim]")
|
|
684
|
+
|
|
685
|
+
for item, error in failed_skills:
|
|
686
|
+
console.print(f"[red]✗[/red] Failed to install {item}: {error}")
|
|
687
|
+
|
|
688
|
+
if skipped_skills and not installed_skills:
|
|
689
|
+
console.print("[dim]Use --force to overwrite existing skills[/dim]")
|
|
690
|
+
elif installed_skills:
|
|
691
|
+
console.print("[dim]Restart your AI tools to activate new skills[/dim]")
|
|
692
|
+
|
|
693
|
+
return 1 if failed_skills else 0
|
realign/commands/config.py
CHANGED
|
@@ -13,18 +13,9 @@ console = Console()
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def config_command(
|
|
16
|
-
action: str = typer.Argument(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
),
|
|
20
|
-
key: Optional[str] = typer.Argument(
|
|
21
|
-
None,
|
|
22
|
-
help="Configuration key (for get/set operations)"
|
|
23
|
-
),
|
|
24
|
-
value: Optional[str] = typer.Argument(
|
|
25
|
-
None,
|
|
26
|
-
help="Configuration value (for set operation)"
|
|
27
|
-
),
|
|
16
|
+
action: str = typer.Argument(..., help="Action to perform: 'get', 'set', or 'init'"),
|
|
17
|
+
key: Optional[str] = typer.Argument(None, help="Configuration key (for get/set operations)"),
|
|
18
|
+
value: Optional[str] = typer.Argument(None, help="Configuration value (for set operation)"),
|
|
28
19
|
):
|
|
29
20
|
"""
|
|
30
21
|
Manage ReAlign configuration.
|
realign/commands/context.py
CHANGED
|
@@ -146,7 +146,9 @@ def context_show_command(
|
|
|
146
146
|
console.print("[dim]No active context.[/dim]")
|
|
147
147
|
env_val = os.environ.get(CONTEXT_ID_ENV_VAR)
|
|
148
148
|
if env_val:
|
|
149
|
-
console.print(
|
|
149
|
+
console.print(
|
|
150
|
+
f"[dim]ALINE_CONTEXT_ID is set to '{env_val}' but no matching context found.[/dim]"
|
|
151
|
+
)
|
|
150
152
|
console.print("[dim]Searches will include all sessions/events.[/dim]")
|
|
151
153
|
return 0
|
|
152
154
|
|