@yottameta/yotta-dev-mcp 0.2.0 → 0.2.1

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.
@@ -30,9 +30,14 @@ def _resolve_python_import(source_rel, node):
30
30
  if node.level > 1:
31
31
  parts = parts[:-(node.level - 1)] if len(parts) >= node.level - 1 else []
32
32
  prefix = "/".join(parts)
33
+ if not node.module:
34
+ return [
35
+ (prefix + "/" + alias.name.replace(".", "/")).strip("/")
36
+ for alias in node.names
37
+ ]
33
38
  module = node.module or ""
34
39
  target = (prefix + "/" + module.replace(".", "/")).strip("/")
35
- return [target + ".py" if target else source_rel]
40
+ return [target] if target else [source_rel]
36
41
  if node.module:
37
42
  return [node.module]
38
43
  return []
@@ -47,7 +52,12 @@ def _repo_map_python(path, rel):
47
52
  for node in ast.walk(tree):
48
53
  if isinstance(node, (ast.Import, ast.ImportFrom)):
49
54
  for target in _resolve_python_import(rel, node):
50
- imports.append({"source": rel, "target": target, "line": getattr(node, "lineno", 1)})
55
+ imports.append({
56
+ "source": rel,
57
+ "target": target,
58
+ "line": getattr(node, "lineno", 1),
59
+ "relative": isinstance(node, ast.ImportFrom) and bool(node.level),
60
+ })
51
61
  return imports
52
62
 
53
63
  def _repo_map_js(path, rel):
@@ -77,7 +87,7 @@ def _resolve_relative_module(raw_target, source_rel, module_set, root=None):
77
87
  return base, "file"
78
88
  return None, None
79
89
 
80
- def _classify_python_import(raw_target, source_rel, module_set):
90
+ def _classify_python_import(raw_target, source_rel, module_set, relative=False):
81
91
  if raw_target.endswith(".py"):
82
92
  if raw_target in module_set:
83
93
  return "internal", raw_target
@@ -91,7 +101,7 @@ def _classify_python_import(raw_target, source_rel, module_set):
91
101
  for candidate in (base + ".py", base + "/__init__.py"):
92
102
  if candidate in module_set:
93
103
  return "internal", candidate
94
- return "external", raw_target
104
+ return ("unresolved" if relative else "external"), raw_target
95
105
 
96
106
  def _classify_js_import(raw_target, source_rel, module_set, root=None):
97
107
  if raw_target.startswith("."):
@@ -265,19 +275,21 @@ def system_model(path, max_files=2000, contract_file=None):
265
275
  suffix = file_path.suffix.lower()
266
276
  if suffix == ".py":
267
277
  raw_imports = _repo_map_python(file_path, rel)
268
- classifier = lambda raw: _classify_python_import(raw, rel, module_set) # noqa: E731
269
278
  elif suffix in JS_EXTS:
270
279
  raw_imports = _repo_map_js(file_path, rel)
271
- classifier = lambda raw: _classify_js_import(raw, rel, module_set, root) # noqa: E731
272
280
  else:
273
281
  raw_imports = []
274
- classifier = None
275
282
  module_imports = []
276
283
  for raw in raw_imports:
277
284
  target_raw = raw["target"]
278
285
  if target_raw == rel:
279
286
  continue
280
- kind, target = classifier(target_raw)
287
+ if suffix == ".py":
288
+ kind, target = _classify_python_import(
289
+ target_raw, rel, module_set, relative=raw.get("relative", False)
290
+ )
291
+ else:
292
+ kind, target = _classify_js_import(target_raw, rel, module_set, root)
281
293
  entry = {
282
294
  "source": rel,
283
295
  "target": target,
@@ -15,7 +15,8 @@ from pathlib import Path
15
15
 
16
16
  from dev_architecture import architecture_review
17
17
  from dev_common import (
18
- EVIDENCE_LIMIT, VERIFY_REQUIRED_INSTALLED_FILES,
18
+ EVIDENCE_LIMIT, LEAN_INSTALL_MARKERS, VERIFY_REQUIRED_INSTALLED_ASSETS,
19
+ VERIFY_REQUIRED_INSTALLED_FILES,
19
20
  VERIFY_REQUIRED_SOURCE_FILES, VERIFY_WRITE_GATES, _frontmatter_version,
20
21
  _read_text,
21
22
  )
@@ -44,6 +45,9 @@ def _function_default(source_text, function_name, parameter_name):
44
45
  def _string_constant(node):
45
46
  return isinstance(node, ast.Constant) and isinstance(node.value, str)
46
47
 
48
+ def _is_lean_install(root):
49
+ return any((root / marker).is_file() for marker in LEAN_INSTALL_MARKERS)
50
+
47
51
  def _protocol_tool_contracts(source_text):
48
52
  tree = ast.parse(source_text)
49
53
  function = None
@@ -380,8 +384,14 @@ def self_test(path, mode="auto", allow_execute=False, timeout=120):
380
384
  else:
381
385
  mode = "source"
382
386
 
383
- required = (VERIFY_REQUIRED_SOURCE_FILES if mode == "source"
384
- else VERIFY_REQUIRED_INSTALLED_FILES)
387
+ if mode == "source":
388
+ required = list(VERIFY_REQUIRED_SOURCE_FILES)
389
+ else:
390
+ required = list(VERIFY_REQUIRED_INSTALLED_FILES)
391
+ # Lean distribution strips assets/ by platform policy; only require
392
+ # the banner when the copy is not lean or it already ships assets/.
393
+ if not _is_lean_install(root) or (root / "assets").is_dir():
394
+ required.extend(VERIFY_REQUIRED_INSTALLED_ASSETS)
385
395
  missing = [rel for rel in required if not (root / rel).is_file()]
386
396
  files_check = {
387
397
  "id": "files",
@@ -386,14 +386,24 @@ def mcp_tools():
386
386
  {
387
387
  "name": "mcp_doctor",
388
388
  "description": (
389
- "Inspect installed skills and MCP JSON configuration files for "
390
- "versions and obvious configuration issues. Read-only."
389
+ "Broad, read-only health check for installed skills and MCP client "
390
+ "configuration. Discovers Codex, Cursor, WorkBuddy, OpenCode, Claude "
391
+ "Code and other known hosts (plus project-level configs), parses JSON, "
392
+ "JSONC and a narrow TOML subset, and always returns a coverage report "
393
+ "with checked / missing / unsupported / unverified hosts. Only server "
394
+ "names are returned; commands, args and env values are never included. "
395
+ "A clean result applies only to the covered scope: read coverage and "
396
+ "coverage_confidence before treating it as all-clear."
391
397
  ),
392
398
  "inputSchema": {
393
399
  "type": "object",
394
400
  "properties": {
395
401
  "skills_dirs": {"type": "array", "items": {"type": "string"}},
396
402
  "config_paths": {"type": "array", "items": {"type": "string"}},
403
+ "include_defaults": {
404
+ "type": "boolean",
405
+ "description": "Also scan the built-in host registry when config_paths is supplied",
406
+ },
397
407
  },
398
408
  "additionalProperties": False,
399
409
  },
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/YottaMeta/yotta-dev-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.0",
9
+ "version": "0.2.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@yottameta/yotta-dev-mcp",
14
- "version": "0.2.0",
14
+ "version": "0.2.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }