maxc-cli 0.2.5__tar.gz → 0.3.1__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.
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/PKG-INFO +14 -9
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/README.md +13 -8
- maxc_cli-0.3.1/pyproject.toml +22 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/setup.py +1 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/__main__.py +0 -1
- maxc_cli-0.3.1/src/maxc_cli/_samples.py +187 -0
- maxc_cli-0.3.1/src/maxc_cli/agent_platforms.py +188 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/app.py +735 -221
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/auth_providers.py +11 -13
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/__init__.py +1 -2
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/auth.py +4 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/catalog.py +1 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/data.py +101 -33
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/job.py +28 -8
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/meta.py +64 -22
- maxc_cli-0.3.1/src/maxc_cli/backend/odps.py +200 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/backend/query.py +22 -9
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/cache.py +1 -2
- maxc_cli-0.3.1/src/maxc_cli/catalog_bootstrap.py +223 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/cli.py +733 -179
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/config.py +6 -6
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/exceptions.py +10 -2
- maxc_cli-0.3.1/src/maxc_cli/help_format.py +134 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/helpers.py +190 -139
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/masking.py +7 -8
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/models.py +19 -12
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/output.py +10 -11
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/setting_parser.py +4 -4
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/SKILL.md +49 -32
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/bootstrap-auth.md +18 -14
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/bootstrap-flow.md +12 -8
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/command-patterns.md +106 -103
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/json-output-format.md +8 -6
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/maxcompute-select-guide.md +2 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +11 -9
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/migrate-from-odpscmd.md +13 -11
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/partition-guide.md +13 -11
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/red-lines.md +3 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/setup-install.md +19 -8
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/sql-common-errors.md +5 -3
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/sql-query-patterns.md +3 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/text2sql-principles.md +2 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/utils.py +24 -3
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/PKG-INFO +14 -9
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/SOURCES.txt +25 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_agent_hints_and_cli.py +2 -2
- maxc_cli-0.3.1/tests/test_agent_platforms.py +95 -0
- maxc_cli-0.3.1/tests/test_agent_skill_commands.py +252 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_agent_skill_commands_context.py +136 -21
- maxc_cli-0.3.1/tests/test_backend_data.py +29 -0
- maxc_cli-0.3.1/tests/test_backend_data_serialization.py +247 -0
- maxc_cli-0.3.1/tests/test_backend_meta.py +92 -0
- maxc_cli-0.3.1/tests/test_build_release_script.py +59 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_cache.py +0 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_catalog.py +2 -3
- maxc_cli-0.3.1/tests/test_catalog_bootstrap.py +237 -0
- maxc_cli-0.3.1/tests/test_cli_arg_validation.py +132 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_cli_mock.py +669 -25
- maxc_cli-0.3.1/tests/test_cli_query_parse_and_sanitize.py +118 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_compat.py +5 -3
- maxc_cli-0.3.1/tests/test_envelope_shape.py +211 -0
- maxc_cli-0.3.1/tests/test_error_translation.py +101 -0
- maxc_cli-0.3.1/tests/test_exit_codes.py +107 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_external_auth.py +84 -12
- maxc_cli-0.3.1/tests/test_flag_hoist.py +48 -0
- maxc_cli-0.3.1/tests/test_help_format.py +166 -0
- maxc_cli-0.3.1/tests/test_helpers.py +51 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_integration.py +4 -2
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_integration_real.py +28 -4
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_job_improvements.py +0 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_masking.py +1 -1
- maxc_cli-0.3.1/tests/test_meta_schema_and_partition_cols.py +232 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_phase1_improvements.py +17 -13
- maxc_cli-0.3.1/tests/test_pyinstaller_bundle.py +61 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_query_auto_promote.py +4 -3
- maxc_cli-0.3.1/tests/test_query_result_csv_fallback.py +235 -0
- maxc_cli-0.3.1/tests/test_skill_cli_consistency.py +75 -0
- maxc_cli-0.3.1/tests/test_skill_renderer.py +201 -0
- maxc_cli-0.2.5/pyproject.toml +0 -7
- maxc_cli-0.2.5/src/maxc_cli/backend/odps.py +0 -134
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/MANIFEST.in +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/scripts/regression_test.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/setup.cfg +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_helpers_csv.py +1 -1
- {maxc_cli-0.2.5 → maxc_cli-0.3.1}/tests/test_setting_parser.py +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maxc-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -29,9 +29,12 @@ MaxCompute CLI — 不是 Agent,而是给 Agent 调用的结构化工具层。
|
|
|
29
29
|
```bash
|
|
30
30
|
pip install maxc-cli
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
maxc auth login --
|
|
34
|
-
|
|
32
|
+
# 认证(推荐:交互式 Catalog Picker,无需手填 project)
|
|
33
|
+
maxc auth login --access-id ID --access-key-secret KEY --json
|
|
34
|
+
|
|
35
|
+
# 其他方式
|
|
36
|
+
maxc auth login --from-env --json # 从环境变量
|
|
37
|
+
maxc auth login --access-id ID --access-key-secret KEY --project PROJ --endpoint URL --json # CI / 脚本:显式 project
|
|
35
38
|
|
|
36
39
|
# 确认就绪
|
|
37
40
|
maxc auth whoami --json
|
|
@@ -56,7 +59,7 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
56
59
|
| **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
|
|
57
60
|
| **diff** | `schema`, `partition`, `data` | 表结构/分区/数据对比 |
|
|
58
61
|
| **cache** | `build`, `build-status`, `status`, `clear` | 元数据缓存管理 |
|
|
59
|
-
| **agent** | `context`, `skill`, `
|
|
62
|
+
| **agent** | `context`, `skill install`, `skill list` | Agent 集成与 SKILL 安装 |
|
|
60
63
|
|
|
61
64
|
所有命令支持 `--json` 输出 Envelope v2.0 结构化响应。支持 `--format json|markdown|brief`(全局标志)。
|
|
62
65
|
|
|
@@ -66,16 +69,16 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
66
69
|
|
|
67
70
|
SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc-cli`。
|
|
68
71
|
|
|
69
|
-
### 方式 2:install
|
|
72
|
+
### 方式 2:agent skill install(内网兜底)
|
|
70
73
|
|
|
71
74
|
先 pip install,再一键注册到 Agent 平台:
|
|
72
75
|
|
|
73
76
|
```bash
|
|
74
77
|
pip install maxc-cli
|
|
75
|
-
maxc agent
|
|
78
|
+
maxc agent skill install claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
`install
|
|
81
|
+
`agent skill install` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
|
|
79
82
|
|
|
80
83
|
### preflight 检查
|
|
81
84
|
|
|
@@ -180,7 +183,9 @@ src/maxc_cli/
|
|
|
180
183
|
## 限制
|
|
181
184
|
|
|
182
185
|
- **只读**:CLI 强制 SELECT-only,不支持 DDL/DML
|
|
183
|
-
- **auth login**:AK/SK 明文存储于 `~/.maxc/config.yaml`(文件权限 0600
|
|
186
|
+
- **auth login**:AK/SK 明文存储于 `~/.maxc/config.yaml`(文件权限 0600)。
|
|
187
|
+
省略 `--project` 时通过 Catalog API 弹交互式 project picker(需 TTY,仅支持中国区 project)。
|
|
188
|
+
CI 用 `--no-picker`;想重选已保存的 project 用 `--reselect`;非中国区用 `--catalog-endpoint` 覆盖。
|
|
184
189
|
- **list-tables 分页**:CLI 侧 offset token,非服务端游标
|
|
185
190
|
- **diff data**:按主键快照对比,非全量 diff
|
|
186
191
|
|
|
@@ -7,9 +7,12 @@ MaxCompute CLI — 不是 Agent,而是给 Agent 调用的结构化工具层。
|
|
|
7
7
|
```bash
|
|
8
8
|
pip install maxc-cli
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
maxc auth login --
|
|
12
|
-
|
|
10
|
+
# 认证(推荐:交互式 Catalog Picker,无需手填 project)
|
|
11
|
+
maxc auth login --access-id ID --access-key-secret KEY --json
|
|
12
|
+
|
|
13
|
+
# 其他方式
|
|
14
|
+
maxc auth login --from-env --json # 从环境变量
|
|
15
|
+
maxc auth login --access-id ID --access-key-secret KEY --project PROJ --endpoint URL --json # CI / 脚本:显式 project
|
|
13
16
|
|
|
14
17
|
# 确认就绪
|
|
15
18
|
maxc auth whoami --json
|
|
@@ -34,7 +37,7 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
34
37
|
| **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
|
|
35
38
|
| **diff** | `schema`, `partition`, `data` | 表结构/分区/数据对比 |
|
|
36
39
|
| **cache** | `build`, `build-status`, `status`, `clear` | 元数据缓存管理 |
|
|
37
|
-
| **agent** | `context`, `skill`, `
|
|
40
|
+
| **agent** | `context`, `skill install`, `skill list` | Agent 集成与 SKILL 安装 |
|
|
38
41
|
|
|
39
42
|
所有命令支持 `--json` 输出 Envelope v2.0 结构化响应。支持 `--format json|markdown|brief`(全局标志)。
|
|
40
43
|
|
|
@@ -44,16 +47,16 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
44
47
|
|
|
45
48
|
SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc-cli`。
|
|
46
49
|
|
|
47
|
-
### 方式 2:install
|
|
50
|
+
### 方式 2:agent skill install(内网兜底)
|
|
48
51
|
|
|
49
52
|
先 pip install,再一键注册到 Agent 平台:
|
|
50
53
|
|
|
51
54
|
```bash
|
|
52
55
|
pip install maxc-cli
|
|
53
|
-
maxc agent
|
|
56
|
+
maxc agent skill install claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
|
|
54
57
|
```
|
|
55
58
|
|
|
56
|
-
`install
|
|
59
|
+
`agent skill install` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
|
|
57
60
|
|
|
58
61
|
### preflight 检查
|
|
59
62
|
|
|
@@ -158,7 +161,9 @@ src/maxc_cli/
|
|
|
158
161
|
## 限制
|
|
159
162
|
|
|
160
163
|
- **只读**:CLI 强制 SELECT-only,不支持 DDL/DML
|
|
161
|
-
- **auth login**:AK/SK 明文存储于 `~/.maxc/config.yaml`(文件权限 0600
|
|
164
|
+
- **auth login**:AK/SK 明文存储于 `~/.maxc/config.yaml`(文件权限 0600)。
|
|
165
|
+
省略 `--project` 时通过 Catalog API 弹交互式 project picker(需 TTY,仅支持中国区 project)。
|
|
166
|
+
CI 用 `--no-picker`;想重选已保存的 project 用 `--reselect`;非中国区用 `--catalog-endpoint` 覆盖。
|
|
162
167
|
- **list-tables 分页**:CLI 侧 offset token,非服务端游标
|
|
163
168
|
- **diff data**:按主键快照对比,非全量 diff
|
|
164
169
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.pytest.ini_options]
|
|
6
|
+
pythonpath = ["src"]
|
|
7
|
+
testpaths = ["tests"]
|
|
8
|
+
|
|
9
|
+
[tool.ruff]
|
|
10
|
+
target-version = "py38"
|
|
11
|
+
line-length = 100
|
|
12
|
+
|
|
13
|
+
[tool.ruff.lint]
|
|
14
|
+
select = ["E", "F", "I", "UP"]
|
|
15
|
+
# E501 ignored for now: legacy codebase has 300+ long lines (mostly long SQL
|
|
16
|
+
# strings, dense parameter lists, and docstrings). Tighten in a later sweep
|
|
17
|
+
# once we've decided whether to wrap at the source or raise line-length.
|
|
18
|
+
ignore = ["E501"]
|
|
19
|
+
|
|
20
|
+
[tool.ruff.lint.per-file-ignores]
|
|
21
|
+
# Tests put `pytestmark = pytest.mark.unit` before imports on purpose.
|
|
22
|
+
"tests/*" = ["E402"]
|
|
@@ -9,7 +9,7 @@ README = ROOT / "README.md"
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="maxc-cli",
|
|
12
|
-
version="0.
|
|
12
|
+
version="0.3.1",
|
|
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",
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Per-command sample text for ``--help``. Keys are dotted command names.
|
|
2
|
+
|
|
3
|
+
Style: 1-3 lines, concrete and copy-pasteable. Prefer realistic project names
|
|
4
|
+
('my_proj') over placeholders ('PROJECT'). Wrap user-facing commands in the
|
|
5
|
+
quoting style they'd actually type.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
SAMPLES: dict[str, str] = {
|
|
10
|
+
"__top__": (
|
|
11
|
+
"maxc auth login\n"
|
|
12
|
+
'maxc query "SELECT 1"\n'
|
|
13
|
+
"maxc meta list-tables --project my_proj"
|
|
14
|
+
),
|
|
15
|
+
# ── query ──────────────────────────────────────────────────────────────
|
|
16
|
+
"query": (
|
|
17
|
+
'maxc query "SELECT 1"\n'
|
|
18
|
+
'maxc query cost "SELECT * FROM big_table"\n'
|
|
19
|
+
"maxc query explain \"SELECT * FROM t WHERE dt='20260101'\""
|
|
20
|
+
),
|
|
21
|
+
# ── auth ───────────────────────────────────────────────────────────────
|
|
22
|
+
"auth": "maxc auth login\nmaxc auth whoami --json",
|
|
23
|
+
"auth.login": (
|
|
24
|
+
"maxc auth login # interactive picker\n"
|
|
25
|
+
"maxc auth login --access-id AK --secret-access-key SK --no-picker"
|
|
26
|
+
),
|
|
27
|
+
"auth.login-external": (
|
|
28
|
+
"maxc auth login-external --process-command 'curl -s https://my-sts/token'"
|
|
29
|
+
),
|
|
30
|
+
"auth.whoami": "maxc auth whoami\nmaxc auth whoami --json",
|
|
31
|
+
"auth.can-i": "maxc auth can-i --table my_proj.my_table --operation SELECT",
|
|
32
|
+
# ── job ────────────────────────────────────────────────────────────────
|
|
33
|
+
"job": (
|
|
34
|
+
'maxc job submit "SELECT count(*) FROM my_table"\n'
|
|
35
|
+
"maxc job list --limit 20\n"
|
|
36
|
+
"maxc job status <job_id>"
|
|
37
|
+
),
|
|
38
|
+
"job.submit": (
|
|
39
|
+
'maxc job submit "SELECT count(*) FROM my_table"\n'
|
|
40
|
+
'maxc job submit --file query.sql --project my_proj'
|
|
41
|
+
),
|
|
42
|
+
"job.status": "maxc job status <job_id>\nmaxc job status <job_id> --json",
|
|
43
|
+
"job.wait": (
|
|
44
|
+
"maxc job wait <job_id>\n"
|
|
45
|
+
"maxc job wait <job_id> --timeout 600 --stream"
|
|
46
|
+
),
|
|
47
|
+
"job.diagnose": "maxc job diagnose <job_id>\nmaxc job diagnose <job_id> --json",
|
|
48
|
+
"job.result": (
|
|
49
|
+
"maxc job result <job_id>\n"
|
|
50
|
+
"maxc job result <job_id> --max-rows 1000 --json"
|
|
51
|
+
),
|
|
52
|
+
"job.cancel": "maxc job cancel <job_id>",
|
|
53
|
+
"job.list": "maxc job list\nmaxc job list --limit 50 --json",
|
|
54
|
+
# ── meta ───────────────────────────────────────────────────────────────
|
|
55
|
+
"meta": (
|
|
56
|
+
"maxc meta list-tables --project my_proj\n"
|
|
57
|
+
"maxc meta describe my_table\n"
|
|
58
|
+
"maxc meta search orders"
|
|
59
|
+
),
|
|
60
|
+
"meta.list-tables": (
|
|
61
|
+
"maxc meta list-tables --project my_proj\n"
|
|
62
|
+
"maxc meta list-tables --schema default --limit 50 --json"
|
|
63
|
+
),
|
|
64
|
+
"meta.describe": (
|
|
65
|
+
"maxc meta describe my_table\n"
|
|
66
|
+
"maxc meta describe my_proj.my_table --full --json"
|
|
67
|
+
),
|
|
68
|
+
"meta.search": (
|
|
69
|
+
"maxc meta search orders\n"
|
|
70
|
+
"maxc meta search user --project my_proj --json"
|
|
71
|
+
),
|
|
72
|
+
"meta.search-columns": (
|
|
73
|
+
"maxc meta search-columns user_id\n"
|
|
74
|
+
"maxc meta search-columns dt --project my_proj --json"
|
|
75
|
+
),
|
|
76
|
+
"meta.latest-partition": (
|
|
77
|
+
"maxc meta latest-partition my_table\n"
|
|
78
|
+
"maxc meta latest-partition my_proj.my_table --json"
|
|
79
|
+
),
|
|
80
|
+
"meta.freshness": (
|
|
81
|
+
"maxc meta freshness my_table\n"
|
|
82
|
+
"maxc meta freshness my_proj.my_table --json"
|
|
83
|
+
),
|
|
84
|
+
"meta.partitions": (
|
|
85
|
+
"maxc meta partitions my_table\n"
|
|
86
|
+
"maxc meta partitions my_proj.my_table --limit 50 --json"
|
|
87
|
+
),
|
|
88
|
+
"meta.list-projects": "maxc meta list-projects\nmaxc meta list-projects --json",
|
|
89
|
+
"meta.list-schemas": (
|
|
90
|
+
"maxc meta list-schemas\n"
|
|
91
|
+
"maxc meta list-schemas --project my_proj --json"
|
|
92
|
+
),
|
|
93
|
+
# ── meta semantic ──────────────────────────────────────────────────────
|
|
94
|
+
"meta.semantic": (
|
|
95
|
+
"maxc meta semantic set my_table --desc 'Order facts'\n"
|
|
96
|
+
"maxc meta semantic get my_table\n"
|
|
97
|
+
"maxc meta semantic list-missing"
|
|
98
|
+
),
|
|
99
|
+
"meta.semantic.set": (
|
|
100
|
+
"maxc meta semantic set my_table --desc 'Order facts'\n"
|
|
101
|
+
"maxc meta semantic set my_table --use-cases reporting analytics --sample-questions 'top users by revenue'"
|
|
102
|
+
),
|
|
103
|
+
"meta.semantic.get": (
|
|
104
|
+
"maxc meta semantic get my_table\n"
|
|
105
|
+
"maxc meta semantic get my_table --json"
|
|
106
|
+
),
|
|
107
|
+
"meta.semantic.list-missing": (
|
|
108
|
+
"maxc meta semantic list-missing\n"
|
|
109
|
+
"maxc meta semantic list-missing --json"
|
|
110
|
+
),
|
|
111
|
+
# ── session ────────────────────────────────────────────────────────────
|
|
112
|
+
"session": (
|
|
113
|
+
"maxc session set --project my_proj\n"
|
|
114
|
+
"maxc session show\n"
|
|
115
|
+
"maxc session unset"
|
|
116
|
+
),
|
|
117
|
+
"session.set": (
|
|
118
|
+
"maxc session set --project my_proj\n"
|
|
119
|
+
"maxc session set --project my_proj --schema default"
|
|
120
|
+
),
|
|
121
|
+
"session.show": "maxc session show\nmaxc session show --json",
|
|
122
|
+
"session.unset": "maxc session unset",
|
|
123
|
+
# ── data ───────────────────────────────────────────────────────────────
|
|
124
|
+
"data": (
|
|
125
|
+
"maxc data sample my_table --rows 10\n"
|
|
126
|
+
"maxc data profile my_table\n"
|
|
127
|
+
"maxc data download my_table --output rows.csv"
|
|
128
|
+
),
|
|
129
|
+
"data.sample": (
|
|
130
|
+
"maxc data sample my_table --rows 10\n"
|
|
131
|
+
"maxc data sample my_table --partition \"dt='20260101'\" --columns id,name"
|
|
132
|
+
),
|
|
133
|
+
"data.profile": (
|
|
134
|
+
"maxc data profile my_table\n"
|
|
135
|
+
"maxc data profile my_table --partition \"dt='20260101'\" --json"
|
|
136
|
+
),
|
|
137
|
+
"data.upload": (
|
|
138
|
+
"maxc data upload my_table --file rows.csv\n"
|
|
139
|
+
"maxc data upload my_table --file rows.tsv --delimiter $'\\t' --partition \"dt='20260101'\" --overwrite"
|
|
140
|
+
),
|
|
141
|
+
"data.download": (
|
|
142
|
+
"maxc data download my_table --output rows.csv\n"
|
|
143
|
+
"maxc data download my_table --output rows.csv --columns id,name --limit 1000"
|
|
144
|
+
),
|
|
145
|
+
# ── agent ──────────────────────────────────────────────────────────────
|
|
146
|
+
"agent": (
|
|
147
|
+
"maxc agent context\n"
|
|
148
|
+
"maxc agent skill\n"
|
|
149
|
+
"maxc agent skill install claude-code"
|
|
150
|
+
),
|
|
151
|
+
"agent.context": "maxc agent context\nmaxc agent context --json",
|
|
152
|
+
"agent.skill": "maxc agent skill\nmaxc agent skill --json",
|
|
153
|
+
"agent.skill.install": (
|
|
154
|
+
"maxc agent skill install claude-code\n"
|
|
155
|
+
"maxc agent skill install cursor --invocation aliyun-maxc"
|
|
156
|
+
),
|
|
157
|
+
"agent.skill.update": (
|
|
158
|
+
"maxc agent skill update cursor\n"
|
|
159
|
+
"maxc agent skill update --all"
|
|
160
|
+
),
|
|
161
|
+
"agent.skill.uninstall": "maxc agent skill uninstall cursor",
|
|
162
|
+
"agent.skill.list": "maxc agent skill list --json",
|
|
163
|
+
"agent.skill.diff": "maxc agent skill diff cursor --unified",
|
|
164
|
+
"agent.skill.path": "maxc agent skill path cursor\nmaxc agent skill path --source",
|
|
165
|
+
# ── cache ──────────────────────────────────────────────────────────────
|
|
166
|
+
"cache": (
|
|
167
|
+
"maxc cache build --project my_proj\n"
|
|
168
|
+
"maxc cache status --project my_proj\n"
|
|
169
|
+
"maxc cache clear --project my_proj"
|
|
170
|
+
),
|
|
171
|
+
"cache.build": (
|
|
172
|
+
"maxc cache build --project my_proj\n"
|
|
173
|
+
"maxc cache build --project my_proj --schema default --async"
|
|
174
|
+
),
|
|
175
|
+
"cache.build-status": (
|
|
176
|
+
"maxc cache build-status --project my_proj\n"
|
|
177
|
+
"maxc cache build-status --project my_proj --build-id <id> --json"
|
|
178
|
+
),
|
|
179
|
+
"cache.status": (
|
|
180
|
+
"maxc cache status --project my_proj\n"
|
|
181
|
+
"maxc cache status --project my_proj --schema default --json"
|
|
182
|
+
),
|
|
183
|
+
"cache.clear": (
|
|
184
|
+
"maxc cache clear --project my_proj\n"
|
|
185
|
+
"maxc cache clear --project my_proj --schema default"
|
|
186
|
+
),
|
|
187
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Agent platform registry — single source of truth for SKILL install targets.
|
|
2
|
+
|
|
3
|
+
Each Platform declares where SKILL.md (and optional extra files) live for a
|
|
4
|
+
specific agent product. The MaxCApp skill_install/update/uninstall/list/diff/path
|
|
5
|
+
methods all consult REGISTRY here, so adding a new platform = one entry below
|
|
6
|
+
(no code changes elsewhere).
|
|
7
|
+
|
|
8
|
+
CRITICAL: install_root values must stay byte-equivalent to the legacy
|
|
9
|
+
`_SKILL_PLATFORMS` table in `app.py:3375-3411` — see tests/test_agent_platforms.py
|
|
10
|
+
::test_install_root_matches_legacy_paths.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Callable
|
|
18
|
+
|
|
19
|
+
RenderFn = Callable[[Path, str, str], None]
|
|
20
|
+
# (effective_install_root, cli, cli_module) -> None
|
|
21
|
+
# Callee mkdir + write_text directly; returns nothing.
|
|
22
|
+
# The first arg is the *effective* install root (after --dir override is applied
|
|
23
|
+
# by the caller), not platform.install_root.
|
|
24
|
+
# Args (cli, cli_module) are currently only consumed by SKILL.md template render;
|
|
25
|
+
# extra_files with fixed-literal output (e.g., plugin.json) may ignore them.
|
|
26
|
+
# Keep them in the signature so future extra_files using {{cli}} placeholders
|
|
27
|
+
# don't need a protocol bump.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class ExtraFile:
|
|
32
|
+
"""Non-SKILL file co-installed with the skill (e.g., claude-plugin manifest).
|
|
33
|
+
|
|
34
|
+
`relative_path` is the discovery anchor for list/diff/uninstall — the
|
|
35
|
+
render_fn MUST write its content at `effective_install_root / relative_path`,
|
|
36
|
+
otherwise diff will report "file missing". Enforced by
|
|
37
|
+
test_render_claude_plugin_writes_declared_path.
|
|
38
|
+
"""
|
|
39
|
+
relative_path: str
|
|
40
|
+
render_fn_name: str # name of a callable in this module matching RenderFn
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class Platform:
|
|
45
|
+
name: str
|
|
46
|
+
install_root: Path
|
|
47
|
+
skill_subpath: str | None = None # SKILL contents location relative to install_root;
|
|
48
|
+
# None = write into effective_install_root directly
|
|
49
|
+
# (claude-code uses this — SKILL.md and .claude-plugin/
|
|
50
|
+
# are siblings).
|
|
51
|
+
requires_dir: bool = True
|
|
52
|
+
extra_files: tuple[ExtraFile, ...] = ()
|
|
53
|
+
aliases: tuple[str, ...] = ()
|
|
54
|
+
deprecated_aliases: tuple[str, ...] = ()
|
|
55
|
+
next_step_hint: str = ""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Invocation map: cli front-end name → (cli, cli_module) substituted into
|
|
59
|
+
# SKILL templates. Migrated here from app.py:3363-3372 so the registry is the
|
|
60
|
+
# single source of truth.
|
|
61
|
+
INVOCATIONS: dict[str, dict[str, str]] = {
|
|
62
|
+
"maxc": {
|
|
63
|
+
"cli": "maxc",
|
|
64
|
+
# `python3 -m` (not `python`) matches the legacy _SKILL_INVOCATIONS at
|
|
65
|
+
# app.py:3363-3372 byte-for-byte — changing this re-renders SKILL.md for
|
|
66
|
+
# every existing install and triggers a phantom `agent skill diff`.
|
|
67
|
+
"cli_module": "python3 -m maxc_cli",
|
|
68
|
+
},
|
|
69
|
+
"aliyun-maxc": {
|
|
70
|
+
"cli": "aliyun maxc",
|
|
71
|
+
"cli_module": "aliyun maxc",
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _claude_root() -> Path:
|
|
77
|
+
return Path.home() / ".claude" / "plugins" / "maxc-cli"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _codex_root() -> Path:
|
|
81
|
+
# Lazy — must re-read CODEX_HOME at call time, not module-import time,
|
|
82
|
+
# so the env var can be overridden in tests/CI.
|
|
83
|
+
return (
|
|
84
|
+
Path(os.environ.get("CODEX_HOME", str(Path.home() / ".codex")))
|
|
85
|
+
/ "skills"
|
|
86
|
+
/ "maxcompute-cli-guidance"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _simple_root(dotdir: str) -> Path:
|
|
91
|
+
return Path.home() / dotdir / "skills" / "maxcompute-cli-guidance"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def render_claude_plugin(install_dir: Path, cli: str, cli_module: str) -> None:
|
|
95
|
+
"""Write `.claude-plugin/plugin.json` under `install_dir`.
|
|
96
|
+
|
|
97
|
+
Byte-equivalent to legacy literal at `app.py:3493-3499`. Do NOT reformat
|
|
98
|
+
via json.dumps(indent=…) — that re-flows whitespace and causes
|
|
99
|
+
`agent skill diff` to report a phantom delta against pre-existing installs.
|
|
100
|
+
"""
|
|
101
|
+
meta_dir = install_dir / ".claude-plugin"
|
|
102
|
+
meta_dir.mkdir(parents=True, exist_ok=True)
|
|
103
|
+
(meta_dir / "plugin.json").write_text(
|
|
104
|
+
'{\n "name": "maxc-cli",\n'
|
|
105
|
+
' "description": "MaxCompute/ODPS CLI — query tables, view schema, '
|
|
106
|
+
'search metadata, execute SQL, check partitions, sample data, '
|
|
107
|
+
'track jobs. Install via: pip install maxc-cli",\n'
|
|
108
|
+
' "author": { "name": "maxc-cli contributors" }\n}\n',
|
|
109
|
+
encoding="utf-8",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _build_registry() -> tuple[Platform, ...]:
|
|
114
|
+
"""Build REGISTRY lazily so tests can monkeypatch HOME/CODEX_HOME + reload."""
|
|
115
|
+
return (
|
|
116
|
+
Platform(
|
|
117
|
+
name="claude-code",
|
|
118
|
+
install_root=_claude_root(),
|
|
119
|
+
skill_subpath=None,
|
|
120
|
+
extra_files=(ExtraFile(".claude-plugin/plugin.json", "render_claude_plugin"),),
|
|
121
|
+
next_step_hint="Run /reload-plugins in Claude Code to activate",
|
|
122
|
+
),
|
|
123
|
+
Platform(name="cursor", install_root=_simple_root(".cursor"),
|
|
124
|
+
next_step_hint="Restart Cursor to activate"),
|
|
125
|
+
Platform(name="windsurf", install_root=_simple_root(".windsurf"),
|
|
126
|
+
next_step_hint="Restart Windsurf to activate"),
|
|
127
|
+
Platform(name="codex", install_root=_codex_root(),
|
|
128
|
+
next_step_hint="Restart Codex to activate"),
|
|
129
|
+
Platform(name="qwen", install_root=_simple_root(".qwen"),
|
|
130
|
+
next_step_hint="Restart Qwen to activate"),
|
|
131
|
+
Platform(name="qoder", install_root=_simple_root(".qoder"),
|
|
132
|
+
next_step_hint="Restart Qoder to activate"),
|
|
133
|
+
Platform(name="qoderwork", install_root=_simple_root(".qoderwork"),
|
|
134
|
+
next_step_hint="Restart QoderWork to activate"),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
REGISTRY: tuple[Platform, ...] = _build_registry()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def resolve(name_or_alias: str) -> Platform:
|
|
142
|
+
"""Return the Platform matching name / alias / deprecated_alias.
|
|
143
|
+
|
|
144
|
+
Raises KeyError when no match. Caller is responsible for emitting a
|
|
145
|
+
stderr warning when the input matches `deprecated_aliases`.
|
|
146
|
+
"""
|
|
147
|
+
for p in REGISTRY:
|
|
148
|
+
if name_or_alias == p.name:
|
|
149
|
+
return p
|
|
150
|
+
if name_or_alias in p.aliases:
|
|
151
|
+
return p
|
|
152
|
+
if name_or_alias in p.deprecated_aliases:
|
|
153
|
+
return p
|
|
154
|
+
raise KeyError(f"Unknown agent platform: {name_or_alias!r}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def all_platforms() -> tuple[Platform, ...]:
|
|
158
|
+
return REGISTRY
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def is_deprecated_alias(name: str) -> bool:
|
|
162
|
+
for p in REGISTRY:
|
|
163
|
+
if name in p.deprecated_aliases:
|
|
164
|
+
return True
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def effective_target(platform: Platform, dir_override: Path | None) -> Path:
|
|
169
|
+
"""Resolve where SKILL content for `platform` should be written.
|
|
170
|
+
|
|
171
|
+
effective_target = (--dir override OR platform.install_root) / (skill_subpath or "")
|
|
172
|
+
"""
|
|
173
|
+
root = dir_override if dir_override is not None else platform.install_root
|
|
174
|
+
if platform.skill_subpath:
|
|
175
|
+
return root / platform.skill_subpath
|
|
176
|
+
return root
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def get_render_fn(name: str) -> RenderFn:
|
|
180
|
+
"""Resolve a render_fn name (declared in ExtraFile) into the actual callable.
|
|
181
|
+
|
|
182
|
+
Kept here so caller can `getattr(agent_platforms, name)` without exposing the
|
|
183
|
+
module-internal naming convention to MaxCApp.
|
|
184
|
+
"""
|
|
185
|
+
fn = globals().get(name)
|
|
186
|
+
if not callable(fn):
|
|
187
|
+
raise KeyError(f"render_fn not found in agent_platforms: {name!r}")
|
|
188
|
+
return fn # type: ignore[return-value]
|