maxc-cli 0.4.2__tar.gz → 0.4.5__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 (97) hide show
  1. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/PKG-INFO +1 -1
  2. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/setup.py +1 -1
  3. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/__init__.py +1 -1
  4. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/_samples.py +2 -1
  5. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/agent_platforms.py +11 -2
  6. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/app.py +9 -2
  7. maxc_cli-0.4.5/src/maxc_cli/backend/auth.py +164 -0
  8. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/cli.py +51 -9
  9. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/helpers.py +5 -5
  10. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/SKILL.md +20 -24
  11. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -4
  12. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/bootstrap-flow.md +4 -13
  13. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/command-patterns.md +1 -1
  14. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/red-lines.md +3 -3
  15. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/PKG-INFO +1 -1
  16. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/SOURCES.txt +1 -1
  17. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_agent_platforms.py +5 -2
  18. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_agent_skill_commands_context.py +47 -4
  19. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_integration_real.py +2 -2
  20. maxc_cli-0.4.5/tests/test_skill_eval.py +193 -0
  21. maxc_cli-0.4.2/src/maxc_cli/backend/auth.py +0 -175
  22. maxc_cli-0.4.2/src/maxc_cli/skills/references/migrate-from-odpscmd.md +0 -133
  23. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/MANIFEST.in +0 -0
  24. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/README.md +0 -0
  25. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/pyproject.toml +0 -0
  26. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/scripts/pyinstaller_entry.py +0 -0
  27. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/scripts/regression_test.py +0 -0
  28. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/setup.cfg +0 -0
  29. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/__main__.py +0 -0
  30. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/audit.py +0 -0
  31. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/auth_providers.py +0 -0
  32. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/__init__.py +0 -0
  33. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/catalog.py +0 -0
  34. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/data.py +0 -0
  35. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/job.py +0 -0
  36. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/meta.py +0 -0
  37. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/odps.py +0 -0
  38. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/backend/query.py +0 -0
  39. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/cache.py +0 -0
  40. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/catalog_bootstrap.py +0 -0
  41. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/config.py +0 -0
  42. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/exceptions.py +0 -0
  43. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/help_format.py +0 -0
  44. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/masking.py +0 -0
  45. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/models.py +0 -0
  46. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/output.py +0 -0
  47. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/setting_parser.py +0 -0
  48. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/agents/openai.yaml +0 -0
  49. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/json-output-format.md +0 -0
  50. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
  51. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
  52. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/partition-guide.md +0 -0
  53. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/setup-install.md +0 -0
  54. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
  55. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
  56. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
  57. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/store.py +0 -0
  58. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli/utils.py +0 -0
  59. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
  60. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/entry_points.txt +0 -0
  61. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/requires.txt +0 -0
  62. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/src/maxc_cli.egg-info/top_level.txt +0 -0
  63. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_agent_hints_and_cli.py +0 -0
  64. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_agent_skill_commands.py +0 -0
  65. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_backend_data.py +0 -0
  66. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_backend_data_serialization.py +0 -0
  67. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_backend_meta.py +0 -0
  68. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_build_release_script.py +0 -0
  69. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_cache.py +0 -0
  70. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_catalog.py +0 -0
  71. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_catalog_bootstrap.py +0 -0
  72. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_cli_arg_validation.py +0 -0
  73. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_cli_mock.py +0 -0
  74. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_cli_query_parse_and_sanitize.py +0 -0
  75. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_compat.py +0 -0
  76. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_e2e_smoke.py +0 -0
  77. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_envelope_shape.py +0 -0
  78. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_error_self_correction.py +0 -0
  79. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_error_translation.py +0 -0
  80. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_exit_codes.py +0 -0
  81. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_external_auth.py +0 -0
  82. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_flag_hoist.py +0 -0
  83. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_help_format.py +0 -0
  84. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_help_version_e2e.py +0 -0
  85. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_helpers.py +0 -0
  86. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_helpers_csv.py +0 -0
  87. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_integration.py +0 -0
  88. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_job_improvements.py +0 -0
  89. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_masking.py +0 -0
  90. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_meta_schema_and_partition_cols.py +0 -0
  91. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_phase1_improvements.py +0 -0
  92. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_pyinstaller_bundle.py +0 -0
  93. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_query_auto_promote.py +0 -0
  94. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_query_result_csv_fallback.py +0 -0
  95. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_setting_parser.py +0 -0
  96. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_skill_cli_consistency.py +0 -0
  97. {maxc_cli-0.4.2 → maxc_cli-0.4.5}/tests/test_skill_renderer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxc-cli
3
- Version: 0.4.2
3
+ Version: 0.4.5
4
4
  Summary: Agent-native MaxCompute CLI for external coding agents
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.9
@@ -9,7 +9,7 @@ README = ROOT / "README.md"
9
9
 
10
10
  setup(
11
11
  name="maxc-cli",
12
- version="0.4.2",
12
+ version="0.4.5",
13
13
  description="Agent-native MaxCompute CLI for external coding agents",
14
14
  long_description=README.read_text(encoding="utf-8"),
15
15
  long_description_content_type="text/markdown",
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.4.2"
5
+ __version__ = "0.4.5"
@@ -152,7 +152,8 @@ SAMPLES: dict[str, str] = {
152
152
  "agent.skill": "maxc agent skill\nmaxc agent skill --json",
153
153
  "agent.skill.install": (
154
154
  "maxc agent skill install claude-code\n"
155
- "maxc agent skill install cursor --invocation aliyun-maxc"
155
+ "maxc agent skill install cursor --invocation aliyun-maxc\n"
156
+ "maxc agent skill install others --dir /path/to/skills"
156
157
  ),
157
158
  "agent.skill.update": (
158
159
  "maxc agent skill update cursor\n"
@@ -118,11 +118,12 @@ def _build_registry() -> tuple[Platform, ...]:
118
118
  install_root=_claude_root(),
119
119
  skill_subpath=None,
120
120
  extra_files=(),
121
- next_step_hint="Restart Claude Code or run /reload-plugins to activate",
121
+ next_step_hint="Skill is auto-discovered — start a new Claude Code session to activate",
122
122
  ),
123
123
  Platform(name="cursor", install_root=_simple_root(".cursor"),
124
124
  next_step_hint="Restart Cursor to activate"),
125
- Platform(name="windsurf", install_root=_simple_root(".windsurf"),
125
+ Platform(name="windsurf",
126
+ install_root=Path.home() / ".codeium" / "windsurf" / "skills" / "maxc-cli",
126
127
  next_step_hint="Restart Windsurf to activate"),
127
128
  Platform(name="codex", install_root=_codex_root(),
128
129
  next_step_hint="Restart Codex to activate"),
@@ -132,6 +133,14 @@ def _build_registry() -> tuple[Platform, ...]:
132
133
  next_step_hint="Restart Qoder to activate"),
133
134
  Platform(name="qoderwork", install_root=_simple_root(".qoderwork"),
134
135
  next_step_hint="Restart QoderWork to activate"),
136
+ Platform(name="openclaw",
137
+ install_root=Path.home() / ".openclaw" / "workspace" / "skills" / "maxc-cli",
138
+ next_step_hint="Restart OpenClaw to activate"),
139
+ Platform(name="hermes", install_root=_simple_root(".hermes"),
140
+ next_step_hint="Restart Hermes to activate"),
141
+ Platform(name="others", install_root=Path("__dir_required__"),
142
+ requires_dir=False,
143
+ next_step_hint="Point your agent at the installed skill directory"),
135
144
  )
136
145
 
137
146
 
@@ -2940,13 +2940,15 @@ class MaxCApp:
2940
2940
  def auth_can_i(
2941
2941
  self,
2942
2942
  *,
2943
- table_name: 'str',
2943
+ object_name: 'str',
2944
+ object_type: 'str' = "Table",
2944
2945
  operation: 'str',
2945
2946
  project: 'str | None' = None,
2946
2947
  ) -> 'Envelope':
2947
2948
  target_project = project or self.config.default_project
2948
2949
  payload, warnings = self.backend.can_i_info(
2949
- table_name=table_name,
2950
+ object_name=object_name,
2951
+ object_type=object_type,
2950
2952
  operation=operation,
2951
2953
  project=target_project,
2952
2954
  )
@@ -3407,6 +3409,11 @@ class MaxCApp:
3407
3409
  platform = agent_platforms.resolve(platform_name)
3408
3410
  except KeyError as exc:
3409
3411
  raise ValidationError(str(exc))
3412
+ if dir_override is None and not platform.install_root.is_absolute():
3413
+ raise ValidationError(
3414
+ f"Platform {platform.name!r} has no default install path — "
3415
+ f"pass --dir <path> to specify where the skill should be installed."
3416
+ )
3410
3417
  target = agent_platforms.effective_target(platform, dir_override)
3411
3418
  return platform, target
3412
3419
 
@@ -0,0 +1,164 @@
1
+ """Auth-related mixin for OdpsBackend."""
2
+
3
+ from typing import Any
4
+ from xml.etree import ElementTree
5
+
6
+ from ..exceptions import BackendConnectionError, MaxCError
7
+ from ..helpers import (
8
+ build_odps_identity_payload,
9
+ odps_identity_source,
10
+ quote_table_name,
11
+ translate_odps_error,
12
+ )
13
+
14
+
15
+ class AuthMixin:
16
+ """Mixin providing authentication and authorization methods."""
17
+
18
+ def whoami_info(self, *, project: 'str | None' = None) -> 'tuple[dict[str, Any], list[str]]':
19
+ """Get current identity info by executing ``whoami`` security query.
20
+
21
+ Calls ``client.execute_security_query("whoami")`` to verify the
22
+ connection and return desensitized identity information.
23
+
24
+ Args:
25
+ project: Optional project override.
26
+
27
+ Returns:
28
+ Tuple of (identity payload dict, warnings list).
29
+ """
30
+ target_project = project or self.project
31
+ try:
32
+ result = self.client.execute_security_query("whoami", project=target_project)
33
+ except Exception as exc:
34
+ raise translate_odps_error(exc, "whoami") from exc
35
+
36
+ owner_display_name = result.get("DisplayName") if isinstance(result, dict) else None
37
+ if owner_display_name:
38
+ self._owner_display_name = owner_display_name
39
+ return build_odps_identity_payload(
40
+ client=self.client,
41
+ settings=self.settings,
42
+ allowed_operations=self.config.allowed_operations,
43
+ identity_source=odps_identity_source(self.setting_sources),
44
+ auth_type=getattr(self.resolved_auth, "auth_type", "access_key"),
45
+ token_expires_at=getattr(self.resolved_auth, "token_expires_at", None),
46
+ project=target_project,
47
+ owner_display_name=owner_display_name,
48
+ )
49
+
50
+ def _check_permission(
51
+ self,
52
+ *,
53
+ object_name: 'str',
54
+ object_type: 'str',
55
+ action: 'str',
56
+ project: 'str',
57
+ ) -> 'tuple[bool, str]':
58
+ """Call the MaxCompute checkPermission REST API.
59
+
60
+ Uses GET /projects/{project}/auth/?type={type}&name={name}&grantee={action}
61
+ (same endpoint as Java SDK's SecurityManager#checkPermission).
62
+
63
+ Returns:
64
+ Tuple of (allowed: bool, message: str).
65
+ """
66
+ import json as _json
67
+
68
+ rest = self.client.rest
69
+ endpoint = rest.endpoint
70
+ url = f"{endpoint}/projects/{project}/auth/"
71
+ params = {
72
+ "type": object_type,
73
+ "name": object_name,
74
+ "grantee": action,
75
+ }
76
+ resp = rest.get(url, params=params)
77
+ root = ElementTree.fromstring(resp.content)
78
+ result = (root.findtext("Result") or "").strip()
79
+ raw_message = (root.findtext("Message") or "").strip()
80
+ try:
81
+ parsed = _json.loads(raw_message)
82
+ message = parsed.get("message", "") if isinstance(parsed, dict) else raw_message
83
+ except (ValueError, TypeError):
84
+ message = raw_message
85
+ return result.upper() == "ALLOW", message
86
+
87
+ def can_i_info(
88
+ self,
89
+ *,
90
+ object_name: 'str',
91
+ object_type: 'str' = "Table",
92
+ operation: 'str',
93
+ project: 'str | None' = None,
94
+ ) -> 'tuple[dict[str, Any], list[str]]':
95
+ """Check if a specific operation is allowed on an object.
96
+
97
+ Calls the MaxCompute checkPermission REST API directly.
98
+
99
+ Args:
100
+ object_name: Object name to check (table name, function name, etc.).
101
+ object_type: Object type (Table, Project, Function, Resource, Instance).
102
+ operation: ODPS ActionType (e.g. "Select", "CreateInstance").
103
+ project: Optional project override.
104
+
105
+ Returns:
106
+ Tuple of (permission payload dict, warnings list).
107
+ """
108
+ target_project = project or self.project
109
+ if object_type == "Table":
110
+ quote_table_name(object_name)
111
+
112
+ try:
113
+ allowed, message = self._check_permission(
114
+ object_name=object_name,
115
+ object_type=object_type,
116
+ action=operation,
117
+ project=target_project,
118
+ )
119
+ except Exception as exc:
120
+ translated = translate_odps_error(exc)
121
+ if isinstance(translated, BackendConnectionError):
122
+ raise translated
123
+ return (
124
+ {
125
+ "object_type": object_type,
126
+ "object_name": object_name,
127
+ "project": target_project,
128
+ "operation": operation,
129
+ "allowed": False,
130
+ "check_mode": "odps_check_permission_api",
131
+ "reason": translated.message,
132
+ "check_error_code": translated.error_code,
133
+ },
134
+ [],
135
+ )
136
+
137
+ return (
138
+ {
139
+ "object_type": object_type,
140
+ "object_name": object_name,
141
+ "project": target_project,
142
+ "operation": operation,
143
+ "allowed": allowed,
144
+ "check_mode": "odps_check_permission_api",
145
+ "reason": message if message else ("Allowed." if allowed else "Denied."),
146
+ "check_error_code": None if allowed else "PERMISSION_DENIED",
147
+ },
148
+ [],
149
+ )
150
+
151
+ def _get_owner_display_name(self) -> 'str | None':
152
+ """Get the current user's display name (e.g., 'ALIYUN$xxx' or 'RAM$xxx')."""
153
+ if self._owner_display_name is not None:
154
+ return self._owner_display_name
155
+ try:
156
+ result = self.client.execute_security_query("whoami", project=self.project)
157
+ display_name = result.get("DisplayName") if isinstance(result, dict) else None
158
+ if display_name:
159
+ self._owner_display_name = display_name
160
+ return display_name
161
+ except Exception:
162
+ pass
163
+
164
+ return None
@@ -532,16 +532,57 @@ def build_parser() -> argparse.ArgumentParser:
532
532
  auth_whoami.add_argument("--json", action="store_true", help="Output as JSON envelope")
533
533
  auth_whoami.set_defaults(handler=_handle_auth_whoami)
534
534
 
535
- auth_can_i = _make_parser(auth_subparsers, "can-i", "auth.can-i", help="Check whether an operation is allowed")
536
- auth_can_i.add_argument("--table", required=True, help="Table name to check")
535
+ _CAN_I_ACTIONS = [
536
+ "Select", "Describe", "Alter", "Update", "Drop", "Download", "All",
537
+ "Read", "Write", "List",
538
+ "CreateTable", "CreateInstance", "CreateFunction", "CreateResource",
539
+ "Execute", "Delete",
540
+ ]
541
+ _CAN_I_ACTIONS_LOWER = {a.lower(): a for a in _CAN_I_ACTIONS}
542
+ _CAN_I_OBJECT_TYPES = ["Table", "Project", "Function", "Resource", "Instance"]
543
+ _CAN_I_OBJECT_TYPES_LOWER = {t.lower(): t for t in _CAN_I_OBJECT_TYPES}
544
+
545
+ auth_can_i = _make_parser(
546
+ auth_subparsers, "can-i", "auth.can-i",
547
+ help="Check whether current user has a specific permission on an object",
548
+ description=(
549
+ "Check whether the current user has a specific permission on an ODPS object.\n"
550
+ "\n"
551
+ "Examples:\n"
552
+ " maxc auth can-i --table my_table --operation Select\n"
553
+ " maxc auth can-i --table my_table --operation Update --project other_proj\n"
554
+ " maxc auth can-i --object my_proj --type Project --operation CreateTable\n"
555
+ " maxc auth can-i --object my_proj --type Project --operation CreateInstance\n"
556
+ ),
557
+ formatter_class=AliyunRawTextFormatter,
558
+ )
559
+ auth_can_i.add_argument("--object", "--table", required=True, dest="object_name",
560
+ help="Object name to check (table name, or project name when --type=Project)")
561
+ auth_can_i.add_argument(
562
+ "--type",
563
+ default="Table",
564
+ type=lambda s: _CAN_I_OBJECT_TYPES_LOWER.get(s.lower(), s),
565
+ choices=_CAN_I_OBJECT_TYPES,
566
+ help=(
567
+ "Object type (case-insensitive, default: Table). "
568
+ "Table | Project | Function | Resource | Instance."
569
+ ),
570
+ )
537
571
  auth_can_i.add_argument(
538
572
  "--operation",
539
573
  required=True,
540
- type=lambda s: s.upper(),
541
- choices=["SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER"],
542
- help="Operation to check (only SELECT is probed against the live backend; others rely on the configured allow-list).",
574
+ type=lambda s: _CAN_I_ACTIONS_LOWER.get(s.lower(), s),
575
+ choices=_CAN_I_ACTIONS,
576
+ help=(
577
+ "Permission to check (case-insensitive). "
578
+ "Table: Select, Describe, Alter, Update, Drop, Download, All. "
579
+ "Project: CreateTable, CreateInstance, CreateFunction, CreateResource, List, Read, Write, All. "
580
+ "Function: Read, Write, Delete, Execute, All. "
581
+ "Resource: Read, Write, Delete, All. "
582
+ "Instance: Read, Write, All."
583
+ ),
543
584
  )
544
- auth_can_i.add_argument("--project", help="Target MaxCompute project")
585
+ auth_can_i.add_argument("--project", help="Project where the object lives (default: current project)")
545
586
  auth_can_i.add_argument("--json", action="store_true", help="Output as JSON envelope")
546
587
  auth_can_i.set_defaults(handler=_handle_auth_can_i)
547
588
 
@@ -802,13 +843,13 @@ def _is_json_mode(args: argparse.Namespace) -> bool:
802
843
 
803
844
 
804
845
  def _build_permission_denied_hints(app: MaxCApp | None) -> AgentHints:
805
- """Build PERMISSION_DENIED agent hints, suggesting _dev workspace switch when appropriate."""
846
+ """Build PERMISSION_DENIED agent hints, suggesting _dev project switch when appropriate."""
806
847
  actions = []
807
848
  project = app.config.default_project if app else None
808
849
  if project and not project.endswith("_dev"):
809
850
  actions.append(SuggestedAction(
810
851
  id="session.set",
811
- title="Switch to dev workspace",
852
+ title="Switch to dev project",
812
853
  command=f"maxc session set --project {project}_dev --json",
813
854
  ))
814
855
  actions.append(action("query", metadata={"sql_executed": "SELECT 1"}))
@@ -1454,7 +1495,8 @@ def _handle_auth_whoami(app: MaxCApp, args: argparse.Namespace, stdout: TextIO)
1454
1495
 
1455
1496
  def _handle_auth_can_i(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -> None:
1456
1497
  envelope = app.auth_can_i(
1457
- table_name=args.table,
1498
+ object_name=args.object_name,
1499
+ object_type=args.type,
1458
1500
  operation=args.operation,
1459
1501
  project=args.project,
1460
1502
  )
@@ -1039,12 +1039,12 @@ def classify_sql_error(message: str) -> dict[str, Any]:
1039
1039
  return {"error_type": "unknown"}
1040
1040
 
1041
1041
 
1042
- def _dev_workspace_hint(project: str | None) -> str:
1043
- """Return a hint about switching to the _dev workspace if the project is not a dev workspace."""
1042
+ def _dev_project_hint(project: str | None) -> str:
1043
+ """Return a hint about switching to the _dev project if the current project is production."""
1044
1044
  if project and not project.endswith("_dev"):
1045
1045
  return (
1046
- f"Current project '{project}' is a production workspace. "
1047
- f"Personal accounts usually only have access to the dev workspace (_dev). "
1046
+ f"Current project '{project}' is a production project. "
1047
+ f"Personal accounts usually only have access to the dev project (_dev). "
1048
1048
  f"Try switching: maxc session set --project {project}_dev"
1049
1049
  )
1050
1050
  return ""
@@ -1063,7 +1063,7 @@ def _build_permission_error(
1063
1063
  preserved as the error message for diagnostics. Context-specific
1064
1064
  guidance goes into the suggestion field.
1065
1065
  """
1066
- dev_hint = _dev_workspace_hint(project_name)
1066
+ dev_hint = _dev_project_hint(project_name)
1067
1067
 
1068
1068
  if context == "list_projects" and project_name:
1069
1069
  suggestion = f"Verify that your account has `odps:Read` on project {project_name}, or contact the project owner."
@@ -11,7 +11,6 @@ Use the live CLI instead of inventing a separate MaxCompute adapter. Prefer `{{c
11
11
 
12
12
  - First-time setup or repair of Python or `maxc-cli`
13
13
  - Auth bootstrap or identity inspection (AK/SK or env vars)
14
- - Migrating from odpscmd (reusing existing ODPS Console credentials)
15
14
  - Session project or schema overrides
16
15
  - Metadata discovery, schema inspection, fast table/column search
17
16
  - Read-only query execution or job tracking
@@ -43,9 +42,9 @@ These are non-negotiable. See [references/red-lines.md](references/red-lines.md)
43
42
 
44
43
  1. **Always use `--json`** for machine work. Use `--format markdown` for user-facing output, `--format brief` in token-tight contexts. `--json` is shorthand for `--format json`. **`--format` is a top-level flag — it must come before the subcommand**: `{{cli}} --format markdown query "SELECT 1"` (✓), not `{{cli}} query "SELECT 1" --format markdown` (✗). `--json` may appear anywhere because each subcommand also accepts it.
45
44
  2. **Never invent names** — table, schema, project, or endpoint. Verify with `meta` commands and `auth whoami`.
46
- 3. **Default to `--project` for the user's target workspace.** The configured project (in `~/.maxc/config.yaml`) is the user's **home dev workspace** — the data they actually want to query usually lives in a *different* workspace (often the corresponding production one). When the user mentions a table/project without specifying which workspace, **ask first**, then pass `--project <name>` on every meta/data command and use `project.table` in SQL.
47
- 4. **Workspace naming convention is a fixed pair:** `<name>_dev` is the dev workspace; the same `<name>` **without** the `_dev` suffix is its corresponding **production** workspace. They share metadata structure but hold different data and different permissions. See Dev vs Production Workspaces below.
48
- 5. **Never re-prompt for credentials** when `auth whoami` shows `authenticated=true`. Permission errors are almost always a workspace/project issue, not a credential issue.
45
+ 3. **Default to `--project` for the user's target project.** The configured project (in `~/.maxc/config.yaml`) is the user's **dev project** — the data they actually want to query usually lives in a *different* project (often the corresponding production one). When the user mentions a table/project without specifying which environment, **ask first**, then pass `--project <name>` on every meta/data command and use `project.table` in SQL.
46
+ 4. **Project naming convention is a fixed pair:** `<name>_dev` is the dev project; the same `<name>` **without** the `_dev` suffix is its corresponding **production** project. Together they form one DataWorks workspace. They share metadata structure but hold different data and different permissions. See Dev vs Production Projects below.
47
+ 5. **Never re-prompt for credentials** when `auth whoami` shows `authenticated=true`. Permission errors are almost always a project environment issue (dev vs prod), not a credential issue.
49
48
  6. **Always discover partitions** via `meta latest-partition` before querying partitioned tables. Format varies per table.
50
49
  7. **Always read `error.suggestion`** before retrying a failed command. Same input → same error.
51
50
  8. **Never install or upgrade Python** without explicit user confirmation.
@@ -56,8 +55,7 @@ These are non-negotiable. See [references/red-lines.md](references/red-lines.md)
56
55
  When `auth whoami --json` returns `configured=false` (no auth set up), follow [references/bootstrap-flow.md](references/bootstrap-flow.md) step by step. Three principles:
57
56
 
58
57
  1. **Never pick the auth method yourself** — always ask the user to choose between AK/SK and environment variables.
59
- 2. **If the user already has `odpscmd` configured**, proactively offer to migrate those credentials before asking them to enter anything new see [references/migrate-from-odpscmd.md](references/migrate-from-odpscmd.md).
60
- 3. **If `auth whoami` shows `auth_type=external`, the user is on an externally-managed credential provider — do NOT modify the auth config.** Treat the bootstrap as already done. Only `project`/`endpoint`/`schema` are safe to change (via `session set` or by re-running `auth login-external` with the same `--process-command`).
58
+ 2. **If `auth whoami` shows `auth_type=external`, the user is on an externally-managed credential provider — do NOT modify the auth config.** Treat the bootstrap as already done. Only `project`/`endpoint`/`schema` are safe to change (via `session set` or by re-running `auth login-external` with the same `--process-command`).
61
59
 
62
60
  ## First Pass
63
61
 
@@ -87,38 +85,38 @@ Key paths:
87
85
 
88
86
  See [references/json-output-format.md](references/json-output-format.md) for full examples and [references/command-patterns.md](references/command-patterns.md) §JSON Contract for all data shapes.
89
87
 
90
- ## Dev vs Production Workspaces
88
+ ## Dev vs Production Projects
91
89
 
92
- MaxCompute workspaces come in **fixed pairs**: a dev workspace and its corresponding production workspace. Confusing the two is the #1 source of permission errors.
90
+ A single **DataWorks workspace** corresponds to **two MaxCompute projects**: a dev project and a production project. Confusing the two is the #1 source of permission errors.
93
91
 
94
92
  ### The naming pair (memorize this)
95
93
 
96
- | Workspace type | Name pattern | Example | Who can access | What lives there |
97
- |----------------|--------------|---------|----------------|------------------|
94
+ | Project type | Name pattern | Example | Who can access | What lives there |
95
+ |----------------|--------------|------------------|----------------|------------------|
98
96
  | **Dev** | `<name>_dev` | `my_project_dev` | Personal accounts (the user themselves) | Test data, scratch tables, the user's own work |
99
97
  | **Production** | `<name>` | `my_project` | Usually only service accounts / DataWorks pipelines | The real business data the user actually wants to query |
100
98
 
101
- The dev workspace and the production workspace **share metadata structure but hold different data**. A table that exists in `my_project_dev` almost always exists in `my_project` too — but the rows, partitions, and freshness will differ.
99
+ Both projects belong to the same DataWorks workspace (`my_project`). They **share metadata structure but hold different data**. A table that exists in `my_project_dev` almost always exists in `my_project` too — but the rows, partitions, and freshness will differ.
102
100
 
103
101
  ### Other key facts
104
102
 
105
- - The project configured in `~/.maxc/config.yaml` or env vars is always the **dev** workspace — this is the user's home workspace.
106
- - Personal accounts usually only have *write* access to dev and *read* access to production (varies by org policy). Pointing a session directly at production often results in `PERMISSION_DENIED`.
107
- - `--project` is the canonical way to access **another project's** tables — most often the corresponding production workspace, occasionally a different team's project.
108
- - When the user asks about a table without naming the workspace, **ask whether they mean the dev or production copy** before guessing.
103
+ - The project configured in `~/.maxc/config.yaml` or env vars is always the **dev project** — this is the user's home project.
104
+ - Personal accounts usually only have *write* access to dev and *read* access to production (varies by org policy). Pointing a session directly at the production project often results in `PERMISSION_DENIED`.
105
+ - `--project` is the canonical way to access **another project's** tables — most often the corresponding production project, occasionally a different team's project.
106
+ - When the user asks about a table without naming the project, **ask whether they mean the dev or production copy** before guessing.
109
107
 
110
- ### How to tell which workspace you are in
108
+ ### How to tell which project you are in
111
109
 
112
110
  ```bash
113
111
  {{cli}} auth whoami --json # check data.identity.project — ends with _dev?
114
112
  {{cli}} session show --json # check current session project
115
113
  ```
116
114
 
117
- If the project name does NOT end with `_dev`, you may be pointed at a production workspace by mistake.
115
+ If the project name does NOT end with `_dev`, you may be pointed at the production project by mistake.
118
116
 
119
- ### Accessing production tables from dev workspace
117
+ ### Accessing production tables from dev project
120
118
 
121
- Use `--project` to read metadata from the production workspace without switching session:
119
+ Use `--project` to read metadata from the production project without switching session:
122
120
 
123
121
  ```bash
124
122
  {{cli}} meta list-tables --project my_project --json
@@ -126,10 +124,10 @@ Use `--project` to read metadata from the production workspace without switching
126
124
  {{cli}} data sample my_table --project my_project --json
127
125
  ```
128
126
 
129
- When writing SQL, use `project.table` format to reference tables in another workspace:
127
+ When writing SQL, use `project.table` format to reference tables in another project:
130
128
 
131
129
  ```sql
132
- -- From dev workspace, query a production table
130
+ -- From dev project, query a production table
133
131
  SELECT * FROM my_project.my_table WHERE ds = '20260418' LIMIT 100
134
132
  ```
135
133
 
@@ -139,7 +137,7 @@ Do NOT use bare table names (`FROM my_table`) when the target table lives in a d
139
137
 
140
138
  | Scenario | Symptom | Fix |
141
139
  |----------|---------|-----|
142
- | Config points to production workspace | `PERMISSION_DENIED` on most operations | `{{cli}} session set --project my_project_dev` |
140
+ | Config points to production project | `PERMISSION_DENIED` on most operations | `{{cli}} session set --project my_project_dev` |
143
141
  | Need to read production table metadata | `PERMISSION_DENIED` on `meta describe` | `{{cli}} meta describe my_table --project my_project --json` |
144
142
  | SQL references a table in another project without project prefix | `TABLE_NOT_FOUND` | Use `project.table` format in SQL |
145
143
  | Mixed access: dev metadata + production data | Confusing results | Be explicit: use `--project` for metadata commands, `project.table` in SQL |
@@ -227,7 +225,6 @@ For full command syntax and options, see [references/command-patterns.md](refere
227
225
  | Check permission for an op | `{{cli}} auth can-i --table T --operation SELECT --json` |
228
226
  | Diagnose a failed job | `{{cli}} job diagnose <id> --json` |
229
227
  | Add semantic metadata to a table | `{{cli}} meta semantic set T ... --json` (see command-patterns.md §Semantic Metadata) |
230
- | Migrate from odpscmd | See [references/migrate-from-odpscmd.md](references/migrate-from-odpscmd.md) |
231
228
  | Plan NL→SQL before writing | See [references/text2sql-principles.md](references/text2sql-principles.md) |
232
229
  | Look up MaxCompute SQL dialect rule | See [references/maxcompute-select-guide.md](references/maxcompute-select-guide.md) |
233
230
  | Pick a query template (Top-N, PIVOT, retention, …) | See [references/sql-query-patterns.md](references/sql-query-patterns.md) |
@@ -260,7 +257,6 @@ For full command syntax and options, see [references/command-patterns.md](refere
260
257
  | [bootstrap-flow.md](references/bootstrap-flow.md) | First-time setup or `configured=false` |
261
258
  | [setup-install.md](references/setup-install.md) | Python / maxc-cli install detail |
262
259
  | [bootstrap-auth.md](references/bootstrap-auth.md) | Per-method auth setup (AK/SK, env vars) |
263
- | [migrate-from-odpscmd.md](references/migrate-from-odpscmd.md) | User has `odpscmd` configured |
264
260
  | [command-patterns.md](references/command-patterns.md) | Full command syntax, output shapes, semantic, multi-project, schema, async |
265
261
  | [json-output-format.md](references/json-output-format.md) | JSON envelope examples |
266
262
  | [partition-guide.md](references/partition-guide.md) | Partition naming, MAX_PT() guidance, ambiguity |
@@ -28,10 +28,6 @@ If the console script is not on `PATH`, use:
28
28
 
29
29
  In sandboxes or CI, make sure those paths are writable.
30
30
 
31
- ### Migrating From odpscmd
32
-
33
- If the user already has `odpscmd` configured, reuse their credentials — see [migrate-from-odpscmd.md](migrate-from-odpscmd.md) for the field mapping and config conversion steps.
34
-
35
31
  ---
36
32
 
37
33
  ## Step 1: Check Current Auth Status
@@ -7,7 +7,7 @@ When `auth whoami --json` returns `configured=false`, follow the three phases be
7
7
  ```
8
8
  Phase 1: Prerequisites → setup-install.md
9
9
 
10
- Phase 2: Auth → bootstrap-auth.md (or migrate-from-odpscmd.md)
10
+ Phase 2: Auth → bootstrap-auth.md
11
11
 
12
12
  Phase 3: Verify → {{cli}} auth whoami --json
13
13
  ```
@@ -51,25 +51,17 @@ Then jump to the matching path in [bootstrap-auth.md](bootstrap-auth.md):
51
51
 
52
52
  If `auth whoami --json` shows `auth_type=external` (or `provider: external` in the saved config), the user is on an externally-managed credential provider. **Do not run Phase 2.** The auth is already set up — only `project`/`endpoint`/`schema` are safe to change via `session set` or by re-running the original `auth login-external` with updated `--project`/`--endpoint`. Treat bootstrap as complete and move to Phase 3.
53
53
 
54
- ### Shortcut for users with `odpscmd` already configured
55
-
56
- Before asking the question above, check whether the user already has `odpscmd` set up. If yes:
57
-
58
- > "Do you already have `odpscmd` configured? We can migrate the existing credentials without you re-entering anything."
59
-
60
- Then follow [migrate-from-odpscmd.md](migrate-from-odpscmd.md).
61
-
62
54
  ### Always confirm project and endpoint
63
55
 
64
56
  Regardless of method, ask the user explicitly for `project` and `endpoint`. If a value is already in the config or env, present it as a default but require confirmation. See [bootstrap-auth.md](bootstrap-auth.md) §"Always ask for project and endpoint".
65
57
 
66
- ### Dev vs production workspace check
58
+ ### Dev vs production project check
67
59
 
68
60
  If the project name does **not** end with `_dev`, warn the user:
69
61
 
70
- > "Project `<project>` does not end with `_dev`. Personal accounts usually only have access to dev workspaces — would you like to switch to `<project>_dev`?"
62
+ > "Project `<project>` does not end with `_dev`. Personal accounts usually only have access to dev projects — would you like to switch to `<project>_dev`?"
71
63
 
72
- See SKILL.md §"Dev vs Production Workspaces" for the full rationale.
64
+ See SKILL.md §"Dev vs Production Projects" for the full rationale.
73
65
 
74
66
  ---
75
67
 
@@ -100,5 +92,4 @@ Expected: `data.identity.authenticated=true`. `validation_status` interpretation
100
92
 
101
93
  - Step-by-step Python / maxc-cli installation — see [setup-install.md](setup-install.md).
102
94
  - Each auth method's exact CLI flags and saved YAML — see [bootstrap-auth.md](bootstrap-auth.md).
103
- - odpscmd field-by-field migration — see [migrate-from-odpscmd.md](migrate-from-odpscmd.md).
104
95
  - Public cloud endpoint catalog — present in [bootstrap-auth.md](bootstrap-auth.md) Path A.
@@ -343,7 +343,7 @@ fi
343
343
  # NOT_FOUND → search for the correct name
344
344
  {{cli}} meta search "partial_name" --json
345
345
 
346
- # PERMISSION_DENIED → check permissions (often a workspace issue)
346
+ # PERMISSION_DENIED → check permissions (often a dev vs prod project issue)
347
347
  {{cli}} auth can-i --table your_table --operation SELECT --json
348
348
 
349
349
  # JOB_TIMEOUT → check status and continue waiting
@@ -13,7 +13,7 @@ Cross-referenced with SKILL.md §Core Principles (which lists the highest-priori
13
13
  | 1 | Always use `--json` for machine-driven work | Plain output is for humans; agents must parse the envelope |
14
14
  | 2 | Never invent table, schema, project, or endpoint names | Always verify with `meta search` / `meta list-tables` / `auth whoami` |
15
15
  | 3 | Never install or upgrade Python without explicit user confirmation | System-level change with broad impact |
16
- | 4 | Never re-prompt for credentials when `auth whoami` shows `authenticated=true` | Permission errors are almost always a workspace issue, not a credential issue — see SKILL.md §Dev vs Production Workspaces |
16
+ | 4 | Never re-prompt for credentials when `auth whoami` shows `authenticated=true` | Permission errors are almost always a dev vs production project issue, not a credential issue — see SKILL.md §Dev vs Production Projects |
17
17
  | 5 | Always check partition value via `meta latest-partition` before querying partitioned tables | Hardcoded partitions go stale; format varies per table |
18
18
  | 6 | Always use schema-qualified (`schema.table`) or `project.table` names in SQL | Bare names fail across schemas/projects |
19
19
  | 7 | Never log, echo, or include AK/SK in output | Even in error context |
@@ -37,7 +37,7 @@ Cross-referenced with SKILL.md §Core Principles (which lists the highest-priori
37
37
  | Running a query without checking cost first | Use `query cost`, or `--cost-check N` to auto-abort |
38
38
  | Ignoring `agent_hints.warnings` in the response | They surface backend issues, cache staleness, cost alerts |
39
39
  | Assuming `meta describe` data is live | Cache may be stale; check `metadata.source` and warnings |
40
- | Using a production project name as default | See SKILL.md §Dev vs Production Workspaces |
40
+ | Using a production project name as default | See SKILL.md §Dev vs Production Projects |
41
41
  | Querying partitioned table without partition filter | Always run `meta latest-partition` first; use the exact returned value in WHERE |
42
42
 
43
43
  ## Agent Anti-Patterns
@@ -64,7 +64,7 @@ When `status=failure`, inspect `error.code` and follow the recovery action. Alwa
64
64
  | `COLUMN_NOT_FOUND` | Column reference does not exist | Check `error.available`; run `meta describe <table> --json` |
65
65
  | `WRITE_OPERATION_REQUIRES_FORCE` | SQL DDL/DML blocked by read-only mode | Read-only is by design; use `--force` only if authorized. Does NOT apply to `data upload` (Tunnel-based write, no `--force` needed). |
66
66
  | `CSV_PARSE_ERROR` | A CSV cell could not be parsed against the column type during `data upload` | Read `error.context.line` (1-based row number, including header if present) and `error.context.column` (the column **name** as a string, not a position index) to find the bad cell; fix the source CSV and retry. The Tunnel session is aborted, nothing is committed. |
67
- | `PERMISSION_DENIED` | No access to the resource | Almost always dev vs production workspace — see SKILL.md §Dev vs Production Workspaces |
67
+ | `PERMISSION_DENIED` | No access to the resource | Almost always dev vs production project issue — see SKILL.md §Dev vs Production Projects |
68
68
  | `SQL_ERROR` | SQL syntax or execution error | Fix the SQL; use `query explain` to validate first |
69
69
  | `COST_LIMIT_EXCEEDED` | Cost exceeds `--cost-check` threshold | Add partition filters, reduce columns, or raise the threshold |
70
70
  | `BACKEND_CONNECTION_ERROR` | Network or service unavailable | Retry after a delay; check endpoint with `auth whoami --json` |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxc-cli
3
- Version: 0.4.2
3
+ Version: 0.4.5
4
4
  Summary: Agent-native MaxCompute CLI for external coding agents
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.9
@@ -46,7 +46,6 @@ src/maxc_cli/skills/references/command-patterns.md
46
46
  src/maxc_cli/skills/references/json-output-format.md
47
47
  src/maxc_cli/skills/references/maxcompute-select-guide.md
48
48
  src/maxc_cli/skills/references/maxcompute-sql-notes.md
49
- src/maxc_cli/skills/references/migrate-from-odpscmd.md
50
49
  src/maxc_cli/skills/references/partition-guide.md
51
50
  src/maxc_cli/skills/references/red-lines.md
52
51
  src/maxc_cli/skills/references/setup-install.md
@@ -90,4 +89,5 @@ tests/test_query_auto_promote.py
90
89
  tests/test_query_result_csv_fallback.py
91
90
  tests/test_setting_parser.py
92
91
  tests/test_skill_cli_consistency.py
92
+ tests/test_skill_eval.py
93
93
  tests/test_skill_renderer.py