maxc-cli 0.1.3__tar.gz → 0.1.4__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 (61) hide show
  1. {maxc_cli-0.1.3/src/maxc_cli.egg-info → maxc_cli-0.1.4}/PKG-INFO +4 -4
  2. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/README.md +3 -3
  3. maxc_cli-0.1.4/scripts/regression_test.py +326 -0
  4. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/setup.py +1 -1
  5. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/__init__.py +1 -1
  6. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/app.py +79 -55
  7. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/odps.py +1 -13
  8. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/query.py +60 -19
  9. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/cli.py +104 -10
  10. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/config.py +5 -0
  11. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/exceptions.py +5 -0
  12. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/helpers.py +47 -0
  13. maxc_cli-0.1.4/src/maxc_cli/masking.py +104 -0
  14. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/models.py +13 -12
  15. maxc_cli-0.1.4/src/maxc_cli/setting_parser.py +126 -0
  16. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/SKILL.md +74 -4
  17. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/references/command-patterns.md +3 -0
  18. maxc_cli-0.1.4/src/maxc_cli/skills/references/maxcompute-sql-notes.md +136 -0
  19. maxc_cli-0.1.4/src/maxc_cli/skills/references/partition-guide.md +135 -0
  20. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/utils.py +9 -1
  21. {maxc_cli-0.1.3 → maxc_cli-0.1.4/src/maxc_cli.egg-info}/PKG-INFO +4 -4
  22. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli.egg-info/SOURCES.txt +9 -2
  23. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_agent_hints_and_cli.py +4 -2
  24. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_agent_skill_commands_context.py +35 -9
  25. maxc_cli-0.1.4/tests/test_error_self_correction.py +75 -0
  26. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_external_auth.py +2 -0
  27. maxc_cli-0.1.4/tests/test_masking.py +134 -0
  28. maxc_cli-0.1.4/tests/test_setting_parser.py +207 -0
  29. maxc_cli-0.1.3/skills/use-maxc-cli/.DS_Store +0 -0
  30. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/MANIFEST.in +0 -0
  31. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/pyproject.toml +0 -0
  32. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/setup.cfg +0 -0
  33. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/__main__.py +0 -0
  34. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/audit.py +0 -0
  35. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/auth_providers.py +0 -0
  36. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/__init__.py +0 -0
  37. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/auth.py +0 -0
  38. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/catalog.py +0 -0
  39. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/data.py +0 -0
  40. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/job.py +0 -0
  41. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/backend/meta.py +0 -0
  42. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/cache.py +0 -0
  43. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/output.py +0 -0
  44. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/agents/openai.yaml +0 -0
  45. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -0
  46. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/references/migrate-from-odpscmd.md +0 -0
  47. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/skills/references/setup-install.md +0 -0
  48. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli/store.py +0 -0
  49. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
  50. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli.egg-info/entry_points.txt +0 -0
  51. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli.egg-info/requires.txt +0 -0
  52. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/src/maxc_cli.egg-info/top_level.txt +0 -0
  53. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_cache.py +0 -0
  54. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_catalog.py +0 -0
  55. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_cli_mock.py +0 -0
  56. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_compat.py +0 -0
  57. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_e2e_smoke.py +0 -0
  58. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_integration.py +0 -0
  59. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_integration_real.py +0 -0
  60. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_job_improvements.py +0 -0
  61. {maxc_cli-0.1.3 → maxc_cli-0.1.4}/tests/test_query_auto_promote.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxc-cli
3
- Version: 0.1.3
3
+ Version: 0.1.4
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.8
@@ -71,10 +71,10 @@ SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc
71
71
 
72
72
  ```bash
73
73
  pip install maxc-cli
74
- maxc agent install-skill claude-code # 或 cursor / windsurf / codex / qwen
74
+ maxc agent install-skill claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
75
75
  ```
76
76
 
77
- `install-skill` 从包内 `skills/` 目录拷贝 SKILL.md + references/ 到目标平台目录,写入 `.maxc-skill-version` 标记版本。同版本跳过,版本变化覆盖。
77
+ `install-skill` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
78
78
 
79
79
  ### preflight 检查
80
80
 
@@ -127,7 +127,7 @@ src/maxc_cli/
127
127
  ├── output.py # Rich / 纯文本渲染
128
128
  ├── auth_providers.py # AK-SK / NCS / 环境变量认证
129
129
  ├── backend/ # ODPS 后端(query / meta / catalog / data / diff 五个 mixin)
130
- └── skills/ # SKILL.md + references/ + agents/(随 pip 包安装,唯一源)
130
+ └── (skills/ 已迁移至独立仓库 aone-open-skill/maxcompute-cli-guidance)
131
131
  ```
132
132
 
133
133
  ## 文档
@@ -50,10 +50,10 @@ SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc
50
50
 
51
51
  ```bash
52
52
  pip install maxc-cli
53
- maxc agent install-skill claude-code # 或 cursor / windsurf / codex / qwen
53
+ maxc agent install-skill claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
54
54
  ```
55
55
 
56
- `install-skill` 从包内 `skills/` 目录拷贝 SKILL.md + references/ 到目标平台目录,写入 `.maxc-skill-version` 标记版本。同版本跳过,版本变化覆盖。
56
+ `install-skill` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
57
57
 
58
58
  ### preflight 检查
59
59
 
@@ -106,7 +106,7 @@ src/maxc_cli/
106
106
  ├── output.py # Rich / 纯文本渲染
107
107
  ├── auth_providers.py # AK-SK / NCS / 环境变量认证
108
108
  ├── backend/ # ODPS 后端(query / meta / catalog / data / diff 五个 mixin)
109
- └── skills/ # SKILL.md + references/ + agents/(随 pip 包安装,唯一源)
109
+ └── (skills/ 已迁移至独立仓库 aone-open-skill/maxcompute-cli-guidance)
110
110
  ```
111
111
 
112
112
  ## 文档
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python3
2
+ """Pre-release regression test - runs all CLI commands against real MaxCompute backend."""
3
+ import json
4
+ import os
5
+ import sys
6
+ import tempfile
7
+ from io import StringIO
8
+ from pathlib import Path
9
+
10
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
11
+ from maxc_cli.cli import run
12
+
13
+ RESULTS = []
14
+ TABLE = None
15
+ TABLE_COL = None
16
+ JOB_ID = None
17
+
18
+ def assert_eq(a, b): assert a == b, f"expected {b!r}, got {a!r}"
19
+ def assert_true(v): assert v, f"expected truthy, got {v!r}"
20
+ def assert_key(d, k): assert k in d, f"missing key: {k!r} in {list(d.keys())}"
21
+ def assert_type(v, t): assert isinstance(v, t), f"expected {t.__name__}, got {type(v).__name__}"
22
+
23
+ def run_cmd(label, argv, expect_code=0, check=None):
24
+ global JOB_ID
25
+ stdout, stderr = StringIO(), StringIO()
26
+ code = run(argv, cwd=Path.cwd(), stdout=stdout, stderr=stderr)
27
+ out = stdout.getvalue()
28
+ err = stderr.getvalue()
29
+ try:
30
+ data = json.loads(out) if out.strip() else {}
31
+ except json.JSONDecodeError:
32
+ data = {"_raw": out[:300]}
33
+
34
+ ok = (expect_code is None) or (code == expect_code)
35
+ detail = ""
36
+ if not ok:
37
+ detail = f"exit_code={code}, expected={expect_code}"
38
+ if err.strip():
39
+ detail += f" stderr={err[:200]}"
40
+ if ok and check:
41
+ try:
42
+ check(data)
43
+ except Exception as e:
44
+ ok = False
45
+ detail = str(e)
46
+
47
+ icon = "PASS" if ok else "FAIL"
48
+ RESULTS.append((label, icon, detail))
49
+ suffix = f" -- {detail}" if detail else ""
50
+ print(f" [{icon}] {label}{suffix}")
51
+ sys.stdout.flush()
52
+ return code, data
53
+
54
+ def pdata(d):
55
+ return d.get("data", {})
56
+
57
+
58
+ # ===========================================================
59
+ print("\n=== 1. Auth ===")
60
+ # ===========================================================
61
+ run_cmd("1.1 auth whoami", ["auth", "whoami", "--json"],
62
+ check=lambda d: (
63
+ assert_eq(d["status"], "success"),
64
+ assert_true(pdata(d)["identity"]["authenticated"]),
65
+ ))
66
+
67
+ # Get test table
68
+ _, tdata = run_cmd("(preflight) list-tables", ["meta", "list-tables", "--json"])
69
+ tables = pdata(tdata).get("tables", [])
70
+ TABLE = tables[0]["table_name"] if tables else None
71
+ if not TABLE:
72
+ print(" [SKIP] No tables available, aborting")
73
+ sys.exit(1)
74
+ print(f" >> Test table: {TABLE}")
75
+
76
+ # Describe to get column name
77
+ _, ddata = run_cmd("(preflight) describe", ["meta", "describe", TABLE, "--json"])
78
+ cols = pdata(ddata).get("table", {}).get("schema", [])
79
+ TABLE_COL = cols[0]["name"] if cols else "id"
80
+ print(f" >> Test column: {TABLE_COL}")
81
+
82
+ run_cmd("1.2 auth can-i SELECT", ["auth", "can-i", "--table", TABLE, "--operation", "SELECT", "--json"],
83
+ expect_code=None, # may be 0 or 1 depending on permissions
84
+ check=lambda d: assert_key(pdata(d), "authorization"))
85
+
86
+ run_cmd("1.3 auth can-i --brief", ["auth", "can-i", "--table", TABLE, "--operation", "SELECT", "--brief", "--json"],
87
+ expect_code=None, # may be 0 or 1
88
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
89
+
90
+
91
+ # ===========================================================
92
+ print("\n=== 2. Meta ===")
93
+ # ===========================================================
94
+ run_cmd("2.1 meta list-tables", ["meta", "list-tables", "--json"],
95
+ check=lambda d: assert_true(len(pdata(d).get("tables", [])) > 0))
96
+
97
+ run_cmd("2.2 meta describe", ["meta", "describe", TABLE, "--json"],
98
+ check=lambda d: (
99
+ assert_eq(pdata(d)["table"]["table_name"], TABLE),
100
+ assert_key(pdata(d)["table"], "schema"),
101
+ ))
102
+
103
+ run_cmd("2.3 meta describe --full", ["meta", "describe", TABLE, "--full", "--json"],
104
+ check=lambda d: assert_eq(d["status"], "success"))
105
+
106
+ run_cmd("2.4 meta search", ["meta", "search", "test", "--json"],
107
+ check=lambda d: assert_key(pdata(d)["search"], "matches"))
108
+
109
+ run_cmd("2.5 meta search-columns", ["meta", "search-columns", "id", "--json"],
110
+ check=lambda d: assert_key(pdata(d)["search"], "matches"))
111
+
112
+ run_cmd("2.6 meta partitions", ["meta", "partitions", TABLE, "--json"],
113
+ check=lambda d: assert_key(pdata(d), "partitions"))
114
+
115
+ run_cmd("2.7 meta latest-partition", ["meta", "latest-partition", TABLE, "--json"],
116
+ check=lambda d: assert_key(pdata(d)["partition"], "has_partitions"))
117
+
118
+ run_cmd("2.8 meta freshness", ["meta", "freshness", TABLE, "--json"],
119
+ check=lambda d: assert_key(pdata(d)["freshness"], "freshness_status"))
120
+
121
+ run_cmd("2.9 meta list-projects", ["meta", "list-projects", "--json"],
122
+ check=lambda d: assert_type(pdata(d)["projects"], list))
123
+
124
+ run_cmd("2.10 meta list-schemas", ["meta", "list-schemas", "--json"],
125
+ expect_code=None, # may fail if catalog not available
126
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
127
+
128
+
129
+ # ===========================================================
130
+ print("\n=== 3. Meta Semantic ===")
131
+ # ===========================================================
132
+ run_cmd("3.1 meta semantic set", ["meta", "semantic", "set", TABLE, "--desc", "regression test desc", "--json"],
133
+ check=lambda d: assert_eq(d["status"], "success"))
134
+
135
+ run_cmd("3.2 meta semantic get", ["meta", "semantic", "get", TABLE, "--json"],
136
+ check=lambda d: assert_eq(d["status"], "success"))
137
+
138
+ run_cmd("3.3 meta semantic list-missing", ["meta", "semantic", "list-missing", "--json"],
139
+ check=lambda d: assert_eq(d["status"], "success"))
140
+
141
+
142
+ # ===========================================================
143
+ print("\n=== 4. Query ===")
144
+ # ===========================================================
145
+ run_cmd("4.1 query simple", ["query", "SELECT 1 AS c1, 'test' AS c2", "--json", "--force"],
146
+ check=lambda d: (
147
+ assert_eq(d["status"], "success"),
148
+ assert_true(len(pdata(d)["result"]["rows"]) == 1),
149
+ ))
150
+
151
+ run_cmd("4.2 query cost", ["query", "cost", "SELECT 1 AS c1", "--json", "--force"],
152
+ check=lambda d: assert_key(pdata(d)["analysis"], "cost_model"))
153
+
154
+ run_cmd("4.3 query explain", ["query", "explain", "SELECT 1 AS c1", "--json", "--force"],
155
+ check=lambda d: assert_eq(pdata(d)["analysis"]["analysis_mode"], "explain"))
156
+
157
+ run_cmd("4.4 query pagination",
158
+ ["query", "SELECT 1 AS id UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5",
159
+ "--page-size", "2", "--json", "--force"],
160
+ check=lambda d: (
161
+ assert_true(pdata(d)["pagination"]["has_more"]),
162
+ assert_true(pdata(d)["pagination"]["next_cursor"] is not None),
163
+ ))
164
+
165
+ run_cmd("4.5 query dry-run", ["query", "SELECT 1", "--dry-run", "--json", "--force"],
166
+ check=lambda d: assert_eq(pdata(d)["result"]["returned_rows"], 0))
167
+
168
+ run_cmd("4.6 query wait=0", ["query", "SELECT 1 AS async_test", "--wait", "0", "--json", "--force"],
169
+ check=lambda d: (
170
+ assert_eq(d["status"], "pending"),
171
+ assert_key(pdata(d), "job"),
172
+ ))
173
+
174
+ tmpfile = Path(tempfile.mktemp(suffix=".csv"))
175
+ run_cmd("4.7 query --output csv",
176
+ ["query", "SELECT 1 AS a, 2 AS b", "--json", "--force", "--output", str(tmpfile), "--output-format", "csv"],
177
+ check=lambda d: assert_eq(d["status"], "success"))
178
+ if tmpfile.exists():
179
+ print(f" CSV file created: {tmpfile.stat().st_size} bytes")
180
+ tmpfile.unlink()
181
+ else:
182
+ RESULTS.append(("4.7b csv file exists", "FAIL", "file not created"))
183
+
184
+
185
+ # ===========================================================
186
+ print("\n=== 5. Query: New Features ===")
187
+ # ===========================================================
188
+ run_cmd("5.1 masking (phone/email/password)",
189
+ ["query", "SELECT 1 as id, '13812345678' as phone, 'alice@example.com' as email, 'secret' as password",
190
+ "--force", "--json"],
191
+ check=lambda d: (
192
+ assert_eq(pdata(d)["result"]["rows"][0]["phone"], "138****5678"),
193
+ assert_eq(pdata(d)["result"]["rows"][0]["email"], "a***@example.com"),
194
+ assert_eq(pdata(d)["result"]["rows"][0]["password"], "******"),
195
+ assert_true(any("masked" in w.lower() for w in d.get("agent_hints", {}).get("warnings", []))),
196
+ ))
197
+
198
+ run_cmd("5.2 LIMIT truncation warning",
199
+ ["query", "SELECT t.* FROM (SELECT 1 as id UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) t",
200
+ "--max-rows", "2", "--force", "--json"],
201
+ check=lambda d: (
202
+ assert_true(pdata(d)["pagination"]["has_more"]),
203
+ assert_true(any("truncated" in w.lower() for w in d.get("agent_hints", {}).get("warnings", []))),
204
+ ))
205
+
206
+ run_cmd("5.3 error self-correction (wrong column)",
207
+ ["query", f"SELECT nonexistent_col FROM {TABLE} LIMIT 5", "--force", "--json"],
208
+ expect_code=None, # SQL_ERROR(1) or other
209
+ check=lambda d: (
210
+ assert_true(d["status"] == "failure"),
211
+ assert_key(d.get("data", {}), "schema_context"),
212
+ ))
213
+
214
+
215
+ # ===========================================================
216
+ print("\n=== 6. Job ===")
217
+ # ===========================================================
218
+ _, jsubmit = run_cmd("6.1 job submit", ["job", "submit", "SELECT 1 AS job_test", "--json", "--force"],
219
+ check=lambda d: assert_key(pdata(d), "job_id"))
220
+ JOB_ID = pdata(jsubmit).get("job_id")
221
+ if JOB_ID:
222
+ print(f" job_id: {JOB_ID}")
223
+
224
+ run_cmd("6.2 job status", ["job", "status", JOB_ID, "--json"],
225
+ check=lambda d: assert_key(pdata(d).get("job", {}), "stage"))
226
+
227
+ run_cmd("6.3 job wait", ["job", "wait", JOB_ID, "--json"],
228
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
229
+
230
+ run_cmd("6.4 job result", ["job", "result", JOB_ID, "--json"],
231
+ check=lambda d: assert_key(pdata(d).get("result", {}), "rows"))
232
+
233
+ run_cmd("6.5 job diagnose", ["job", "diagnose", JOB_ID, "--json"],
234
+ check=lambda d: assert_key(pdata(d).get("diagnosis", {}), "diagnosis_category"))
235
+ else:
236
+ for label in ["6.2 job status", "6.3 job wait", "6.4 job result", "6.5 job diagnose"]:
237
+ RESULTS.append((label, "SKIP", "no job_id"))
238
+
239
+ run_cmd("6.6 job list", ["job", "list", "--json"],
240
+ check=lambda d: assert_type(pdata(d).get("jobs", None), list))
241
+
242
+
243
+ # ===========================================================
244
+ print("\n=== 7. Data ===")
245
+ # ===========================================================
246
+ run_cmd("7.1 data sample", ["data", "sample", TABLE, "--rows", "3", "--json"],
247
+ expect_code=None, # may fail if table is partitioned
248
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
249
+
250
+ run_cmd("7.2 data profile", ["data", "profile", TABLE, "--json"],
251
+ expect_code=None, # may fail if table is partitioned
252
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
253
+
254
+
255
+ # ===========================================================
256
+ print("\n=== 8. Diff ===")
257
+ # ===========================================================
258
+ run_cmd("8.1 diff schema self", ["diff", "schema", TABLE, TABLE, "--json"],
259
+ check=lambda d: assert_eq(pdata(d)["diff"]["compatible"], True))
260
+
261
+ run_cmd("8.2 diff partition self", ["diff", "partition", TABLE, TABLE, "--json"],
262
+ check=lambda d: assert_key(pdata(d)["diff"], "summary"))
263
+
264
+ run_cmd("8.3 diff data self", ["diff", "data", TABLE, TABLE, "--keys", TABLE_COL, "--rows", "5", "--json"],
265
+ expect_code=None, # may fail if table is partitioned
266
+ check=lambda d: assert_true(d["status"] in ("success", "failure")))
267
+
268
+
269
+ # ===========================================================
270
+ print("\n=== 9. Session ===")
271
+ # ===========================================================
272
+ run_cmd("9.1 session show", ["session", "show", "--json"],
273
+ check=lambda d: assert_key(pdata(d), "project"))
274
+
275
+ run_cmd("9.2 session set", ["session", "set", "--project", "meta_dev", "--json"],
276
+ check=lambda d: assert_eq(d["status"], "success"))
277
+
278
+ run_cmd("9.3 session unset", ["session", "unset", "--json"],
279
+ check=lambda d: assert_eq(d["status"], "success"))
280
+
281
+
282
+ # ===========================================================
283
+ print("\n=== 10. Agent ===")
284
+ # ===========================================================
285
+ run_cmd("10.1 agent context", ["agent", "context", "--json"],
286
+ check=lambda d: assert_key(pdata(d).get("context", {}), "project"))
287
+
288
+ run_cmd("10.2 agent skill", ["agent", "skill", "--json"],
289
+ check=lambda d: assert_eq(d["status"], "success"))
290
+
291
+
292
+ # ===========================================================
293
+ print("\n=== 11. Cache ===")
294
+ # ===========================================================
295
+ run_cmd("11.1 cache status", ["cache", "status", "--json"],
296
+ check=lambda d: assert_key(pdata(d), "table_count"))
297
+
298
+ run_cmd("11.2 cache clear", ["cache", "clear", "--json"],
299
+ check=lambda d: assert_key(pdata(d), "deleted_tables"))
300
+
301
+ run_cmd("11.3 cache build", ["cache", "build", "--json"],
302
+ check=lambda d: assert_key(pdata(d), "cached_tables"))
303
+
304
+ run_cmd("11.4 cache build-status", ["cache", "build-status", "--json"],
305
+ check=lambda d: assert_eq(d["status"], "success"))
306
+
307
+
308
+ # ===========================================================
309
+ # SUMMARY
310
+ # ===========================================================
311
+ print("\n" + "=" * 60)
312
+ print("REGRESSION TEST SUMMARY")
313
+ print("=" * 60)
314
+ passed = sum(1 for _, s, _ in RESULTS if s == "PASS")
315
+ failed = sum(1 for _, s, _ in RESULTS if s == "FAIL")
316
+ skipped = sum(1 for _, s, _ in RESULTS if s == "SKIP")
317
+
318
+ if failed:
319
+ print(f"\nFAILED ({failed}):")
320
+ for label, status, detail in RESULTS:
321
+ if status == "FAIL":
322
+ print(f" - {label}: {detail}")
323
+
324
+ print(f"\nTotal: {passed} passed, {failed} failed, {skipped} skipped / {len(RESULTS)} tests")
325
+ print("=" * 60)
326
+ sys.exit(1 if failed else 0)
@@ -9,7 +9,7 @@ README = ROOT / "README.md"
9
9
 
10
10
  setup(
11
11
  name="maxc-cli",
12
- version="0.1.3",
12
+ version="0.1.4",
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.1.3"
5
+ __version__ = "0.1.4"