xrefkit 0.4.9__tar.gz → 0.4.10__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.
Files changed (117) hide show
  1. {xrefkit-0.4.9/xrefkit.egg-info → xrefkit-0.4.10}/PKG-INFO +1 -1
  2. {xrefkit-0.4.9 → xrefkit-0.4.10}/pyproject.toml +1 -1
  3. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_cli.py +57 -0
  4. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/__init__.py +1 -1
  5. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/import_skill.py +55 -22
  6. {xrefkit-0.4.9 → xrefkit-0.4.10/xrefkit.egg-info}/PKG-INFO +1 -1
  7. {xrefkit-0.4.9 → xrefkit-0.4.10}/LICENSE +0 -0
  8. {xrefkit-0.4.9 → xrefkit-0.4.10}/README.md +0 -0
  9. {xrefkit-0.4.9 → xrefkit-0.4.10}/setup.cfg +0 -0
  10. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_base_sync_ownership.py +0 -0
  11. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_boundary_analysis.py +0 -0
  12. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_brownfield_file_editing_protocol.py +0 -0
  13. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_calibration_lint.py +0 -0
  14. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_check_skill_knowledge_xids.py +0 -0
  15. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_collect_analyzer_sarif.py +0 -0
  16. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_convert_to_xrefkit_skill.py +0 -0
  17. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_cs_scope_probe.py +0 -0
  18. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_csharp_commonality.py +0 -0
  19. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_csharp_naming_profile.py +0 -0
  20. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_ctx.py +0 -0
  21. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_cutover_readiness.py +0 -0
  22. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_dashboard.py +0 -0
  23. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_error_policy_audit.py +0 -0
  24. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_error_policy_locator.py +0 -0
  25. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_fm_multiroot.py +0 -0
  26. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_gate.py +0 -0
  27. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_goal_desired_state.py +0 -0
  28. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_host_precheck.py +0 -0
  29. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_human_evaluation.py +0 -0
  30. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_instruction_workflow.py +0 -0
  31. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_knowledge_relations_validator.py +0 -0
  32. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_mcp_setup.py +0 -0
  33. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_ownership.py +0 -0
  34. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_packmeta.py +0 -0
  35. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_project_quality_baseline.py +0 -0
  36. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_resource_provider.py +0 -0
  37. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_runtime_contracts.py +0 -0
  38. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_sarif_to_locator.py +0 -0
  39. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_skill_runtime_audit.py +0 -0
  40. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_skillmeta.py +0 -0
  41. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_skills_sync.py +0 -0
  42. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_structure_catalog.py +0 -0
  43. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xref.py +0 -0
  44. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xrefkit_instance.py +0 -0
  45. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xrefkit_tools.py +0 -0
  46. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xrefkit_v2_discovery.py +0 -0
  47. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xrefkit_v2_models.py +0 -0
  48. {xrefkit-0.4.9 → xrefkit-0.4.10}/tests/test_xrefkit_v2_pipeline.py +0 -0
  49. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/__main__.py +0 -0
  50. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/boundary_analysis.py +0 -0
  51. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/catalog_cli.py +0 -0
  52. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/cli.py +0 -0
  53. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/contracts.py +0 -0
  54. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/ctx.py +0 -0
  55. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/dashboard.py +0 -0
  56. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/discovery.py +0 -0
  57. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/gate.py +0 -0
  58. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/goalstate.py +0 -0
  59. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/hashing.py +0 -0
  60. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/host_precheck.py +0 -0
  61. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/instance.py +0 -0
  62. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/loaders.py +0 -0
  63. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/__init__.py +0 -0
  64. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/audit.py +0 -0
  65. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/bootstrap.py +0 -0
  66. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/catalog.py +0 -0
  67. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/cli.py +0 -0
  68. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/client_cache.py +0 -0
  69. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/client_flow.py +0 -0
  70. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/context_registry.py +0 -0
  71. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/context_token.py +0 -0
  72. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/contracts.py +0 -0
  73. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/dist.py +0 -0
  74. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/ownership.py +0 -0
  75. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/repository.py +0 -0
  76. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/schemas.py +0 -0
  77. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/server.py +0 -0
  78. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/setup.py +0 -0
  79. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp/startup_contract_pack.py +0 -0
  80. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/mcp_tools.py +0 -0
  81. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/__init__.py +0 -0
  82. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/common.py +0 -0
  83. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/effective_bundle.py +0 -0
  84. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/human_evaluation.py +0 -0
  85. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/local_manifest.py +0 -0
  86. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/package_manifest.py +0 -0
  87. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/run_log.py +0 -0
  88. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/server_config.py +0 -0
  89. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/models/skill_definition.py +0 -0
  90. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/operations_cli.py +0 -0
  91. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/ownership.py +0 -0
  92. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/packmeta.py +0 -0
  93. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/registry.py +0 -0
  94. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resolver.py +0 -0
  95. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resource_provider.py +0 -0
  96. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/contracts.json +0 -0
  97. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/current.json +0 -0
  98. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
  99. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
  100. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
  101. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
  102. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/resources/base/model_body.md +0 -0
  103. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/runlog.py +0 -0
  104. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/skillmeta.py +0 -0
  105. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/skillrun.py +0 -0
  106. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/skills_sync.py +0 -0
  107. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/structure_catalog.py +0 -0
  108. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/tools/__init__.py +0 -0
  109. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/tools/__main__.py +0 -0
  110. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/v2_cli.py +0 -0
  111. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/workspace.py +0 -0
  112. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit/xref.py +0 -0
  113. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit.egg-info/SOURCES.txt +0 -0
  114. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit.egg-info/dependency_links.txt +0 -0
  115. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit.egg-info/entry_points.txt +0 -0
  116. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit.egg-info/requires.txt +0 -0
  117. {xrefkit-0.4.9 → xrefkit-0.4.10}/xrefkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrefkit
3
- Version: 0.4.9
3
+ Version: 0.4.10
4
4
  Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
5
5
  Author: synthaicode
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrefkit"
7
- version = "0.4.9"
7
+ version = "0.4.10"
8
8
  description = "Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1464,6 +1464,63 @@ class CliTests(unittest.TestCase):
1464
1464
  self.assertIn("## Closure Gate\n\n- status: `done`", text)
1465
1465
  self.assertIn("`closure` -> `done` role=`closure_gate`: closure gate accepted completed run", text)
1466
1466
 
1467
+ def test_skill_import_invalid_batch_input_returns_safe_json_error(self) -> None:
1468
+ with tempfile.TemporaryDirectory() as tmp:
1469
+ root = Path(tmp) / "repo"
1470
+ source = Path(tmp) / "not-a-batch"
1471
+ source.mkdir()
1472
+
1473
+ stdout = io.StringIO()
1474
+ stderr = io.StringIO()
1475
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
1476
+ exit_code = main(
1477
+ [
1478
+ "skill",
1479
+ "import",
1480
+ str(source),
1481
+ "--root",
1482
+ str(root),
1483
+ "--batch",
1484
+ "--json",
1485
+ ]
1486
+ )
1487
+
1488
+ payload = json.loads(stdout.getvalue())
1489
+ self.assertEqual(2, exit_code)
1490
+ self.assertFalse(payload["ok"])
1491
+ self.assertEqual("invalid_input", payload["error"]["code"])
1492
+ self.assertEqual("batch source root must contain a skills/ directory", payload["error"]["message"])
1493
+ self.assertNotIn(str(root), stdout.getvalue())
1494
+ self.assertNotIn(str(source), stdout.getvalue())
1495
+ self.assertEqual("", stderr.getvalue())
1496
+
1497
+ def test_skill_import_missing_document_returns_safe_text_error(self) -> None:
1498
+ with tempfile.TemporaryDirectory() as tmp:
1499
+ root = Path(tmp) / "repo"
1500
+ source = Path(tmp) / "empty-skill"
1501
+ source.mkdir()
1502
+
1503
+ stdout = io.StringIO()
1504
+ stderr = io.StringIO()
1505
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
1506
+ exit_code = main(
1507
+ [
1508
+ "skill",
1509
+ "import",
1510
+ str(source),
1511
+ "--root",
1512
+ str(root),
1513
+ "--skill-id",
1514
+ "legacy_review",
1515
+ ]
1516
+ )
1517
+
1518
+ self.assertEqual(2, exit_code)
1519
+ self.assertEqual("", stdout.getvalue())
1520
+ self.assertIn("could not find SKILL.md or README.md in the source Skill directory", stderr.getvalue())
1521
+ self.assertNotIn(str(root), stderr.getvalue())
1522
+ self.assertNotIn(str(source), stderr.getvalue())
1523
+
1467
1524
 
1468
1525
  if __name__ == "__main__":
1469
1526
  unittest.main()
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.4.9"
5
+ __version__ = "0.4.10"
@@ -412,29 +412,62 @@ def _print_result(result: ConversionResult | BatchConversionResult, *, as_json:
412
412
  return 0 if result.ok else 1
413
413
 
414
414
 
415
- def cmd_skill_import(args: argparse.Namespace) -> int:
416
- repo_root = Path(args.root)
417
- if args.batch:
418
- result = convert_skill_tree(
419
- source_root=Path(args.source_dir),
420
- repo_root=repo_root,
421
- skill_id_prefix=args.skill_id_prefix,
422
- target_skill_root=Path(args.target_skill_root) if args.target_skill_root else None,
423
- target_knowledge_dir=Path(args.target_knowledge_dir) if args.target_knowledge_dir else None,
424
- dry_run=bool(args.dry_run),
425
- )
415
+ def _print_import_error(message: str, *, as_json: bool) -> int:
416
+ """Render expected import-input failures without exposing implementation details."""
417
+ if as_json:
418
+ print(json.dumps({"ok": False, "error": {"code": "invalid_input", "message": message}}, ensure_ascii=False))
426
419
  else:
427
- if not args.skill_id:
428
- raise SystemExit("--skill-id is required unless --batch is used")
429
- result = convert_skill(
430
- source_dir=Path(args.source_dir),
431
- repo_root=repo_root,
432
- skill_id=args.skill_id,
433
- target_skill_dir=Path(args.target_skill_dir) if args.target_skill_dir else None,
434
- target_knowledge_dir=Path(args.target_knowledge_dir) if args.target_knowledge_dir else None,
435
- source_skill_doc=args.source_skill_doc,
436
- dry_run=bool(args.dry_run),
437
- )
420
+ print(f"xrefkit skill import: error: {message}", file=sys.stderr)
421
+ return 2
422
+
423
+
424
+ def _import_error_message(exc: Exception, *, batch: bool) -> str:
425
+ if isinstance(exc, FileNotFoundError):
426
+ detail = str(exc)
427
+ if batch and "batch source root must contain skills/:" in detail:
428
+ return "batch source root must contain a skills/ directory"
429
+ if "skill doc not found:" in detail:
430
+ return "could not find SKILL.md or README.md in the source Skill directory"
431
+ if "source skill directory not found:" in detail:
432
+ return "source Skill directory was not found"
433
+ if not batch:
434
+ return "could not find SKILL.md or README.md in the source Skill directory"
435
+ return "required source file was not found"
436
+ if isinstance(exc, NotADirectoryError):
437
+ return "source path must be a directory"
438
+ if isinstance(exc, PermissionError):
439
+ return "source or target path is not accessible"
440
+ if isinstance(exc, UnicodeError):
441
+ return "source file is not valid UTF-8"
442
+ return "invalid Skill import input"
443
+
444
+
445
+ def cmd_skill_import(args: argparse.Namespace) -> int:
446
+ try:
447
+ repo_root = Path(args.root)
448
+ if args.batch:
449
+ result = convert_skill_tree(
450
+ source_root=Path(args.source_dir),
451
+ repo_root=repo_root,
452
+ skill_id_prefix=args.skill_id_prefix,
453
+ target_skill_root=Path(args.target_skill_root) if args.target_skill_root else None,
454
+ target_knowledge_dir=Path(args.target_knowledge_dir) if args.target_knowledge_dir else None,
455
+ dry_run=bool(args.dry_run),
456
+ )
457
+ else:
458
+ if not args.skill_id:
459
+ raise SystemExit("--skill-id is required unless --batch is used")
460
+ result = convert_skill(
461
+ source_dir=Path(args.source_dir),
462
+ repo_root=repo_root,
463
+ skill_id=args.skill_id,
464
+ target_skill_dir=Path(args.target_skill_dir) if args.target_skill_dir else None,
465
+ target_knowledge_dir=Path(args.target_knowledge_dir) if args.target_knowledge_dir else None,
466
+ source_skill_doc=args.source_skill_doc,
467
+ dry_run=bool(args.dry_run),
468
+ )
469
+ except (FileNotFoundError, NotADirectoryError, PermissionError, UnicodeError, ValueError) as exc:
470
+ return _print_import_error(_import_error_message(exc, batch=bool(args.batch)), as_json=bool(args.json))
438
471
  return _print_result(result, as_json=bool(args.json))
439
472
 
440
473
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrefkit
3
- Version: 0.4.9
3
+ Version: 0.4.10
4
4
  Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
5
5
  Author: synthaicode
6
6
  License: MIT License
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
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