maxc-cli 0.3.0__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.3.0 → maxc_cli-0.3.1}/PKG-INFO +5 -5
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/README.md +4 -4
- maxc_cli-0.3.1/pyproject.toml +22 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/setup.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/__main__.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/_samples.py +13 -6
- maxc_cli-0.3.1/src/maxc_cli/agent_platforms.py +188 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/app.py +284 -190
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/auth_providers.py +11 -13
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/__init__.py +1 -2
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/catalog.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/data.py +8 -10
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/job.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/meta.py +8 -8
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/odps.py +4 -5
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/query.py +4 -6
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/cache.py +1 -2
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/cli.py +317 -116
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/config.py +6 -6
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/exceptions.py +10 -2
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/help_format.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/helpers.py +113 -119
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/masking.py +7 -8
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/models.py +16 -9
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/output.py +10 -11
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/setting_parser.py +4 -4
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/SKILL.md +17 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/bootstrap-auth.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/bootstrap-flow.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/command-patterns.md +10 -8
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/json-output-format.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/maxcompute-select-guide.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/migrate-from-odpscmd.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/partition-guide.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/red-lines.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/setup-install.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/sql-common-errors.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/sql-query-patterns.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/references/text2sql-principles.md +2 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/utils.py +1 -2
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/PKG-INFO +5 -5
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/SOURCES.txt +6 -0
- {maxc_cli-0.3.0 → 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.3.0 → maxc_cli-0.3.1}/tests/test_agent_skill_commands_context.py +30 -28
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_backend_data.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_backend_data_serialization.py +26 -27
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_backend_meta.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_cache.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_catalog.py +2 -3
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_catalog_bootstrap.py +4 -3
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_cli_mock.py +32 -18
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_cli_query_parse_and_sanitize.py +9 -11
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_envelope_shape.py +10 -11
- maxc_cli-0.3.1/tests/test_exit_codes.py +107 -0
- {maxc_cli-0.3.0 → 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.0 → maxc_cli-0.3.1}/tests/test_help_format.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_helpers.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_integration.py +4 -2
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_integration_real.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_job_improvements.py +0 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_masking.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_meta_schema_and_partition_cols.py +16 -17
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_phase1_improvements.py +17 -13
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_query_auto_promote.py +4 -3
- maxc_cli-0.3.1/tests/test_skill_cli_consistency.py +75 -0
- maxc_cli-0.3.0/pyproject.toml +0 -7
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/MANIFEST.in +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/scripts/regression_test.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/setup.cfg +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/backend/auth.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/catalog_bootstrap.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_build_release_script.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_cli_arg_validation.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_compat.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_error_translation.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_helpers_csv.py +1 -1
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_pyinstaller_bundle.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_query_result_csv_fallback.py +0 -0
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/tests/test_setting_parser.py +3 -3
- {maxc_cli-0.3.0 → maxc_cli-0.3.1}/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.3.
|
|
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
|
|
@@ -59,7 +59,7 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
59
59
|
| **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
|
|
60
60
|
| **diff** | `schema`, `partition`, `data` | 表结构/分区/数据对比 |
|
|
61
61
|
| **cache** | `build`, `build-status`, `status`, `clear` | 元数据缓存管理 |
|
|
62
|
-
| **agent** | `context`, `skill`, `
|
|
62
|
+
| **agent** | `context`, `skill install`, `skill list` | Agent 集成与 SKILL 安装 |
|
|
63
63
|
|
|
64
64
|
所有命令支持 `--json` 输出 Envelope v2.0 结构化响应。支持 `--format json|markdown|brief`(全局标志)。
|
|
65
65
|
|
|
@@ -69,16 +69,16 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
69
69
|
|
|
70
70
|
SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc-cli`。
|
|
71
71
|
|
|
72
|
-
### 方式 2:install
|
|
72
|
+
### 方式 2:agent skill install(内网兜底)
|
|
73
73
|
|
|
74
74
|
先 pip install,再一键注册到 Agent 平台:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
pip install maxc-cli
|
|
78
|
-
maxc agent
|
|
78
|
+
maxc agent skill install claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
`install
|
|
81
|
+
`agent skill install` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
|
|
82
82
|
|
|
83
83
|
### preflight 检查
|
|
84
84
|
|
|
@@ -37,7 +37,7 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
37
37
|
| **session** | `set`, `show`, `unset` | 项目/Schema 切换 |
|
|
38
38
|
| **diff** | `schema`, `partition`, `data` | 表结构/分区/数据对比 |
|
|
39
39
|
| **cache** | `build`, `build-status`, `status`, `clear` | 元数据缓存管理 |
|
|
40
|
-
| **agent** | `context`, `skill`, `
|
|
40
|
+
| **agent** | `context`, `skill install`, `skill list` | Agent 集成与 SKILL 安装 |
|
|
41
41
|
|
|
42
42
|
所有命令支持 `--json` 输出 Envelope v2.0 结构化响应。支持 `--format json|markdown|brief`(全局标志)。
|
|
43
43
|
|
|
@@ -47,16 +47,16 @@ maxc query "SELECT * FROM schema.table WHERE ds='20260415'" --json
|
|
|
47
47
|
|
|
48
48
|
SKILL HUB 安装 SKILL → Agent 读 SKILL.md → Agent 自己 `pip install maxc-cli`。
|
|
49
49
|
|
|
50
|
-
### 方式 2:install
|
|
50
|
+
### 方式 2:agent skill install(内网兜底)
|
|
51
51
|
|
|
52
52
|
先 pip install,再一键注册到 Agent 平台:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
pip install maxc-cli
|
|
56
|
-
maxc agent
|
|
56
|
+
maxc agent skill install claude-code # 或 cursor / windsurf / codex / qwen / qoder / qoderwork
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
`install
|
|
59
|
+
`agent skill install` 从独立仓库 [maxcompute-cli-guidance](https://gitlab.alibaba-inc.com/aone-open-skill/maxcompute-cli-guidance) 获取 SKILL.md + references/ 并安装到目标平台目录。
|
|
60
60
|
|
|
61
61
|
### preflight 检查
|
|
62
62
|
|
|
@@ -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.3.
|
|
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",
|
|
@@ -6,7 +6,6 @@ quoting style they'd actually type.
|
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
SAMPLES: dict[str, str] = {
|
|
11
10
|
"__top__": (
|
|
12
11
|
"maxc auth login\n"
|
|
@@ -147,14 +146,22 @@ SAMPLES: dict[str, str] = {
|
|
|
147
146
|
"agent": (
|
|
148
147
|
"maxc agent context\n"
|
|
149
148
|
"maxc agent skill\n"
|
|
150
|
-
"maxc agent
|
|
149
|
+
"maxc agent skill install claude-code"
|
|
151
150
|
),
|
|
152
151
|
"agent.context": "maxc agent context\nmaxc agent context --json",
|
|
153
152
|
"agent.skill": "maxc agent skill\nmaxc agent skill --json",
|
|
154
|
-
"agent.install
|
|
155
|
-
"maxc agent
|
|
156
|
-
"maxc agent
|
|
157
|
-
),
|
|
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",
|
|
158
165
|
# ── cache ──────────────────────────────────────────────────────────────
|
|
159
166
|
"cache": (
|
|
160
167
|
"maxc cache build --project my_proj\n"
|
|
@@ -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]
|