nexus-dev-toolkit 3.1.2__tar.gz → 3.1.3__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.
- {nexus_dev_toolkit-3.1.2/nexus_dev_toolkit.egg-info → nexus_dev_toolkit-3.1.3}/PKG-INFO +3 -1
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/README.md +2 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_cli.py +223 -1
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3/nexus_dev_toolkit.egg-info}/PKG-INFO +3 -1
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/pyproject.toml +1 -1
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tests/test_cli.py +73 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/LICENSE +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/SOURCES.txt +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/dependency_links.txt +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/entry_points.txt +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/requires.txt +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/top_level.txt +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_server.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/setup.cfg +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tests/test_server.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/__init__.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/__init__.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/code-reviewer.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/database-reviewer.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/deployment-reviewer.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/monitoring-reviewer.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/agents/performance-reviewer.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/__init__.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/arch_ingest.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/package_resolver.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/project_rules.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/__init__.py +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/apply.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/code-review.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/database-review.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/deployment-review.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/epav.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/evaluate.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/monitoring-review.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/performance-review.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/plan.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/scaffold.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/skills/validate.md +0 -0
- {nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/tools/epav/task_loader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nexus-dev-toolkit
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.3
|
|
4
4
|
Summary: Developer workflow toolkit for Claude Code — Day 0 scaffold + Day 1 EPAV
|
|
5
5
|
Author-email: Ronald dela Cruz <rcdelacruz@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -172,6 +172,8 @@ nexus init . # Claude Code — set up .claude/ + knowledge/
|
|
|
172
172
|
nexus init . --tool opencode # OpenCode — set up .opencode/ + knowledge/ + opencode.json
|
|
173
173
|
nexus --version # show installed version
|
|
174
174
|
nexus update # update to latest version
|
|
175
|
+
nexus sync # sync built-in skills & agents to latest (custom files untouched)
|
|
176
|
+
nexus doctor # validate project setup
|
|
175
177
|
|
|
176
178
|
nexus skill list # list all skills in .claude/commands/
|
|
177
179
|
nexus skill add my-review # create a custom skill
|
|
@@ -151,6 +151,8 @@ nexus init . # Claude Code — set up .claude/ + knowledge/
|
|
|
151
151
|
nexus init . --tool opencode # OpenCode — set up .opencode/ + knowledge/ + opencode.json
|
|
152
152
|
nexus --version # show installed version
|
|
153
153
|
nexus update # update to latest version
|
|
154
|
+
nexus sync # sync built-in skills & agents to latest (custom files untouched)
|
|
155
|
+
nexus doctor # validate project setup
|
|
154
156
|
|
|
155
157
|
nexus skill list # list all skills in .claude/commands/
|
|
156
158
|
nexus skill add my-review # create a custom skill
|
|
@@ -18,7 +18,7 @@ app.add_typer(agent_app, name="agent")
|
|
|
18
18
|
|
|
19
19
|
console = Console()
|
|
20
20
|
|
|
21
|
-
_VERSION = "3.1.
|
|
21
|
+
_VERSION = "3.1.3"
|
|
22
22
|
|
|
23
23
|
_LOGO = """\
|
|
24
24
|
[cyan]███╗ ██╗███████╗██╗ ██╗██╗ ██╗███████╗[/cyan]
|
|
@@ -301,6 +301,11 @@ def _check_and_offer_install(tool: str) -> None:
|
|
|
301
301
|
return
|
|
302
302
|
|
|
303
303
|
console.print(f" [yellow]⚠[/yellow] {info['name']} not found — you need it to use nexus.\n")
|
|
304
|
+
|
|
305
|
+
if not sys.stdin.isatty():
|
|
306
|
+
console.print(f" [dim]Install manually: {info['install']}[/dim]\n")
|
|
307
|
+
return
|
|
308
|
+
|
|
304
309
|
answer = console.input(f" Install {info['name']} now? [y/N] ").strip().lower()
|
|
305
310
|
if answer != "y":
|
|
306
311
|
console.print(f" [dim]Skipping. Install manually: {info['install']}[/dim]\n")
|
|
@@ -364,6 +369,223 @@ def update() -> None:
|
|
|
364
369
|
console.print("\n [green]✓[/green] Done.\n")
|
|
365
370
|
|
|
366
371
|
|
|
372
|
+
@app.command()
|
|
373
|
+
def sync(
|
|
374
|
+
project_dir: str = typer.Argument(".", help="Project directory to sync"),
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Sync built-in skills and agents to their latest versions (custom files untouched)."""
|
|
377
|
+
root = Path(project_dir).resolve()
|
|
378
|
+
has_claude = (root / ".claude").is_dir()
|
|
379
|
+
has_opencode = (root / ".opencode").is_dir()
|
|
380
|
+
|
|
381
|
+
if not has_claude and not has_opencode:
|
|
382
|
+
console.print(" [red]✗[/red] Not a nexus project. Run [cyan]nexus init[/cyan] first.\n")
|
|
383
|
+
raise typer.Exit(1)
|
|
384
|
+
|
|
385
|
+
console.print(f"\n [cyan]▶[/cyan] Syncing built-ins in [bold]{root}[/bold]\n")
|
|
386
|
+
|
|
387
|
+
table = Table(show_header=True, header_style="dim")
|
|
388
|
+
table.add_column("File")
|
|
389
|
+
table.add_column("Status")
|
|
390
|
+
|
|
391
|
+
if has_claude:
|
|
392
|
+
(root / ".claude" / "commands").mkdir(parents=True, exist_ok=True)
|
|
393
|
+
(root / ".claude" / "agents").mkdir(parents=True, exist_ok=True)
|
|
394
|
+
|
|
395
|
+
for fname in _BUILTIN_SKILLS:
|
|
396
|
+
src = _SKILLS_SRC / fname
|
|
397
|
+
if not src.exists():
|
|
398
|
+
continue
|
|
399
|
+
dst = root / ".claude" / "commands" / fname
|
|
400
|
+
src_text = src.read_text(encoding="utf-8")
|
|
401
|
+
if dst.exists() and dst.read_text(encoding="utf-8") == src_text:
|
|
402
|
+
table.add_row(f".claude/commands/{fname}", "[dim green]ok[/dim green]")
|
|
403
|
+
else:
|
|
404
|
+
dst.write_text(src_text, encoding="utf-8")
|
|
405
|
+
table.add_row(f".claude/commands/{fname}", "[cyan]updated[/cyan]")
|
|
406
|
+
|
|
407
|
+
for fname in _BUILTIN_AGENTS:
|
|
408
|
+
src = _AGENTS_SRC / fname
|
|
409
|
+
if not src.exists():
|
|
410
|
+
continue
|
|
411
|
+
dst = root / ".claude" / "agents" / fname
|
|
412
|
+
src_text = src.read_text(encoding="utf-8")
|
|
413
|
+
if dst.exists() and dst.read_text(encoding="utf-8") == src_text:
|
|
414
|
+
table.add_row(f".claude/agents/{fname}", "[dim green]ok[/dim green]")
|
|
415
|
+
else:
|
|
416
|
+
dst.write_text(src_text, encoding="utf-8")
|
|
417
|
+
table.add_row(f".claude/agents/{fname}", "[cyan]updated[/cyan]")
|
|
418
|
+
|
|
419
|
+
if has_opencode:
|
|
420
|
+
(root / ".opencode" / "commands").mkdir(parents=True, exist_ok=True)
|
|
421
|
+
(root / ".opencode" / "agents").mkdir(parents=True, exist_ok=True)
|
|
422
|
+
(root / ".opencode" / "plugins").mkdir(parents=True, exist_ok=True)
|
|
423
|
+
|
|
424
|
+
for fname in _BUILTIN_SKILLS:
|
|
425
|
+
src = _SKILLS_SRC / fname
|
|
426
|
+
if not src.exists():
|
|
427
|
+
continue
|
|
428
|
+
dst = root / ".opencode" / "commands" / fname
|
|
429
|
+
src_text = src.read_text(encoding="utf-8")
|
|
430
|
+
if dst.exists() and dst.read_text(encoding="utf-8") == src_text:
|
|
431
|
+
table.add_row(f".opencode/commands/{fname}", "[dim green]ok[/dim green]")
|
|
432
|
+
else:
|
|
433
|
+
dst.write_text(src_text, encoding="utf-8")
|
|
434
|
+
table.add_row(f".opencode/commands/{fname}", "[cyan]updated[/cyan]")
|
|
435
|
+
|
|
436
|
+
for fname in _BUILTIN_AGENTS:
|
|
437
|
+
src = _AGENTS_SRC / fname
|
|
438
|
+
if not src.exists():
|
|
439
|
+
continue
|
|
440
|
+
dst = root / ".opencode" / "agents" / fname
|
|
441
|
+
src_text = _strip_claude_frontmatter(src.read_text(encoding="utf-8"))
|
|
442
|
+
if dst.exists() and dst.read_text(encoding="utf-8") == src_text:
|
|
443
|
+
table.add_row(f".opencode/agents/{fname}", "[dim green]ok[/dim green]")
|
|
444
|
+
else:
|
|
445
|
+
dst.write_text(src_text, encoding="utf-8")
|
|
446
|
+
table.add_row(f".opencode/agents/{fname}", "[cyan]updated[/cyan]")
|
|
447
|
+
|
|
448
|
+
plugin_dst = root / ".opencode" / "plugins" / "graphify.js"
|
|
449
|
+
if plugin_dst.exists() and plugin_dst.read_text(encoding="utf-8") == _OPENCODE_GRAPHIFY_PLUGIN:
|
|
450
|
+
table.add_row(".opencode/plugins/graphify.js", "[dim green]ok[/dim green]")
|
|
451
|
+
else:
|
|
452
|
+
plugin_dst.write_text(_OPENCODE_GRAPHIFY_PLUGIN, encoding="utf-8")
|
|
453
|
+
table.add_row(".opencode/plugins/graphify.js", "[cyan]updated[/cyan]")
|
|
454
|
+
|
|
455
|
+
console.print(table)
|
|
456
|
+
console.print()
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@app.command()
|
|
460
|
+
def doctor(
|
|
461
|
+
project_dir: str = typer.Argument(".", help="Project directory to check"),
|
|
462
|
+
) -> None:
|
|
463
|
+
"""Validate the nexus project setup and show what's working or missing."""
|
|
464
|
+
root = Path(project_dir).resolve()
|
|
465
|
+
|
|
466
|
+
table = Table(show_header=True, header_style="dim", show_lines=False)
|
|
467
|
+
table.add_column("Check", style="dim white")
|
|
468
|
+
table.add_column("Status")
|
|
469
|
+
|
|
470
|
+
has_failure = False
|
|
471
|
+
|
|
472
|
+
def ok(msg: str) -> str:
|
|
473
|
+
return f"[green]✓[/green] {msg}"
|
|
474
|
+
|
|
475
|
+
def warn(msg: str) -> str:
|
|
476
|
+
return f"[yellow]⚠[/yellow] {msg}"
|
|
477
|
+
|
|
478
|
+
def fail(msg: str) -> str:
|
|
479
|
+
nonlocal has_failure
|
|
480
|
+
has_failure = True
|
|
481
|
+
return f"[red]✗[/red] {msg}"
|
|
482
|
+
|
|
483
|
+
# ── Tool ─────────────────────────────────────────────────────────────────
|
|
484
|
+
has_claude_bin = bool(shutil.which("claude"))
|
|
485
|
+
has_opencode_bin = bool(shutil.which("opencode"))
|
|
486
|
+
table.add_row("Claude Code", ok("installed") if has_claude_bin else warn("not found — install: npm install -g @anthropic-ai/claude-code"))
|
|
487
|
+
table.add_row("OpenCode", ok("installed") if has_opencode_bin else warn("not found — install: curl -fsSL https://opencode.ai/install | bash"))
|
|
488
|
+
|
|
489
|
+
# ── Project ──────────────────────────────────────────────────────────────
|
|
490
|
+
has_claude_dir = (root / ".claude").is_dir()
|
|
491
|
+
has_opencode_dir = (root / ".opencode").is_dir()
|
|
492
|
+
initialized = has_claude_dir or has_opencode_dir
|
|
493
|
+
table.add_row(
|
|
494
|
+
"nexus initialized",
|
|
495
|
+
ok(f"yes ({', '.join(filter(None, ['claude' if has_claude_dir else '', 'opencode' if has_opencode_dir else '']))})")
|
|
496
|
+
if initialized else fail("no — run nexus init")
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
# MCP config — Claude Code
|
|
500
|
+
if has_claude_dir:
|
|
501
|
+
mcp_path = root / ".mcp.json"
|
|
502
|
+
if not mcp_path.exists():
|
|
503
|
+
table.add_row(".mcp.json", fail("missing"))
|
|
504
|
+
else:
|
|
505
|
+
try:
|
|
506
|
+
mcp_data = json.loads(mcp_path.read_text())
|
|
507
|
+
if "nexus" in mcp_data.get("mcpServers", {}):
|
|
508
|
+
table.add_row(".mcp.json", ok("nexus entry present"))
|
|
509
|
+
else:
|
|
510
|
+
table.add_row(".mcp.json", warn("exists but no nexus entry"))
|
|
511
|
+
except Exception:
|
|
512
|
+
table.add_row(".mcp.json", warn("invalid JSON"))
|
|
513
|
+
|
|
514
|
+
# MCP config — OpenCode
|
|
515
|
+
if has_opencode_dir:
|
|
516
|
+
oc_json = root / "opencode.json"
|
|
517
|
+
if not oc_json.exists():
|
|
518
|
+
table.add_row("opencode.json", fail("missing"))
|
|
519
|
+
else:
|
|
520
|
+
try:
|
|
521
|
+
oc_data = json.loads(oc_json.read_text())
|
|
522
|
+
if "nexus-mcp" in oc_data.get("mcp", {}):
|
|
523
|
+
table.add_row("opencode.json", ok("nexus entry present"))
|
|
524
|
+
else:
|
|
525
|
+
table.add_row("opencode.json", warn("exists but no nexus-mcp entry"))
|
|
526
|
+
except Exception:
|
|
527
|
+
table.add_row("opencode.json", warn("invalid JSON"))
|
|
528
|
+
|
|
529
|
+
# ── Skills & Agents ──────────────────────────────────────────────────────
|
|
530
|
+
if has_claude_dir:
|
|
531
|
+
cmd_dir = root / ".claude" / "commands"
|
|
532
|
+
present = [f for f in _BUILTIN_SKILLS if (cmd_dir / f).exists()]
|
|
533
|
+
missing = len(_BUILTIN_SKILLS) - len(present)
|
|
534
|
+
if missing == 0:
|
|
535
|
+
table.add_row("Built-in skills (Claude)", ok(f"all {len(_BUILTIN_SKILLS)} present"))
|
|
536
|
+
else:
|
|
537
|
+
table.add_row("Built-in skills (Claude)", warn(f"{missing} missing — run nexus sync"))
|
|
538
|
+
|
|
539
|
+
ag_dir = root / ".claude" / "agents"
|
|
540
|
+
present_ag = [f for f in _BUILTIN_AGENTS if (ag_dir / f).exists()]
|
|
541
|
+
missing_ag = len(_BUILTIN_AGENTS) - len(present_ag)
|
|
542
|
+
if missing_ag == 0:
|
|
543
|
+
table.add_row("Built-in agents (Claude)", ok(f"all {len(_BUILTIN_AGENTS)} present"))
|
|
544
|
+
else:
|
|
545
|
+
table.add_row("Built-in agents (Claude)", warn(f"{missing_ag} missing — run nexus sync"))
|
|
546
|
+
|
|
547
|
+
if has_opencode_dir:
|
|
548
|
+
oc_cmd_dir = root / ".opencode" / "commands"
|
|
549
|
+
present_oc = [f for f in _BUILTIN_SKILLS if (oc_cmd_dir / f).exists()]
|
|
550
|
+
missing_oc = len(_BUILTIN_SKILLS) - len(present_oc)
|
|
551
|
+
if missing_oc == 0:
|
|
552
|
+
table.add_row("Built-in skills (OpenCode)", ok(f"all {len(_BUILTIN_SKILLS)} present"))
|
|
553
|
+
else:
|
|
554
|
+
table.add_row("Built-in skills (OpenCode)", warn(f"{missing_oc} missing — run nexus sync"))
|
|
555
|
+
|
|
556
|
+
oc_ag_dir = root / ".opencode" / "agents"
|
|
557
|
+
present_oc_ag = [f for f in _BUILTIN_AGENTS if (oc_ag_dir / f).exists()]
|
|
558
|
+
missing_oc_ag = len(_BUILTIN_AGENTS) - len(present_oc_ag)
|
|
559
|
+
if missing_oc_ag == 0:
|
|
560
|
+
table.add_row("Built-in agents (OpenCode)", ok(f"all {len(_BUILTIN_AGENTS)} present"))
|
|
561
|
+
else:
|
|
562
|
+
table.add_row("Built-in agents (OpenCode)", warn(f"{missing_oc_ag} missing — run nexus sync"))
|
|
563
|
+
|
|
564
|
+
# ── Knowledge ────────────────────────────────────────────────────────────
|
|
565
|
+
missing_dirs = [d for d in _KNOWLEDGE_DIRS if not (root / d).is_dir()]
|
|
566
|
+
if not missing_dirs:
|
|
567
|
+
table.add_row("Knowledge dirs", ok(f"all {len(_KNOWLEDGE_DIRS)} present"))
|
|
568
|
+
else:
|
|
569
|
+
table.add_row("Knowledge dirs", warn(f"missing: {', '.join(missing_dirs)}"))
|
|
570
|
+
|
|
571
|
+
# ── graphify ─────────────────────────────────────────────────────────────
|
|
572
|
+
has_graphify = bool(shutil.which("graphify"))
|
|
573
|
+
table.add_row("graphify", ok("installed") if has_graphify else warn("not installed — uv tool install graphifyy"))
|
|
574
|
+
|
|
575
|
+
graph_path = root / "graphify-out" / "graph.json"
|
|
576
|
+
if graph_path.exists():
|
|
577
|
+
table.add_row("Knowledge graph", ok("graphify-out/graph.json exists"))
|
|
578
|
+
else:
|
|
579
|
+
table.add_row("Knowledge graph", warn("not built — run: graphify ."))
|
|
580
|
+
|
|
581
|
+
console.print()
|
|
582
|
+
console.print(table)
|
|
583
|
+
console.print()
|
|
584
|
+
|
|
585
|
+
if has_failure:
|
|
586
|
+
raise typer.Exit(1)
|
|
587
|
+
|
|
588
|
+
|
|
367
589
|
# ── skill subcommands ─────────────────────────────────────────────────────────
|
|
368
590
|
|
|
369
591
|
_SKILL_TEMPLATE = """\
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nexus-dev-toolkit
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.3
|
|
4
4
|
Summary: Developer workflow toolkit for Claude Code — Day 0 scaffold + Day 1 EPAV
|
|
5
5
|
Author-email: Ronald dela Cruz <rcdelacruz@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -172,6 +172,8 @@ nexus init . # Claude Code — set up .claude/ + knowledge/
|
|
|
172
172
|
nexus init . --tool opencode # OpenCode — set up .opencode/ + knowledge/ + opencode.json
|
|
173
173
|
nexus --version # show installed version
|
|
174
174
|
nexus update # update to latest version
|
|
175
|
+
nexus sync # sync built-in skills & agents to latest (custom files untouched)
|
|
176
|
+
nexus doctor # validate project setup
|
|
175
177
|
|
|
176
178
|
nexus skill list # list all skills in .claude/commands/
|
|
177
179
|
nexus skill add my-review # create a custom skill
|
|
@@ -150,3 +150,76 @@ def test_init_opencode_idempotent(tmp_project):
|
|
|
150
150
|
def test_init_unknown_tool(tmp_project):
|
|
151
151
|
result = runner.invoke(app, ["init", str(tmp_project), "--tool", "cursor"])
|
|
152
152
|
assert result.exit_code == 1
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ── sync ──────────────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
def test_sync_not_nexus_project(tmp_project):
|
|
158
|
+
result = runner.invoke(app, ["sync", str(tmp_project)])
|
|
159
|
+
assert result.exit_code == 1
|
|
160
|
+
assert "Not a nexus project" in result.output
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_sync_shows_ok_when_current(tmp_project):
|
|
164
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
165
|
+
result = runner.invoke(app, ["sync", str(tmp_project)])
|
|
166
|
+
assert result.exit_code == 0
|
|
167
|
+
assert "ok" in result.output
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_sync_updates_modified_builtin(tmp_project):
|
|
171
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
172
|
+
skill_file = tmp_project / ".claude" / "commands" / "scaffold.md"
|
|
173
|
+
skill_file.write_text("# stale content")
|
|
174
|
+
result = runner.invoke(app, ["sync", str(tmp_project)])
|
|
175
|
+
assert result.exit_code == 0
|
|
176
|
+
assert "updated" in result.output
|
|
177
|
+
assert "stale content" not in skill_file.read_text()
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_sync_skips_custom_files(tmp_project):
|
|
181
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
182
|
+
custom = tmp_project / ".claude" / "commands" / "my-custom.md"
|
|
183
|
+
custom.write_text("# custom skill")
|
|
184
|
+
runner.invoke(app, ["sync", str(tmp_project)])
|
|
185
|
+
assert custom.exists()
|
|
186
|
+
assert custom.read_text() == "# custom skill"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_sync_opencode(tmp_project):
|
|
190
|
+
runner.invoke(app, ["init", str(tmp_project), "--tool", "opencode"])
|
|
191
|
+
result = runner.invoke(app, ["sync", str(tmp_project)])
|
|
192
|
+
assert result.exit_code == 0
|
|
193
|
+
assert ".opencode/commands" in result.output
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ── doctor ────────────────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
def test_doctor_not_initialized(tmp_project):
|
|
199
|
+
result = runner.invoke(app, ["doctor", str(tmp_project)])
|
|
200
|
+
assert result.exit_code == 1
|
|
201
|
+
assert "Not a nexus project" in result.output or "✗" in result.output
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_doctor_healthy_project(tmp_project):
|
|
205
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
206
|
+
result = runner.invoke(app, ["doctor", str(tmp_project)])
|
|
207
|
+
assert result.exit_code == 0
|
|
208
|
+
assert "✓" in result.output
|
|
209
|
+
assert "all 11 present" in result.output
|
|
210
|
+
assert "all 5 present" in result.output
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_doctor_missing_skills(tmp_project):
|
|
214
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
215
|
+
(tmp_project / ".claude" / "commands" / "scaffold.md").unlink()
|
|
216
|
+
result = runner.invoke(app, ["doctor", str(tmp_project)])
|
|
217
|
+
assert result.exit_code == 0
|
|
218
|
+
assert "missing" in result.output
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_doctor_missing_graph(tmp_project):
|
|
222
|
+
runner.invoke(app, ["init", str(tmp_project)])
|
|
223
|
+
result = runner.invoke(app, ["doctor", str(tmp_project)])
|
|
224
|
+
assert result.exit_code == 0
|
|
225
|
+
assert "not built" in result.output
|
|
File without changes
|
|
File without changes
|
{nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{nexus_dev_toolkit-3.1.2 → nexus_dev_toolkit-3.1.3}/nexus_dev_toolkit.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|