papycli 0.16.1__tar.gz → 0.16.2__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.
- {papycli-0.16.1 → papycli-0.16.2}/.github/workflows/docs.yml +1 -1
- papycli-0.16.2/.release-please-manifest.json +3 -0
- {papycli-0.16.1 → papycli-0.16.2}/CHANGELOG.md +7 -0
- {papycli-0.16.1 → papycli-0.16.2}/PKG-INFO +1 -1
- {papycli-0.16.1 → papycli-0.16.2}/mkdocs.yml +2 -1
- {papycli-0.16.1 → papycli-0.16.2}/pyproject.toml +1 -1
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/api_call.py +3 -2
- papycli-0.16.1/.release-please-manifest.json +0 -3
- papycli-0.16.1/design/2026-03-27-completion-static-script.md +0 -664
- papycli-0.16.1/design/2026-03-28-claude-md-simplification-plan.md +0 -188
- papycli-0.16.1/design/2026-03-28-claude-md-simplification.md +0 -100
- {papycli-0.16.1 → papycli-0.16.2}/.claude/rules/cli-spec.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.claude/rules/data-format.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.claude/rules/workflow.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.claude/settings.json +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.github/copilot-instructions.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.github/workflows/copilot-review.yml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.github/workflows/release-please.yml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.gitignore +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.pre-commit-config.yaml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/.python-version +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/CLAUDE.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/LICENSE +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/README.ja.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/README.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/design/design_doc.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/plans/2026-04-01-config-add-upgrade.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/plans/2026-04-02-dotenv-autoload.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/specs/2026-04-01-config-add-upgrade-design.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/specs/2026-04-02-dotenv-autoload-design.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/petstore/docker-compose.yml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/petstore/petstore-oas3.json +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/request_filter/README.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/request_filter/pyproject.toml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/request_filter/src/papycli_debug_filter/__init__.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/response_filter/README.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/response_filter/pyproject.toml +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/examples/response_filter/src/papycli_debug_response_filter/__init__.py +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/development.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/index.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/installation.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/development.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/index.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/installation.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/plugins.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/quickstart.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/ja/reference.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/plugins.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/quickstart.md +0 -0
- {papycli-0.16.1/docs → papycli-0.16.2/manual}/reference.md +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/release-please-config.json +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/__init__.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/checker.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/completion.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/config.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/filters.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/i18n.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/init_cmd.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/main.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/response_checker.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/spec_loader.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/src/papycli/summary.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/integration/__init__.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/integration/conftest.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/integration/test_integration.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/conftest.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_api_call.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_checker.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_completion.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_config.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_filters.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_i18n.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_init_cmd.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_main.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_response_checker.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_spec_loader.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/tests/unittest/test_summary.py +0 -0
- {papycli-0.16.1 → papycli-0.16.2}/uv.lock +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.2](https://github.com/tmonj1/papycli/compare/v0.16.1...v0.16.2) (2026-04-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* api_call.py の mypy エラーを修正 (ResponseContext | None の型不整合) ([#176](https://github.com/tmonj1/papycli/issues/176)) ([9b9917c](https://github.com/tmonj1/papycli/commit/9b9917c2f8d3ea8b660d5618b15c1dfa0fa28c15)), closes [#175](https://github.com/tmonj1/papycli/issues/175)
|
|
9
|
+
|
|
3
10
|
## [0.16.1](https://github.com/tmonj1/papycli/compare/v0.16.0...v0.16.1) (2026-04-08)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -3,7 +3,8 @@ site_description: Python CLI for OpenAPI 3.0 REST APIs
|
|
|
3
3
|
site_url: https://tmonj1.github.io/papycli/
|
|
4
4
|
repo_url: https://github.com/tmonj1/papycli
|
|
5
5
|
repo_name: tmonj1/papycli
|
|
6
|
-
|
|
6
|
+
docs_dir: manual
|
|
7
|
+
edit_uri: edit/main/manual/
|
|
7
8
|
|
|
8
9
|
theme:
|
|
9
10
|
name: material
|
|
@@ -451,10 +451,11 @@ def call_api(
|
|
|
451
451
|
request_body=ctx.body,
|
|
452
452
|
schema=resp_schema,
|
|
453
453
|
)
|
|
454
|
-
|
|
455
|
-
if
|
|
454
|
+
filtered_ctx = apply_response_filters(resp_ctx, response_filters)
|
|
455
|
+
if filtered_ctx is None:
|
|
456
456
|
# フィルターが None を返してチェーンを中断した。出力を抑制するため None を返す。
|
|
457
457
|
return None
|
|
458
|
+
resp_ctx = filtered_ctx
|
|
458
459
|
|
|
459
460
|
# フィルターがフィールドを変更した場合、resp に反映する。
|
|
460
461
|
# ボディ: 値の等価比較で変更を検出し、_content・encoding・Content-Type を更新する。
|
|
@@ -1,664 +0,0 @@
|
|
|
1
|
-
# Shell Completion 高速化(静的スクリプト生成)実装計画
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
-
|
|
5
|
-
**Goal:** `config completion-script` が補完データを埋め込んだ静的 bash/zsh スクリプトを出力するようにし、Tab 補完時の Python プロセス起動をゼロにする。
|
|
6
|
-
|
|
7
|
-
**Architecture:** `completion.py` に `generate_static_script(shell, cmd_name, apidef, api_names)` を追加する。この関数はリソースパス・パラメータ名・enum 値を shell の case 文として埋め込んだ完全な補完スクリプトを返す。`main.py` の `cmd_config_completion_script` がこの関数を呼ぶよう変更する。ユーザーの利用方法(`eval "$(papycli config completion-script bash)"`)は変わらない。既存の `generate_script` と `_complete` サブコマンドは後方互換のため残す。
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** Python 3.12, bash 3.x+, zsh 5.x+, pytest, ruff, mypy
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## ファイル変更マップ
|
|
14
|
-
|
|
15
|
-
| ファイル | 変更種別 | 内容 |
|
|
16
|
-
|---|---|---|
|
|
17
|
-
| `src/papycli/completion.py` | 追加 | 静的テンプレート・データ構築ヘルパー・`generate_static_script` |
|
|
18
|
-
| `src/papycli/main.py` | 変更 | `cmd_config_completion_script` が `generate_static_script` を呼ぶ |
|
|
19
|
-
| `tests/unittest/test_completion.py` | 追加 | `TestGenerateStaticScript` クラス |
|
|
20
|
-
| `tests/unittest/test_main.py` | 追加 | `TestCompletionScriptStatic` クラス |
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Task 1: `generate_static_script` と補完データ構築ヘルパーの追加
|
|
25
|
-
|
|
26
|
-
**Files:**
|
|
27
|
-
- Modify: `src/papycli/completion.py`
|
|
28
|
-
- Modify: `tests/unittest/test_completion.py`
|
|
29
|
-
|
|
30
|
-
- [ ] **Step 1-1: 失敗するテストを書く**
|
|
31
|
-
|
|
32
|
-
`tests/unittest/test_completion.py` の末尾にクラスを追加する。ファイル先頭の既存 import ブロックに `generate_static_script` を追加する(新しい `from` 行は追加しない)。
|
|
33
|
-
|
|
34
|
-
```python
|
|
35
|
-
# 既存 import ブロックに追加(既存行は省略):
|
|
36
|
-
from papycli.completion import (
|
|
37
|
-
CONFIG_SUBCOMMANDS,
|
|
38
|
-
TOP_LEVEL_COMMANDS,
|
|
39
|
-
_used_param_names,
|
|
40
|
-
completions_for_context,
|
|
41
|
-
generate_script,
|
|
42
|
-
generate_static_script, # ← 追加
|
|
43
|
-
)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
ファイル末尾に以下のクラスを追加する:
|
|
47
|
-
|
|
48
|
-
```python
|
|
49
|
-
class TestGenerateStaticScript:
|
|
50
|
-
def test_bash_contains_resources(self) -> None:
|
|
51
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore"])
|
|
52
|
-
assert "/pet/findByStatus" in script
|
|
53
|
-
assert "/pet/{petId}" in script
|
|
54
|
-
|
|
55
|
-
def test_bash_contains_api_names(self) -> None:
|
|
56
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore", "myapi"])
|
|
57
|
-
assert "petstore" in script
|
|
58
|
-
assert "myapi" in script
|
|
59
|
-
|
|
60
|
-
def test_bash_contains_enum_values(self) -> None:
|
|
61
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore"])
|
|
62
|
-
assert "available" in script
|
|
63
|
-
assert "pending" in script
|
|
64
|
-
assert "sold" in script
|
|
65
|
-
|
|
66
|
-
def test_bash_contains_required_param_marker(self) -> None:
|
|
67
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore"])
|
|
68
|
-
# POST /pet の name は required → name* が含まれる
|
|
69
|
-
assert "name*" in script
|
|
70
|
-
|
|
71
|
-
def test_bash_has_completion_function_and_binding(self) -> None:
|
|
72
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore"])
|
|
73
|
-
assert "_papycli_completion()" in script
|
|
74
|
-
assert "complete -o nospace -F _papycli_completion papycli" in script
|
|
75
|
-
|
|
76
|
-
def test_bash_no_python_call(self) -> None:
|
|
77
|
-
script = generate_static_script("bash", "papycli", APIDEF, ["petstore"])
|
|
78
|
-
assert "_complete" not in script
|
|
79
|
-
|
|
80
|
-
def test_bash_none_apidef_generates_valid_script(self) -> None:
|
|
81
|
-
script = generate_static_script("bash", "papycli", None, None)
|
|
82
|
-
assert "_papycli_completion()" in script
|
|
83
|
-
assert "get post put patch delete" in script
|
|
84
|
-
assert "_complete" not in script
|
|
85
|
-
|
|
86
|
-
def test_bash_custom_cmd_name(self) -> None:
|
|
87
|
-
script = generate_static_script("bash", "petcli", APIDEF, ["petstore"])
|
|
88
|
-
assert "_petcli_completion()" in script
|
|
89
|
-
assert "complete -o nospace -F _petcli_completion petcli" in script
|
|
90
|
-
|
|
91
|
-
def test_zsh_has_compdef(self) -> None:
|
|
92
|
-
script = generate_static_script("zsh", "papycli", APIDEF, ["petstore"])
|
|
93
|
-
assert "compdef _papycli papycli" in script
|
|
94
|
-
assert "/pet/findByStatus" in script
|
|
95
|
-
|
|
96
|
-
def test_zsh_no_python_call(self) -> None:
|
|
97
|
-
script = generate_static_script("zsh", "papycli", APIDEF, ["petstore"])
|
|
98
|
-
assert "_complete" not in script
|
|
99
|
-
|
|
100
|
-
def test_zsh_contains_enum_values(self) -> None:
|
|
101
|
-
script = generate_static_script("zsh", "papycli", APIDEF, ["petstore"])
|
|
102
|
-
assert "available" in script
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- [ ] **Step 1-2: テスト実行(失敗確認)**
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
uv run pytest tests/unittest/test_completion.py::TestGenerateStaticScript -v 2>&1 | head -20
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Expected: `ImportError` または `cannot import name 'generate_static_script'`
|
|
112
|
-
|
|
113
|
-
- [ ] **Step 1-3: `completion.py` に実装を追加する**
|
|
114
|
-
|
|
115
|
-
`src/papycli/completion.py` のファイル末尾(`get_completions` 関数の後)に以下を追加する。
|
|
116
|
-
|
|
117
|
-
```python
|
|
118
|
-
# ---------------------------------------------------------------------------
|
|
119
|
-
# 静的補完スクリプト生成
|
|
120
|
-
# ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
_STATIC_BASH_TEMPLATE = """\
|
|
123
|
-
_@@SAFENAME@@_completion() {
|
|
124
|
-
local cur prev pprev
|
|
125
|
-
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
126
|
-
prev=""
|
|
127
|
-
pprev=""
|
|
128
|
-
[[ $COMP_CWORD -ge 1 ]] && prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
129
|
-
[[ $COMP_CWORD -ge 2 ]] && pprev="${COMP_WORDS[COMP_CWORD-2]}"
|
|
130
|
-
|
|
131
|
-
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
|
132
|
-
COMPREPLY=($(compgen -W "get post put patch delete config spec summary" -- "$cur"))
|
|
133
|
-
return
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
local cmd="${COMP_WORDS[1]}"
|
|
137
|
-
|
|
138
|
-
if [[ "$cmd" == "config" ]]; then
|
|
139
|
-
case ${COMP_CWORD} in
|
|
140
|
-
2) COMPREPLY=($(compgen -W "add alias completion-script list log remove use" -- "$cur")) ;;
|
|
141
|
-
3) case "${COMP_WORDS[2]}" in
|
|
142
|
-
remove|use) COMPREPLY=($(compgen -W "@@API_NAMES@@" -- "$cur")) ;;
|
|
143
|
-
add) COMPREPLY=($(compgen -f -- "$cur"))
|
|
144
|
-
compopt -o filenames 2>/dev/null ;;
|
|
145
|
-
esac ;;
|
|
146
|
-
esac
|
|
147
|
-
return
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
if [[ "$cmd" == "summary" && ${COMP_CWORD} -eq 2 ]]; then
|
|
151
|
-
COMPREPLY=($(compgen -W "--csv @@ALL_RESOURCES@@" -- "$cur"))
|
|
152
|
-
return
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
if [[ "$cmd" == "spec" ]]; then
|
|
156
|
-
if [[ ${COMP_CWORD} -eq 2 ]]; then
|
|
157
|
-
COMPREPLY=($(compgen -W "--full @@ALL_RESOURCES@@" -- "$cur"))
|
|
158
|
-
elif [[ ${COMP_CWORD} -eq 3 && "${COMP_WORDS[2]}" == "--full" ]]; then
|
|
159
|
-
COMPREPLY=($(compgen -W "@@ALL_RESOURCES@@" -- "$cur"))
|
|
160
|
-
fi
|
|
161
|
-
return
|
|
162
|
-
fi
|
|
163
|
-
|
|
164
|
-
case "$cmd" in get|post|put|patch|delete) ;; *) return ;; esac
|
|
165
|
-
|
|
166
|
-
if [[ ${COMP_CWORD} -eq 2 ]]; then
|
|
167
|
-
case "$cmd" in
|
|
168
|
-
@@METHOD_RESOURCE_CASES@@
|
|
169
|
-
esac
|
|
170
|
-
return
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
local resource="${COMP_WORDS[2]}"
|
|
174
|
-
local ctx="${cmd}:${resource}"
|
|
175
|
-
|
|
176
|
-
if [[ "$prev" == "-q" ]]; then
|
|
177
|
-
case "$ctx" in
|
|
178
|
-
@@Q_PARAM_CASES@@
|
|
179
|
-
esac
|
|
180
|
-
return
|
|
181
|
-
fi
|
|
182
|
-
|
|
183
|
-
if [[ "$pprev" == "-q" ]]; then
|
|
184
|
-
local pname="${prev%\\*}"
|
|
185
|
-
case "${ctx}:${pname}" in
|
|
186
|
-
@@Q_ENUM_CASES@@
|
|
187
|
-
esac
|
|
188
|
-
return
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
if [[ "$prev" == "-p" ]]; then
|
|
192
|
-
case "$ctx" in
|
|
193
|
-
@@P_PARAM_CASES@@
|
|
194
|
-
esac
|
|
195
|
-
return
|
|
196
|
-
fi
|
|
197
|
-
|
|
198
|
-
if [[ "$pprev" == "-p" ]]; then
|
|
199
|
-
local pname="${prev%\\*}"
|
|
200
|
-
case "${ctx}:${pname}" in
|
|
201
|
-
@@P_ENUM_CASES@@
|
|
202
|
-
esac
|
|
203
|
-
return
|
|
204
|
-
fi
|
|
205
|
-
|
|
206
|
-
COMPREPLY=($(compgen -W "-q -p -d -H --summary -v --verbose --check --check-strict --response-check" -- "$cur"))
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
complete -o nospace -F _@@SAFENAME@@_completion @@CMDNAME@@
|
|
210
|
-
"""
|
|
211
|
-
|
|
212
|
-
_STATIC_ZSH_TEMPLATE = """\
|
|
213
|
-
_@@SAFENAME@@() {
|
|
214
|
-
local cur="${words[CURRENT]}"
|
|
215
|
-
local prev="${words[CURRENT-1]:-}"
|
|
216
|
-
local pprev="${words[CURRENT-2]:-}"
|
|
217
|
-
local cword=$((CURRENT - 1))
|
|
218
|
-
local -a _c
|
|
219
|
-
|
|
220
|
-
if [[ $cword -eq 1 ]]; then
|
|
221
|
-
_c=(get post put patch delete config spec summary)
|
|
222
|
-
_describe 'command' _c && return
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
local cmd="${words[2]}"
|
|
226
|
-
|
|
227
|
-
if [[ "$cmd" == "config" ]]; then
|
|
228
|
-
case $cword in
|
|
229
|
-
2) _c=(add alias completion-script list log remove use)
|
|
230
|
-
_describe 'subcommand' _c ;;
|
|
231
|
-
3) case "${words[3]}" in
|
|
232
|
-
remove|use) _c=(@@API_NAMES_ZSH@@); _describe 'api' _c ;;
|
|
233
|
-
add) _files ;;
|
|
234
|
-
esac ;;
|
|
235
|
-
esac
|
|
236
|
-
return
|
|
237
|
-
fi
|
|
238
|
-
|
|
239
|
-
if [[ "$cmd" == "summary" && $cword -eq 2 ]]; then
|
|
240
|
-
_c=(--csv @@ALL_RESOURCES_ZSH@@)
|
|
241
|
-
_describe 'resource' _c
|
|
242
|
-
return
|
|
243
|
-
fi
|
|
244
|
-
|
|
245
|
-
if [[ "$cmd" == "spec" ]]; then
|
|
246
|
-
if [[ $cword -eq 2 ]]; then
|
|
247
|
-
_c=(--full @@ALL_RESOURCES_ZSH@@)
|
|
248
|
-
_describe 'resource' _c
|
|
249
|
-
elif [[ $cword -eq 3 && "${words[3]}" == "--full" ]]; then
|
|
250
|
-
_c=(@@ALL_RESOURCES_ZSH@@)
|
|
251
|
-
_describe 'resource' _c
|
|
252
|
-
fi
|
|
253
|
-
return
|
|
254
|
-
fi
|
|
255
|
-
|
|
256
|
-
case "$cmd" in get|post|put|patch|delete) ;; *) return ;; esac
|
|
257
|
-
|
|
258
|
-
if [[ $cword -eq 2 ]]; then
|
|
259
|
-
case "$cmd" in
|
|
260
|
-
@@ZSH_METHOD_RESOURCE_CASES@@
|
|
261
|
-
esac
|
|
262
|
-
return
|
|
263
|
-
fi
|
|
264
|
-
|
|
265
|
-
local resource="${words[3]}"
|
|
266
|
-
local ctx="${cmd}:${resource}"
|
|
267
|
-
|
|
268
|
-
if [[ "$prev" == "-q" ]]; then
|
|
269
|
-
case "$ctx" in
|
|
270
|
-
@@ZSH_Q_PARAM_CASES@@
|
|
271
|
-
esac
|
|
272
|
-
return
|
|
273
|
-
fi
|
|
274
|
-
|
|
275
|
-
if [[ "$pprev" == "-q" ]]; then
|
|
276
|
-
local pname="${prev%\\*}"
|
|
277
|
-
case "${ctx}:${pname}" in
|
|
278
|
-
@@ZSH_Q_ENUM_CASES@@
|
|
279
|
-
esac
|
|
280
|
-
return
|
|
281
|
-
fi
|
|
282
|
-
|
|
283
|
-
if [[ "$prev" == "-p" ]]; then
|
|
284
|
-
case "$ctx" in
|
|
285
|
-
@@ZSH_P_PARAM_CASES@@
|
|
286
|
-
esac
|
|
287
|
-
return
|
|
288
|
-
fi
|
|
289
|
-
|
|
290
|
-
if [[ "$pprev" == "-p" ]]; then
|
|
291
|
-
local pname="${prev%\\*}"
|
|
292
|
-
case "${ctx}:${pname}" in
|
|
293
|
-
@@ZSH_P_ENUM_CASES@@
|
|
294
|
-
esac
|
|
295
|
-
return
|
|
296
|
-
fi
|
|
297
|
-
|
|
298
|
-
_c=(-q -p -d -H --summary -v --verbose --check --check-strict --response-check)
|
|
299
|
-
_describe 'option' _c
|
|
300
|
-
}
|
|
301
|
-
compdef _@@SAFENAME@@ @@CMDNAME@@
|
|
302
|
-
"""
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def _shell_word_list(items: list[str]) -> str:
|
|
306
|
-
"""スペース区切りの単語列をダブルクォートで囲んで返す(bash compgen -W 用)。"""
|
|
307
|
-
escaped = " ".join(w.replace("\\", "\\\\").replace('"', '\\"') for w in items)
|
|
308
|
-
return f'"{escaped}"'
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def _zsh_array_elems(items: list[str]) -> str:
|
|
312
|
-
"""zsh 配列リテラルの要素部分(括弧なし)を返す。"""
|
|
313
|
-
return " ".join(f'"{w}"' for w in items)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def _bash_method_resource_cases(apidef: dict[str, Any]) -> str:
|
|
317
|
-
lines: list[str] = []
|
|
318
|
-
for method in ("get", "post", "put", "patch", "delete"):
|
|
319
|
-
resources = sorted(
|
|
320
|
-
p for p, ops in apidef.items() if any(o["method"] == method for o in ops)
|
|
321
|
-
)
|
|
322
|
-
if resources:
|
|
323
|
-
lines.append(
|
|
324
|
-
f' {method}) COMPREPLY=($(compgen -W {_shell_word_list(resources)} -- "$cur")) ;;'
|
|
325
|
-
)
|
|
326
|
-
return "\n".join(lines)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def _build_param_names(params: list[dict[str, Any]]) -> list[str]:
|
|
330
|
-
"""required パラメータに * を付けてリストを返す。"""
|
|
331
|
-
return (
|
|
332
|
-
[p["name"] + "*" for p in params if p.get("required")]
|
|
333
|
-
+ [p["name"] for p in params if not p.get("required")]
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def _bash_param_cases(apidef: dict[str, Any], kind: str) -> str:
|
|
338
|
-
key = "query_parameters" if kind == "query" else "post_parameters"
|
|
339
|
-
lines: list[str] = []
|
|
340
|
-
for path in sorted(apidef):
|
|
341
|
-
for op in apidef[path]:
|
|
342
|
-
params = op.get(key, [])
|
|
343
|
-
if not params:
|
|
344
|
-
continue
|
|
345
|
-
method = op["method"]
|
|
346
|
-
names = _build_param_names(params)
|
|
347
|
-
lines.append(
|
|
348
|
-
f' "{method}:{path}") COMPREPLY=($(compgen -W {_shell_word_list(names)} -- "$cur")) ;;'
|
|
349
|
-
)
|
|
350
|
-
return "\n".join(lines)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
def _bash_enum_cases(apidef: dict[str, Any], kind: str) -> str:
|
|
354
|
-
key = "query_parameters" if kind == "query" else "post_parameters"
|
|
355
|
-
lines: list[str] = []
|
|
356
|
-
for path in sorted(apidef):
|
|
357
|
-
for op in apidef[path]:
|
|
358
|
-
method = op["method"]
|
|
359
|
-
for p in op.get(key, []):
|
|
360
|
-
if "enum" not in p:
|
|
361
|
-
continue
|
|
362
|
-
vals = [str(v) for v in p["enum"]]
|
|
363
|
-
lines.append(
|
|
364
|
-
f' "{method}:{path}:{p["name"]}") COMPREPLY=($(compgen -W {_shell_word_list(vals)} -- "$cur")) ;;'
|
|
365
|
-
)
|
|
366
|
-
return "\n".join(lines)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
def _zsh_method_resource_cases(apidef: dict[str, Any]) -> str:
|
|
370
|
-
lines: list[str] = []
|
|
371
|
-
for method in ("get", "post", "put", "patch", "delete"):
|
|
372
|
-
resources = sorted(
|
|
373
|
-
p for p, ops in apidef.items() if any(o["method"] == method for o in ops)
|
|
374
|
-
)
|
|
375
|
-
if resources:
|
|
376
|
-
lines.append(
|
|
377
|
-
f" {method}) _c=({_zsh_array_elems(resources)}); _describe 'resource' _c ;;"
|
|
378
|
-
)
|
|
379
|
-
return "\n".join(lines)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
def _zsh_param_cases(apidef: dict[str, Any], kind: str) -> str:
|
|
383
|
-
key = "query_parameters" if kind == "query" else "post_parameters"
|
|
384
|
-
lines: list[str] = []
|
|
385
|
-
for path in sorted(apidef):
|
|
386
|
-
for op in apidef[path]:
|
|
387
|
-
params = op.get(key, [])
|
|
388
|
-
if not params:
|
|
389
|
-
continue
|
|
390
|
-
method = op["method"]
|
|
391
|
-
names = _build_param_names(params)
|
|
392
|
-
lines.append(
|
|
393
|
-
f' "{method}:{path}") _c=({_zsh_array_elems(names)}); _describe \'\' _c ;;'
|
|
394
|
-
)
|
|
395
|
-
return "\n".join(lines)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
def _zsh_enum_cases(apidef: dict[str, Any], kind: str) -> str:
|
|
399
|
-
key = "query_parameters" if kind == "query" else "post_parameters"
|
|
400
|
-
lines: list[str] = []
|
|
401
|
-
for path in sorted(apidef):
|
|
402
|
-
for op in apidef[path]:
|
|
403
|
-
method = op["method"]
|
|
404
|
-
for p in op.get(key, []):
|
|
405
|
-
if "enum" not in p:
|
|
406
|
-
continue
|
|
407
|
-
vals = [str(v) for v in p["enum"]]
|
|
408
|
-
lines.append(
|
|
409
|
-
f' "{method}:{path}:{p["name"]}") _c=({_zsh_array_elems(vals)}); _describe \'\' _c ;;'
|
|
410
|
-
)
|
|
411
|
-
return "\n".join(lines)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
def generate_static_script(
|
|
415
|
-
shell: str,
|
|
416
|
-
cmd_name: str,
|
|
417
|
-
apidef: dict[str, Any] | None,
|
|
418
|
-
api_names: list[str] | None,
|
|
419
|
-
) -> str:
|
|
420
|
-
"""補完データ埋め込み済みの静的シェル補完スクリプトを返す。
|
|
421
|
-
|
|
422
|
-
Args:
|
|
423
|
-
shell: "bash" または "zsh"
|
|
424
|
-
cmd_name: 補完対象のコマンド名
|
|
425
|
-
apidef: API 定義 dict。None の場合は API 固有の補完候補なし。
|
|
426
|
-
api_names: 登録済み API 名リスト。None の場合は空。
|
|
427
|
-
|
|
428
|
-
Raises:
|
|
429
|
-
ValueError: cmd_name に安全でない文字が含まれる場合。
|
|
430
|
-
"""
|
|
431
|
-
cmd_name = Path(cmd_name).stem
|
|
432
|
-
if not _SAFE_CMD_RE.match(cmd_name):
|
|
433
|
-
raise ValueError(
|
|
434
|
-
f"Invalid command name '{cmd_name}': "
|
|
435
|
-
"must start with a letter or digit, and contain only letters, digits, hyphens, and underscores."
|
|
436
|
-
)
|
|
437
|
-
safe = cmd_name.replace("-", "_")
|
|
438
|
-
names = api_names or []
|
|
439
|
-
adef = apidef or {}
|
|
440
|
-
all_resources = sorted(adef.keys())
|
|
441
|
-
|
|
442
|
-
if shell == "bash":
|
|
443
|
-
script = _STATIC_BASH_TEMPLATE
|
|
444
|
-
script = script.replace("@@CMDNAME@@", cmd_name)
|
|
445
|
-
script = script.replace("@@SAFENAME@@", safe)
|
|
446
|
-
script = script.replace("@@API_NAMES@@", " ".join(names))
|
|
447
|
-
script = script.replace("@@ALL_RESOURCES@@", " ".join(all_resources))
|
|
448
|
-
script = script.replace("@@METHOD_RESOURCE_CASES@@", _bash_method_resource_cases(adef))
|
|
449
|
-
script = script.replace("@@Q_PARAM_CASES@@", _bash_param_cases(adef, "query"))
|
|
450
|
-
script = script.replace("@@Q_ENUM_CASES@@", _bash_enum_cases(adef, "query"))
|
|
451
|
-
script = script.replace("@@P_PARAM_CASES@@", _bash_param_cases(adef, "body"))
|
|
452
|
-
script = script.replace("@@P_ENUM_CASES@@", _bash_enum_cases(adef, "body"))
|
|
453
|
-
return script
|
|
454
|
-
|
|
455
|
-
# zsh
|
|
456
|
-
script = _STATIC_ZSH_TEMPLATE
|
|
457
|
-
script = script.replace("@@CMDNAME@@", cmd_name)
|
|
458
|
-
script = script.replace("@@SAFENAME@@", safe)
|
|
459
|
-
script = script.replace("@@API_NAMES_ZSH@@", _zsh_array_elems(names))
|
|
460
|
-
script = script.replace("@@ALL_RESOURCES_ZSH@@", _zsh_array_elems(all_resources))
|
|
461
|
-
script = script.replace("@@ZSH_METHOD_RESOURCE_CASES@@", _zsh_method_resource_cases(adef))
|
|
462
|
-
script = script.replace("@@ZSH_Q_PARAM_CASES@@", _zsh_param_cases(adef, "query"))
|
|
463
|
-
script = script.replace("@@ZSH_Q_ENUM_CASES@@", _zsh_enum_cases(adef, "query"))
|
|
464
|
-
script = script.replace("@@ZSH_P_PARAM_CASES@@", _zsh_param_cases(adef, "body"))
|
|
465
|
-
script = script.replace("@@ZSH_P_ENUM_CASES@@", _zsh_enum_cases(adef, "body"))
|
|
466
|
-
return script
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
- [ ] **Step 1-4: テスト実行(成功確認)**
|
|
470
|
-
|
|
471
|
-
```bash
|
|
472
|
-
uv run pytest tests/unittest/test_completion.py::TestGenerateStaticScript -v
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
Expected: 11 tests PASS
|
|
476
|
-
|
|
477
|
-
- [ ] **Step 1-5: ruff + mypy チェック**
|
|
478
|
-
|
|
479
|
-
```bash
|
|
480
|
-
uv run ruff check src/papycli/completion.py && uv run mypy src/papycli/completion.py
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
Expected: no errors / warnings
|
|
484
|
-
|
|
485
|
-
- [ ] **Step 1-6: コミット**
|
|
486
|
-
|
|
487
|
-
```bash
|
|
488
|
-
git add src/papycli/completion.py tests/unittest/test_completion.py
|
|
489
|
-
git commit -m "feat: add generate_static_script for Python-free shell completion"
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
---
|
|
493
|
-
|
|
494
|
-
## Task 2: `cmd_config_completion_script` を静的スクリプト生成に切り替える
|
|
495
|
-
|
|
496
|
-
**Files:**
|
|
497
|
-
- Modify: `src/papycli/main.py`
|
|
498
|
-
- Modify: `tests/unittest/test_main.py`
|
|
499
|
-
|
|
500
|
-
- [ ] **Step 2-1: 失敗するテストを書く**
|
|
501
|
-
|
|
502
|
-
`tests/unittest/test_main.py` の末尾に追加する。
|
|
503
|
-
|
|
504
|
-
```python
|
|
505
|
-
class TestCompletionScriptStatic:
|
|
506
|
-
def test_bash_no_python_call(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
507
|
-
"""生成スクリプトに _complete 呼び出しが含まれないこと。"""
|
|
508
|
-
monkeypatch.setenv("PAPYCLI_CONF_DIR", str(tmp_path))
|
|
509
|
-
runner = CliRunner()
|
|
510
|
-
result = runner.invoke(cli, ["config", "completion-script", "bash"])
|
|
511
|
-
assert result.exit_code == 0
|
|
512
|
-
assert "_complete" not in result.output
|
|
513
|
-
assert "_papycli_completion()" in result.output
|
|
514
|
-
|
|
515
|
-
def test_zsh_no_python_call(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
516
|
-
monkeypatch.setenv("PAPYCLI_CONF_DIR", str(tmp_path))
|
|
517
|
-
runner = CliRunner()
|
|
518
|
-
result = runner.invoke(cli, ["config", "completion-script", "zsh"])
|
|
519
|
-
assert result.exit_code == 0
|
|
520
|
-
assert "_complete" not in result.output
|
|
521
|
-
assert "compdef _papycli papycli" in result.output
|
|
522
|
-
|
|
523
|
-
@pytest.mark.skipif(not PETSTORE_PATH.exists(), reason="petstore-oas3.json not found")
|
|
524
|
-
def test_bash_with_apidef_contains_resources(
|
|
525
|
-
self, petstore_conf_dir: Path, monkeypatch: pytest.MonkeyPatch
|
|
526
|
-
) -> None:
|
|
527
|
-
"""apidef がある場合、生成スクリプトにリソースパスが含まれること。"""
|
|
528
|
-
monkeypatch.setenv("PAPYCLI_CONF_DIR", str(petstore_conf_dir))
|
|
529
|
-
runner = CliRunner()
|
|
530
|
-
result = runner.invoke(cli, ["config", "completion-script", "bash"])
|
|
531
|
-
assert result.exit_code == 0
|
|
532
|
-
assert "/pet" in result.output
|
|
533
|
-
assert "/store/inventory" in result.output
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
- [ ] **Step 2-2: テスト実行(失敗確認)**
|
|
537
|
-
|
|
538
|
-
```bash
|
|
539
|
-
uv run pytest tests/unittest/test_main.py::TestCompletionScriptStatic -v 2>&1 | head -20
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
Expected: `test_bash_no_python_call` が FAIL(現状のスクリプトに `_complete` が含まれるため)
|
|
543
|
-
|
|
544
|
-
- [ ] **Step 2-3: `main.py` の import と `cmd_config_completion_script` を更新する**
|
|
545
|
-
|
|
546
|
-
**import 行の変更:**
|
|
547
|
-
|
|
548
|
-
変更前(`main.py` の `completion` import 行):
|
|
549
|
-
```python
|
|
550
|
-
from papycli.completion import _SAFE_CMD_RE, generate_script, get_completions
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
変更後:
|
|
554
|
-
```python
|
|
555
|
-
from papycli.completion import _SAFE_CMD_RE, generate_script, generate_static_script, get_completions
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
**関数本体の変更:**
|
|
559
|
-
|
|
560
|
-
変更前(`cmd_config_completion_script` 関数本体):
|
|
561
|
-
```python
|
|
562
|
-
root_name = click.get_current_context().find_root().info_name or ""
|
|
563
|
-
cmd_name = Path(root_name).stem # .stem で Windows の ".exe" 等を除去する
|
|
564
|
-
try:
|
|
565
|
-
click.echo(generate_script(shell, cmd_name), nl=False)
|
|
566
|
-
except ValueError as e:
|
|
567
|
-
click.echo(f"Error: {e}", err=True)
|
|
568
|
-
sys.exit(1)
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
変更後:
|
|
572
|
-
```python
|
|
573
|
-
root_name = click.get_current_context().find_root().info_name or ""
|
|
574
|
-
cmd_name = Path(root_name).stem # .stem で Windows の ".exe" 等を除去する
|
|
575
|
-
conf_dir = get_conf_dir()
|
|
576
|
-
try:
|
|
577
|
-
conf = load_conf(conf_dir)
|
|
578
|
-
api_names: list[str] = [
|
|
579
|
-
k for k in conf if k not in ("default", "aliases") and isinstance(conf[k], dict)
|
|
580
|
-
]
|
|
581
|
-
apidef, _ = load_current_apidef(conf_dir, conf=conf)
|
|
582
|
-
except Exception:
|
|
583
|
-
api_names = []
|
|
584
|
-
apidef = None
|
|
585
|
-
try:
|
|
586
|
-
click.echo(generate_static_script(shell, cmd_name, apidef, api_names), nl=False)
|
|
587
|
-
except ValueError as e:
|
|
588
|
-
click.echo(f"Error: {e}", err=True)
|
|
589
|
-
sys.exit(1)
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
- [ ] **Step 2-4: テスト実行(成功確認)**
|
|
593
|
-
|
|
594
|
-
```bash
|
|
595
|
-
uv run pytest tests/unittest/test_main.py::TestCompletionScriptStatic -v
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
Expected: 3 tests PASS
|
|
599
|
-
|
|
600
|
-
- [ ] **Step 2-5: 全ユニットテスト実行(既存テストの破損確認)**
|
|
601
|
-
|
|
602
|
-
```bash
|
|
603
|
-
uv run pytest tests/unittest/ -v
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
Expected: 全テスト PASS
|
|
607
|
-
|
|
608
|
-
- [ ] **Step 2-6: ruff + mypy チェック**
|
|
609
|
-
|
|
610
|
-
```bash
|
|
611
|
-
uv run ruff check src/papycli/main.py && uv run mypy src/papycli/main.py
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
Expected: no errors / warnings
|
|
615
|
-
|
|
616
|
-
- [ ] **Step 2-7: コミット**
|
|
617
|
-
|
|
618
|
-
```bash
|
|
619
|
-
git add src/papycli/main.py tests/unittest/test_main.py
|
|
620
|
-
git commit -m "feat: switch config completion-script to static script generation"
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
---
|
|
624
|
-
|
|
625
|
-
## Task 3: 全テスト実行と最終確認
|
|
626
|
-
|
|
627
|
-
**Files:** 変更なし
|
|
628
|
-
|
|
629
|
-
- [ ] **Step 3-1: 全テスト実行**
|
|
630
|
-
|
|
631
|
-
```bash
|
|
632
|
-
uv run pytest tests/ -v
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
Expected: 全テスト PASS
|
|
636
|
-
|
|
637
|
-
- [ ] **Step 3-2: 生成スクリプトの内容確認**
|
|
638
|
-
|
|
639
|
-
```bash
|
|
640
|
-
uv run papycli config completion-script bash 2>/dev/null | head -30
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
Expected: `_papycli_completion()` 関数の定義が出力される。`_complete` の文字列が含まれない。
|
|
644
|
-
|
|
645
|
-
- [ ] **Step 3-3: ruff 全体チェック**
|
|
646
|
-
|
|
647
|
-
```bash
|
|
648
|
-
uv run ruff check src/
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
Expected: no errors
|
|
652
|
-
|
|
653
|
-
---
|
|
654
|
-
|
|
655
|
-
## 移行メモ(実装者向け)
|
|
656
|
-
|
|
657
|
-
既存ユーザーが古いスクリプト(`papycli _complete` を呼ぶ動的版)を `.bashrc` に設定している場合、papycli をアップグレード後に以下を再実行する必要がある:
|
|
658
|
-
|
|
659
|
-
```bash
|
|
660
|
-
eval "$(papycli config completion-script bash)" # bash
|
|
661
|
-
eval "$(papycli config completion-script zsh)" # zsh
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
また、`config add`・`config use`・`config remove` で API 設定を変更した後も、補完候補を更新するために同コマンドを再実行する必要がある。
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md 簡潔化 実装計画
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
-
|
|
5
|
-
**Goal:** CLAUDE.md を約 277 行から約 110 行に削減し、コンテキスト消費を抑える。
|
|
6
|
-
|
|
7
|
-
**Architecture:** 1 ファイル (`CLAUDE.md`) を書き換えるだけのドキュメント変更。テストコードなし。削除・圧縮の判断基準は `design/2026-03-28-claude-md-simplification.md` に記載。
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** なし(テキスト編集のみ)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
### Task 1: CLAUDE.md を簡潔版に書き換える
|
|
14
|
-
|
|
15
|
-
**Files:**
|
|
16
|
-
- Modify: `CLAUDE.md`
|
|
17
|
-
|
|
18
|
-
- [ ] **Step 1: 現在の行数を確認する**
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
wc -l CLAUDE.md
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Expected: `277 CLAUDE.md`
|
|
25
|
-
|
|
26
|
-
- [ ] **Step 2: CLAUDE.md を以下の内容に全置換する**
|
|
27
|
-
|
|
28
|
-
```markdown
|
|
29
|
-
# CLAUDE.md
|
|
30
|
-
|
|
31
|
-
## プロジェクト概要
|
|
32
|
-
|
|
33
|
-
`papycli` は OpenAPI 3.0 仕様を読み込み、REST API エンドポイントをターミナルから直接呼び出せるインタラクティブな CLI を提供する Python 製ツールです。
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 開発環境
|
|
38
|
-
|
|
39
|
-
- Python 3.12 以上 / uv で仮想環境を管理(Linux / macOS のみ)
|
|
40
|
-
- テスト: `uv run pytest`
|
|
41
|
-
- Lint・フォーマット: `uv run ruff check` / `uv run ruff format`
|
|
42
|
-
- 型チェック: `uv run mypy src/`
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## 主要モジュール
|
|
47
|
-
|
|
48
|
-
- `main.py` — CLI エントリポイント・引数パース
|
|
49
|
-
- `init_cmd.py` — `config add`(spec 変換・保存)
|
|
50
|
-
- `spec_loader.py` — OpenAPI spec 読み込み・$ref 解決・内部形式変換
|
|
51
|
-
- `api_call.py` — HTTP リクエスト実行・パステンプレートマッチング
|
|
52
|
-
- `checker.py` — `--check` / `--check-strict` パラメータ検証
|
|
53
|
-
- `completion.py` — bash/zsh 補完スクリプト生成
|
|
54
|
-
- `config.py` — 設定ファイル読み書き・ログパス管理
|
|
55
|
-
- `filters.py` — リクエスト・レスポンスフィルタープラグイン機構
|
|
56
|
-
- `response_checker.py` — `--response-check` レスポンス検証
|
|
57
|
-
- `summary.py` — summary コマンド・CSV 出力
|
|
58
|
-
- `i18n.py` — 日英ヘルプテキスト切り替え
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## 内部データフォーマット
|
|
63
|
-
|
|
64
|
-
### API 定義ファイル (`apis/<name>.json`)
|
|
65
|
-
|
|
66
|
-
`{ "<path>": [ { "method", "query_parameters", "post_parameters" } ] }` の形式。
|
|
67
|
-
|
|
68
|
-
- `method`: `"get"` / `"post"` / `"put"` / `"patch"` / `"delete"`
|
|
69
|
-
- `query_parameters` / `post_parameters`: `[{ "name", "type", "required", "enum"(省略可) }]`
|
|
70
|
-
- path にはパステンプレート(`/pet/{petId}` 形式)も使用可
|
|
71
|
-
|
|
72
|
-
### 設定ファイル (`papycli.conf`)
|
|
73
|
-
|
|
74
|
-
`{ "default": "<api-name>", "<api-name>": { "openapispec", "apidef", "url" } }` の形式。
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## CLI 仕様
|
|
79
|
-
|
|
80
|
-
### コマンド構文
|
|
81
|
-
|
|
82
|
-
\`\`\`
|
|
83
|
-
papycli <method> <resource> [options]
|
|
84
|
-
papycli config add <spec-file>
|
|
85
|
-
papycli config use <api-name>
|
|
86
|
-
papycli config remove <api-name>
|
|
87
|
-
papycli config list
|
|
88
|
-
papycli config log [PATH] [--unset]
|
|
89
|
-
papycli config completion-script <bash|zsh>
|
|
90
|
-
papycli spec [resource]
|
|
91
|
-
papycli spec --full [resource]
|
|
92
|
-
papycli summary [resource] [--csv]
|
|
93
|
-
papycli --version
|
|
94
|
-
papycli --help / -h
|
|
95
|
-
\`\`\`
|
|
96
|
-
|
|
97
|
-
### サポートするメソッド
|
|
98
|
-
|
|
99
|
-
`get | post | put | patch | delete` をサポートする。
|
|
100
|
-
|
|
101
|
-
### パステンプレートのマッチング
|
|
102
|
-
|
|
103
|
-
リソースパスに数値や文字列が含まれる場合(例:`/pet/99`)、API 定義内のテンプレート(`/pet/{petId}`)にマッチさせ、値を埋め込んでリクエストを送信する。
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## 環境変数
|
|
108
|
-
|
|
109
|
-
| 変数 | デフォルト | 説明 |
|
|
110
|
-
|------|-----------|------|
|
|
111
|
-
| `PAPYCLI_CONF_DIR` | `~/.papycli` | 設定ディレクトリのパス |
|
|
112
|
-
| `PAPYCLI_CUSTOM_HEADER` | (なし) | 全リクエストに付与するカスタムヘッダー(改行区切りで複数指定可) |
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## テスト方針
|
|
117
|
-
|
|
118
|
-
- `tests/unittest/` 以下にユニットテストを配置する
|
|
119
|
-
- `tests/integration/` 以下に統合テストを配置する
|
|
120
|
-
- HTTP リクエストは `responses` ライブラリ等でモックして実テストしない
|
|
121
|
-
- OpenAPI spec の変換ロジックは代表的なケースを網羅するテストを書く
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## コーディング規約
|
|
126
|
-
|
|
127
|
-
- フォーマッタ・Lint は `ruff` を使用する
|
|
128
|
-
- 型ヒントを積極的に使用し、`mypy` でチェックを通す
|
|
129
|
-
- 関数・モジュールの公開 API にのみ docstring を書く(内部実装には不要)
|
|
130
|
-
- エラーメッセージはユーザーが原因を特定しやすい内容にする
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## コード調査・編集のワークフロー
|
|
135
|
-
|
|
136
|
-
- シンボル操作はファイルを直接読む前にできるだけSerenaを使うこと
|
|
137
|
-
- ファイル内容の検索には可能なら `grep` ではなく `rg`(ripgrep)を使うこと
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Git リポジトリ運用 (GitHub)
|
|
142
|
-
|
|
143
|
-
### issue 管理
|
|
144
|
-
|
|
145
|
-
- ソースコードやドキュメントの追加・修正は原則として最初に issue を作成し、それに対応する形で行う
|
|
146
|
-
- issue には適切なラベルをつける。使用するラベルは以下のいずれか
|
|
147
|
-
- 機能以外のバグの場合、`ci`、`chore` などのラベルも追加する
|
|
148
|
-
|
|
149
|
-
| ラベル | 用途 |
|
|
150
|
-
|---------------|------|
|
|
151
|
-
| `feature` | 新機能の追加 |
|
|
152
|
-
| `bug` | バグ修正 |
|
|
153
|
-
| `refactor` | 機能変更を伴わないリファクタリング |
|
|
154
|
-
| `test` | テストコードの追加 |
|
|
155
|
-
| `ci` | GitHub ActionsなどContinuous Integrationに関係する変更 |
|
|
156
|
-
| `documentation` | ドキュメントのみの変更 |
|
|
157
|
-
| `chore` | 上記以外 (ビルド・依存関係・設定等のメンテナンス) |
|
|
158
|
-
|
|
159
|
-
### ブランチ・コミット・PR
|
|
160
|
-
|
|
161
|
-
- ソースコード修正は必ず最新の `main` ブランチからピックブランチを作成しておこなう
|
|
162
|
-
- ソースコード修正時、異なるタイプの修正 (たとえば機能追加とリファクタリング) は極力コミットを分ける。
|
|
163
|
-
- Pythonのソースコードを追加・修正した場合、`ruff` と `mypy` でチェックし、警告がなくなるようにしておく
|
|
164
|
-
- コミットメッセージは原則として Conventional Commits に従う
|
|
165
|
-
- ユーザーから見て機能に変更がないときは `feat` は使わない
|
|
166
|
-
- 破壊的変更を伴う場合、`feat!:` や `fix!` のようにコミットメッセージのタイプに**必ず**`!`を付ける
|
|
167
|
-
- `ci` や `chore` のバグ修正の場合、`fix(ci):`、`fix(chore)` のように機能上の不具合修正でないことがわかるように明記する
|
|
168
|
-
- コミットに互換性を損なう破壊的変更が含まれる場合、"BREAKING CHANGE:" フッターを**必ず**追加する
|
|
169
|
-
- 重要: プルリクエスト (PR) のレビュー指摘に対するコード修正のときだけ Conventional Commits で定義されていない `review:` プレフィクスを付ける(CHANGELOG対象から外すため)
|
|
170
|
-
- PR でレビュー指摘を受けた場合、必要であればコードを修正し、修正コミットをプッシュする
|
|
171
|
-
- PR で `Nit` の指摘に対しては「このままとします」と回答し、コードは修正しなくてよい
|
|
172
|
-
- プッシュ後、修正内容を簡潔にまとめてPRに返信する
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
- [ ] **Step 3: 行数を確認する**
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
wc -l CLAUDE.md
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Expected: 110 行前後(100〜120 行の範囲)
|
|
182
|
-
|
|
183
|
-
- [ ] **Step 4: コミットする**
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
git add CLAUDE.md
|
|
187
|
-
git commit -m "docs: simplify CLAUDE.md from 277 to ~110 lines"
|
|
188
|
-
```
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md 簡潔化 設計ドキュメント
|
|
2
|
-
|
|
3
|
-
## 目的
|
|
4
|
-
|
|
5
|
-
CLAUDE.md がコンテキストを圧迫しないよう、約 277 行から約 100 行に削減する。
|
|
6
|
-
詳細設計情報は `design_doc.md`(コンテキスト外)に既に存在するため、
|
|
7
|
-
CLAUDE.md は「Claude が自力で導出できない情報」と「行動ルール」だけに絞る。
|
|
8
|
-
|
|
9
|
-
## 方針
|
|
10
|
-
|
|
11
|
-
削除・圧縮の判断基準:
|
|
12
|
-
- **削除**: Glob/ls/コード読解で容易に確認できる情報
|
|
13
|
-
- **圧縮**: 有用だが現在の記述量が過剰な情報
|
|
14
|
-
- **維持**: 行動ルール・非自明な仕様・コンテキスト節約後も価値が高いもの
|
|
15
|
-
|
|
16
|
-
## セクション別の変更内容
|
|
17
|
-
|
|
18
|
-
### 開発環境(22行 → 8行)
|
|
19
|
-
|
|
20
|
-
ツール表とセットアップ節を削除し、`uv run` コマンド 3 つだけ箇条書きで残す。
|
|
21
|
-
|
|
22
|
-
```markdown
|
|
23
|
-
## 開発環境
|
|
24
|
-
|
|
25
|
-
- Python 3.12 以上 / uv で仮想環境を管理(Linux / macOS のみ)
|
|
26
|
-
- テスト: `uv run pytest`
|
|
27
|
-
- Lint・フォーマット: `uv run ruff check` / `uv run ruff format`
|
|
28
|
-
- 型チェック: `uv run mypy src/`
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### ディレクトリ構成ツリー(42行 → 削除)
|
|
32
|
-
|
|
33
|
-
全ファイル名を列挙したツリーは Claude が Glob/ls で確認できるため削除する。
|
|
34
|
-
|
|
35
|
-
### 主要モジュール説明(32行 → 12行)
|
|
36
|
-
|
|
37
|
-
詳細説明をやめ、`file.py — 役割` の1行形式に統一する。
|
|
38
|
-
|
|
39
|
-
```markdown
|
|
40
|
-
## 主要モジュール
|
|
41
|
-
|
|
42
|
-
- `main.py` — CLI エントリポイント・引数パース
|
|
43
|
-
- `init_cmd.py` — `config add`(spec 変換・保存)
|
|
44
|
-
- `spec_loader.py` — OpenAPI spec 読み込み・$ref 解決・内部形式変換
|
|
45
|
-
- `api_call.py` — HTTP リクエスト実行・パステンプレートマッチング
|
|
46
|
-
- `checker.py` — `--check` / `--check-strict` パラメータ検証
|
|
47
|
-
- `completion.py` — bash/zsh 補完スクリプト生成
|
|
48
|
-
- `config.py` — 設定ファイル読み書き・ログパス管理
|
|
49
|
-
- `filters.py` — リクエスト・レスポンスフィルタープラグイン機構
|
|
50
|
-
- `response_checker.py` — `--response-check` レスポンス検証
|
|
51
|
-
- `spec_loader.py` — OpenAPI spec 読み込み・$ref 解決
|
|
52
|
-
- `summary.py` — summary コマンド・CSV 出力
|
|
53
|
-
- `i18n.py` — 日英ヘルプテキスト切り替え
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 内部データフォーマット(40行 → 15行)
|
|
57
|
-
|
|
58
|
-
JSON サンプルを削除し、スキーマをインライン記法+箇条書きで表現する。
|
|
59
|
-
|
|
60
|
-
```markdown
|
|
61
|
-
## 内部データフォーマット
|
|
62
|
-
|
|
63
|
-
### API 定義ファイル (`apis/<name>.json`)
|
|
64
|
-
|
|
65
|
-
`{ "<path>": [ { "method", "query_parameters", "post_parameters" } ] }` の形式。
|
|
66
|
-
- `method`: `"get"` / `"post"` / `"put"` / `"patch"` / `"delete"`
|
|
67
|
-
- `query_parameters` / `post_parameters`: `[{ "name", "type", "required", "enum"(省略可) }]`
|
|
68
|
-
- path にはパステンプレート(`/pet/{petId}` 形式)も使用可
|
|
69
|
-
|
|
70
|
-
### 設定ファイル (`papycli.conf`)
|
|
71
|
-
|
|
72
|
-
`{ "default": "<api-name>", "<api-name>": { "openapispec", "apidef", "url" } }` の形式。
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### CLI 仕様(27行 → 15行)
|
|
76
|
-
|
|
77
|
-
コマンド一覧(15行)とパステンプレートマッチング(2行)を維持する。
|
|
78
|
-
パラメータ詳細(`-q`, `-p`, `-d`, `-H`, `--check`, `--response-check` の説明10行)は
|
|
79
|
-
`--help` で確認できるため削除する。
|
|
80
|
-
|
|
81
|
-
### 環境変数・テスト方針・コーディング規約・調査ワークフロー・Git 運用
|
|
82
|
-
|
|
83
|
-
変更なし。
|
|
84
|
-
|
|
85
|
-
## 削減後の想定行数
|
|
86
|
-
|
|
87
|
-
| セクション | 現在 | 変更後 |
|
|
88
|
-
|---|---|---|
|
|
89
|
-
| プロジェクト概要 | 5 | 5 |
|
|
90
|
-
| 開発環境 | 22 | 8 |
|
|
91
|
-
| ディレクトリ構成ツリー | 42 | 0 |
|
|
92
|
-
| 主要モジュール | 32 | 14 |
|
|
93
|
-
| 内部データフォーマット | 40 | 15 |
|
|
94
|
-
| CLI 仕様 | 27 | 15 |
|
|
95
|
-
| 環境変数 | 5 | 5 |
|
|
96
|
-
| テスト方針 | 6 | 6 |
|
|
97
|
-
| コーディング規約 | 6 | 6 |
|
|
98
|
-
| 調査ワークフロー | 4 | 4 |
|
|
99
|
-
| Git 運用 | 30 | 30 |
|
|
100
|
-
| **合計** | **277** | **108** |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/specs/2026-04-01-config-add-upgrade-design.md
RENAMED
|
File without changes
|
{papycli-0.16.1 → papycli-0.16.2}/docs/superpowers/specs/2026-04-02-dotenv-autoload-design.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{papycli-0.16.1 → papycli-0.16.2}/examples/request_filter/src/papycli_debug_filter/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|