decklens 0.1.0__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.
- decklens-0.1.0/LICENSE +21 -0
- decklens-0.1.0/PKG-INFO +192 -0
- decklens-0.1.0/README.md +158 -0
- decklens-0.1.0/decklens/__init__.py +3 -0
- decklens-0.1.0/decklens/cli.py +199 -0
- decklens-0.1.0/decklens/diff/__init__.py +3 -0
- decklens-0.1.0/decklens/diff/engine.py +472 -0
- decklens-0.1.0/decklens/explainer/__init__.py +3 -0
- decklens-0.1.0/decklens/explainer/claude.py +108 -0
- decklens-0.1.0/decklens/parsers/__init__.py +27 -0
- decklens-0.1.0/decklens/parsers/base.py +87 -0
- decklens-0.1.0/decklens/parsers/openradioss.py +344 -0
- decklens-0.1.0/decklens.egg-info/PKG-INFO +192 -0
- decklens-0.1.0/decklens.egg-info/SOURCES.txt +20 -0
- decklens-0.1.0/decklens.egg-info/dependency_links.txt +1 -0
- decklens-0.1.0/decklens.egg-info/entry_points.txt +2 -0
- decklens-0.1.0/decklens.egg-info/requires.txt +8 -0
- decklens-0.1.0/decklens.egg-info/top_level.txt +1 -0
- decklens-0.1.0/pyproject.toml +54 -0
- decklens-0.1.0/setup.cfg +4 -0
- decklens-0.1.0/tests/test_diff.py +92 -0
- decklens-0.1.0/tests/test_parser.py +174 -0
decklens-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maya Kachina
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
decklens-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decklens
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Engineering-aware semantic diff tool for CAE input files
|
|
5
|
+
Author-email: Maya Kachina <pj.kachina@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pj-kachina/DeckLens-Semantic-Diff-for-CAE
|
|
8
|
+
Project-URL: Documentation, https://github.com/pj-kachina/DeckLens-Semantic-Diff-for-CAE#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/pj-kachina/DeckLens-Semantic-Diff-for-CAE.git
|
|
10
|
+
Project-URL: Issues, https://github.com/pj-kachina/DeckLens-Semantic-Diff-for-CAE/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/pj-kachina/DeckLens-Semantic-Diff-for-CAE/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: CAE,FEA,diff,OpenRadioss,NASTRAN,LS-DYNA,semantic-diff,engineering,simulation,comparison
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: anthropic>=0.49.0
|
|
27
|
+
Requires-Dist: click>=8.1
|
|
28
|
+
Requires-Dist: rich>=13.0
|
|
29
|
+
Requires-Dist: python-dotenv>=1.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# DeckLens: Semantic Diff for CAE
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
**DeckLens** は CAE(計算力学)入力ファイルの**セマンティックdiff**ツールです。テキスト差分ではなく、エンジニアリング観点から「何が変わった」かを理解します。
|
|
41
|
+
|
|
42
|
+
## 問題
|
|
43
|
+
|
|
44
|
+
CAE検証チームの現状:
|
|
45
|
+
- 型紙(テンプレート)から修正内容を手動確認 → ボトルネック
|
|
46
|
+
- `grep` + テキストエディタで変更追跡 → ミスが多い
|
|
47
|
+
- 厚さ変更は単なる%数値表示 → 剛性への影響が不明確
|
|
48
|
+
- 境界条件削除が見落とされやすい
|
|
49
|
+
|
|
50
|
+
## ソリューション
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
$ decklens diff before.rad after.rad
|
|
54
|
+
|
|
55
|
+
─────────── DeckLens Semantic Diff ──────────
|
|
56
|
+
Before: before.rad
|
|
57
|
+
After: after.rad
|
|
58
|
+
|
|
59
|
+
┌─ Summary ─┐
|
|
60
|
+
│ 2 CRITICAL, 4 WARNING │
|
|
61
|
+
└────────────┘
|
|
62
|
+
|
|
63
|
+
┌────────┬─────────┬────────────┬─────────────────────┐
|
|
64
|
+
│ Sev │ Type │ Name │ Field │
|
|
65
|
+
├────────┼─────────┼────────────┼─────────────────────┤
|
|
66
|
+
│ [CRIT] │ PROP/SH │ OUTER_PNL │ thickness │
|
|
67
|
+
│ │ │ │ (bend-stiffness │
|
|
68
|
+
│ │ │ │ +138%) │
|
|
69
|
+
│ [CRIT] │ CLOAD │ FORCE_Z │ Fscale_y: 1000→2000│
|
|
70
|
+
│ [WARN] │ MAT/ELS │ STEEL │ E: 210k→206k (-1.9%)
|
|
71
|
+
│ [WARN] │ BCS │ FIXED_SUP │ RX: free→fixed │
|
|
72
|
+
└────────┴─────────┴────────────┴─────────────────────┘
|
|
73
|
+
|
|
74
|
+
🤖 AI Engineering Analysis (Claude Opus 4.8)
|
|
75
|
+
─────────────────────────────────────────
|
|
76
|
+
曲げ剛性が +138% 増加(t³則)するため、座屈荷重が大幅に上昇。
|
|
77
|
+
ただし Young の係数が -1.9% 低下しているため、相殺効果は限定的。
|
|
78
|
+
回転拘束追加により、モーメント反力が発生可能性あり。
|
|
79
|
+
負荷 2 倍は FOS に直結 — 応力状態の再確認推奨。
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 主な機能
|
|
83
|
+
|
|
84
|
+
✅ **OpenRadioss パーサー**
|
|
85
|
+
- `/PROP/SHELL`, `/MAT/ELAST`, `/MAT/PLAS_JOHNS`, `/PART`, `/BCS`, `/LOAD`, `/INTER` 対応
|
|
86
|
+
- FORTRAN D-notation(`7.85D-9`)自動変換
|
|
87
|
+
|
|
88
|
+
✅ **セマンティック差分エンジン**
|
|
89
|
+
- 厚さ変更 → 曲げ剛性(EI ∝ t³)を自動計算・注釈
|
|
90
|
+
- Young係数 → 剛性への影響を評価
|
|
91
|
+
- 境界条件追加/削除 → 重大度を自動判定
|
|
92
|
+
- 負荷 2 倍 → CRITICAL フラグ
|
|
93
|
+
|
|
94
|
+
✅ **重大度自動判定**
|
|
95
|
+
| 重大度 | 条件 |
|
|
96
|
+
|--------|------|
|
|
97
|
+
| CRITICAL | ≥20% 変化、または BC/PART 削除 |
|
|
98
|
+
| WARNING | 5–20% 変化、または BC 追加 |
|
|
99
|
+
| INFO | <5% 変化 |
|
|
100
|
+
|
|
101
|
+
✅ **Claude AI 分析(オプション)**
|
|
102
|
+
- `claude-opus-4-8` による自動解釈
|
|
103
|
+
- 適応思考(adaptive thinking)で複雑な物理関係を推論
|
|
104
|
+
- JSON 出力対応(CI/CD 統合)
|
|
105
|
+
|
|
106
|
+
## インストール
|
|
107
|
+
|
|
108
|
+
### PyPI(CLI ツール)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install decklens-semantic-diff
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### VS Code 拡張機能
|
|
115
|
+
|
|
116
|
+
1. VS Code Marketplace で「DeckLens」検索
|
|
117
|
+
2. インストール
|
|
118
|
+
3. `.env` に `ANTHROPIC_API_KEY` 設定(AI 分析有効化)
|
|
119
|
+
|
|
120
|
+
## 使い方
|
|
121
|
+
|
|
122
|
+
### CLI
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# 基本的な diff
|
|
126
|
+
decklens diff before.rad after.rad
|
|
127
|
+
|
|
128
|
+
# AI 分析をスキップ
|
|
129
|
+
decklens diff before.rad after.rad --no-ai
|
|
130
|
+
|
|
131
|
+
# JSON 出力(CI/CD 連携)
|
|
132
|
+
decklens diff before.rad after.rad --format json
|
|
133
|
+
|
|
134
|
+
# 重大度フィルタ
|
|
135
|
+
decklens diff before.rad after.rad --min-severity WARNING
|
|
136
|
+
|
|
137
|
+
# 別の Claude モデル指定
|
|
138
|
+
decklens diff before.rad after.rad --model claude-sonnet-4-6
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### VS Code 拡張機能
|
|
142
|
+
|
|
143
|
+
右クリック → **DeckLens: Compare with** → ファイル選択 → サイドパネルに結果表示
|
|
144
|
+
|
|
145
|
+
## 対応フォーマット
|
|
146
|
+
|
|
147
|
+
| フォーマット | サポート | ロードマップ |
|
|
148
|
+
|------------|---------|-----------|
|
|
149
|
+
| OpenRadioss (.rad) | ✅ | 本実装 |
|
|
150
|
+
| NASTRAN (.bdf) | 🔜 | Q3 2026 |
|
|
151
|
+
| LS-DYNA (.k) | 🔜 | Q3 2026 |
|
|
152
|
+
|
|
153
|
+
## 技術仕様
|
|
154
|
+
|
|
155
|
+
- **言語**: Python 3.11+(CLI)、TypeScript(VS Code 拡張)
|
|
156
|
+
- **API**: Claude Opus 4.8、adaptive thinking、high effort
|
|
157
|
+
- **出力**: Rich テーブル、Markdown、JSON
|
|
158
|
+
- **ライセンス**: MIT
|
|
159
|
+
|
|
160
|
+
## 必要な環境
|
|
161
|
+
|
|
162
|
+
- Python 3.11+
|
|
163
|
+
- VS Code 1.85+(拡張機能使用時)
|
|
164
|
+
- ANTHROPIC_API_KEY(AI 分析機能使用時)
|
|
165
|
+
|
|
166
|
+
## トラブルシューティング
|
|
167
|
+
|
|
168
|
+
### Q: AI 分析が実行されない
|
|
169
|
+
**A**: `.env` に `ANTHROPIC_API_KEY` が設定されているか確認。未設定なら `--no-ai` を付与。
|
|
170
|
+
|
|
171
|
+
### Q: 出力が文字化けする(Windows)
|
|
172
|
+
**A**: PowerShell で UTF-8 出力を有効化:
|
|
173
|
+
```powershell
|
|
174
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 開発
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
git clone https://github.com/YOUR_GITHUB_USERNAME/DeckLens-Semantic-Diff-for-CAE.git
|
|
181
|
+
cd DeckLens-Semantic-Diff-for-CAE
|
|
182
|
+
pip install -e ".[dev]"
|
|
183
|
+
pytest -v
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## フィードバック
|
|
187
|
+
|
|
188
|
+
Issue や Discussion は [GitHub](https://github.com/YOUR_GITHUB_USERNAME/DeckLens-Semantic-Diff-for-CAE/issues) へ。
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
**Made with ❤️ by CAE engineers, for CAE engineers**
|
decklens-0.1.0/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# DeckLens: Semantic Diff for CAE
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
**DeckLens** は CAE(計算力学)入力ファイルの**セマンティックdiff**ツールです。テキスト差分ではなく、エンジニアリング観点から「何が変わった」かを理解します。
|
|
7
|
+
|
|
8
|
+
## 問題
|
|
9
|
+
|
|
10
|
+
CAE検証チームの現状:
|
|
11
|
+
- 型紙(テンプレート)から修正内容を手動確認 → ボトルネック
|
|
12
|
+
- `grep` + テキストエディタで変更追跡 → ミスが多い
|
|
13
|
+
- 厚さ変更は単なる%数値表示 → 剛性への影響が不明確
|
|
14
|
+
- 境界条件削除が見落とされやすい
|
|
15
|
+
|
|
16
|
+
## ソリューション
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
$ decklens diff before.rad after.rad
|
|
20
|
+
|
|
21
|
+
─────────── DeckLens Semantic Diff ──────────
|
|
22
|
+
Before: before.rad
|
|
23
|
+
After: after.rad
|
|
24
|
+
|
|
25
|
+
┌─ Summary ─┐
|
|
26
|
+
│ 2 CRITICAL, 4 WARNING │
|
|
27
|
+
└────────────┘
|
|
28
|
+
|
|
29
|
+
┌────────┬─────────┬────────────┬─────────────────────┐
|
|
30
|
+
│ Sev │ Type │ Name │ Field │
|
|
31
|
+
├────────┼─────────┼────────────┼─────────────────────┤
|
|
32
|
+
│ [CRIT] │ PROP/SH │ OUTER_PNL │ thickness │
|
|
33
|
+
│ │ │ │ (bend-stiffness │
|
|
34
|
+
│ │ │ │ +138%) │
|
|
35
|
+
│ [CRIT] │ CLOAD │ FORCE_Z │ Fscale_y: 1000→2000│
|
|
36
|
+
│ [WARN] │ MAT/ELS │ STEEL │ E: 210k→206k (-1.9%)
|
|
37
|
+
│ [WARN] │ BCS │ FIXED_SUP │ RX: free→fixed │
|
|
38
|
+
└────────┴─────────┴────────────┴─────────────────────┘
|
|
39
|
+
|
|
40
|
+
🤖 AI Engineering Analysis (Claude Opus 4.8)
|
|
41
|
+
─────────────────────────────────────────
|
|
42
|
+
曲げ剛性が +138% 増加(t³則)するため、座屈荷重が大幅に上昇。
|
|
43
|
+
ただし Young の係数が -1.9% 低下しているため、相殺効果は限定的。
|
|
44
|
+
回転拘束追加により、モーメント反力が発生可能性あり。
|
|
45
|
+
負荷 2 倍は FOS に直結 — 応力状態の再確認推奨。
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 主な機能
|
|
49
|
+
|
|
50
|
+
✅ **OpenRadioss パーサー**
|
|
51
|
+
- `/PROP/SHELL`, `/MAT/ELAST`, `/MAT/PLAS_JOHNS`, `/PART`, `/BCS`, `/LOAD`, `/INTER` 対応
|
|
52
|
+
- FORTRAN D-notation(`7.85D-9`)自動変換
|
|
53
|
+
|
|
54
|
+
✅ **セマンティック差分エンジン**
|
|
55
|
+
- 厚さ変更 → 曲げ剛性(EI ∝ t³)を自動計算・注釈
|
|
56
|
+
- Young係数 → 剛性への影響を評価
|
|
57
|
+
- 境界条件追加/削除 → 重大度を自動判定
|
|
58
|
+
- 負荷 2 倍 → CRITICAL フラグ
|
|
59
|
+
|
|
60
|
+
✅ **重大度自動判定**
|
|
61
|
+
| 重大度 | 条件 |
|
|
62
|
+
|--------|------|
|
|
63
|
+
| CRITICAL | ≥20% 変化、または BC/PART 削除 |
|
|
64
|
+
| WARNING | 5–20% 変化、または BC 追加 |
|
|
65
|
+
| INFO | <5% 変化 |
|
|
66
|
+
|
|
67
|
+
✅ **Claude AI 分析(オプション)**
|
|
68
|
+
- `claude-opus-4-8` による自動解釈
|
|
69
|
+
- 適応思考(adaptive thinking)で複雑な物理関係を推論
|
|
70
|
+
- JSON 出力対応(CI/CD 統合)
|
|
71
|
+
|
|
72
|
+
## インストール
|
|
73
|
+
|
|
74
|
+
### PyPI(CLI ツール)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install decklens-semantic-diff
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### VS Code 拡張機能
|
|
81
|
+
|
|
82
|
+
1. VS Code Marketplace で「DeckLens」検索
|
|
83
|
+
2. インストール
|
|
84
|
+
3. `.env` に `ANTHROPIC_API_KEY` 設定(AI 分析有効化)
|
|
85
|
+
|
|
86
|
+
## 使い方
|
|
87
|
+
|
|
88
|
+
### CLI
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 基本的な diff
|
|
92
|
+
decklens diff before.rad after.rad
|
|
93
|
+
|
|
94
|
+
# AI 分析をスキップ
|
|
95
|
+
decklens diff before.rad after.rad --no-ai
|
|
96
|
+
|
|
97
|
+
# JSON 出力(CI/CD 連携)
|
|
98
|
+
decklens diff before.rad after.rad --format json
|
|
99
|
+
|
|
100
|
+
# 重大度フィルタ
|
|
101
|
+
decklens diff before.rad after.rad --min-severity WARNING
|
|
102
|
+
|
|
103
|
+
# 別の Claude モデル指定
|
|
104
|
+
decklens diff before.rad after.rad --model claude-sonnet-4-6
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### VS Code 拡張機能
|
|
108
|
+
|
|
109
|
+
右クリック → **DeckLens: Compare with** → ファイル選択 → サイドパネルに結果表示
|
|
110
|
+
|
|
111
|
+
## 対応フォーマット
|
|
112
|
+
|
|
113
|
+
| フォーマット | サポート | ロードマップ |
|
|
114
|
+
|------------|---------|-----------|
|
|
115
|
+
| OpenRadioss (.rad) | ✅ | 本実装 |
|
|
116
|
+
| NASTRAN (.bdf) | 🔜 | Q3 2026 |
|
|
117
|
+
| LS-DYNA (.k) | 🔜 | Q3 2026 |
|
|
118
|
+
|
|
119
|
+
## 技術仕様
|
|
120
|
+
|
|
121
|
+
- **言語**: Python 3.11+(CLI)、TypeScript(VS Code 拡張)
|
|
122
|
+
- **API**: Claude Opus 4.8、adaptive thinking、high effort
|
|
123
|
+
- **出力**: Rich テーブル、Markdown、JSON
|
|
124
|
+
- **ライセンス**: MIT
|
|
125
|
+
|
|
126
|
+
## 必要な環境
|
|
127
|
+
|
|
128
|
+
- Python 3.11+
|
|
129
|
+
- VS Code 1.85+(拡張機能使用時)
|
|
130
|
+
- ANTHROPIC_API_KEY(AI 分析機能使用時)
|
|
131
|
+
|
|
132
|
+
## トラブルシューティング
|
|
133
|
+
|
|
134
|
+
### Q: AI 分析が実行されない
|
|
135
|
+
**A**: `.env` に `ANTHROPIC_API_KEY` が設定されているか確認。未設定なら `--no-ai` を付与。
|
|
136
|
+
|
|
137
|
+
### Q: 出力が文字化けする(Windows)
|
|
138
|
+
**A**: PowerShell で UTF-8 出力を有効化:
|
|
139
|
+
```powershell
|
|
140
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 開発
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
git clone https://github.com/YOUR_GITHUB_USERNAME/DeckLens-Semantic-Diff-for-CAE.git
|
|
147
|
+
cd DeckLens-Semantic-Diff-for-CAE
|
|
148
|
+
pip install -e ".[dev]"
|
|
149
|
+
pytest -v
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## フィードバック
|
|
153
|
+
|
|
154
|
+
Issue や Discussion は [GitHub](https://github.com/YOUR_GITHUB_USERNAME/DeckLens-Semantic-Diff-for-CAE/issues) へ。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
**Made with ❤️ by CAE engineers, for CAE engineers**
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""CLI entry point for DeckLens Semantic Diff."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from rich.columns import Columns
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
from rich.text import Text
|
|
15
|
+
|
|
16
|
+
from .diff.engine import Change, DiffEngine
|
|
17
|
+
from .parsers.openradioss import parse_deck
|
|
18
|
+
|
|
19
|
+
def _make_console() -> Console:
|
|
20
|
+
import sys
|
|
21
|
+
if sys.platform == "win32":
|
|
22
|
+
try:
|
|
23
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
|
|
24
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
|
|
25
|
+
except (AttributeError, Exception):
|
|
26
|
+
pass
|
|
27
|
+
return Console(highlight=False, safe_box=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
console = _make_console()
|
|
31
|
+
|
|
32
|
+
_SEVERITY_STYLE = {
|
|
33
|
+
"CRITICAL": "bold red",
|
|
34
|
+
"WARNING": "bold yellow",
|
|
35
|
+
"INFO": "cyan",
|
|
36
|
+
}
|
|
37
|
+
_SEVERITY_LABEL = {
|
|
38
|
+
"CRITICAL": "CRIT",
|
|
39
|
+
"WARNING": "WARN",
|
|
40
|
+
"INFO": "INFO",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _fmt_value(v, unit: str | None) -> str:
|
|
45
|
+
if v is None:
|
|
46
|
+
return "-"
|
|
47
|
+
u = f" {unit}" if unit else ""
|
|
48
|
+
if isinstance(v, float):
|
|
49
|
+
return f"{v:g}{u}"
|
|
50
|
+
return f"{v}{u}"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _pct_text(pct: float | None) -> Text:
|
|
54
|
+
if pct is None:
|
|
55
|
+
return Text("")
|
|
56
|
+
sign = "+" if pct > 0 else ""
|
|
57
|
+
color = "red" if abs(pct) >= 20 else "yellow" if abs(pct) >= 5 else "green"
|
|
58
|
+
return Text(f"{sign}{pct:.1f}%", style=color)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _build_table(changes: list[Change]) -> Table:
|
|
62
|
+
table = Table(
|
|
63
|
+
show_header=True,
|
|
64
|
+
header_style="bold white on dark_blue",
|
|
65
|
+
border_style="bright_black",
|
|
66
|
+
safe_box=True,
|
|
67
|
+
)
|
|
68
|
+
table.add_column("Sev", style="bold", no_wrap=True)
|
|
69
|
+
table.add_column("Category", no_wrap=True)
|
|
70
|
+
table.add_column("Type / ID", no_wrap=True)
|
|
71
|
+
table.add_column("Name")
|
|
72
|
+
table.add_column("Field", ratio=2)
|
|
73
|
+
table.add_column("Before", justify="right", no_wrap=True)
|
|
74
|
+
table.add_column("After", justify="right", no_wrap=True)
|
|
75
|
+
table.add_column("Change", justify="right", no_wrap=True)
|
|
76
|
+
|
|
77
|
+
for ch in changes:
|
|
78
|
+
style = _SEVERITY_STYLE[ch.severity]
|
|
79
|
+
icon = Text("[" + _SEVERITY_LABEL[ch.severity] + "]", style=style)
|
|
80
|
+
table.add_row(
|
|
81
|
+
icon,
|
|
82
|
+
Text(ch.category, style="dim"),
|
|
83
|
+
Text(f"{ch.item_type}/{ch.item_id}", style="bright_white"),
|
|
84
|
+
Text(str(ch.item_name), style="white"),
|
|
85
|
+
Text(str(ch.field)),
|
|
86
|
+
Text(_fmt_value(ch.old_value, None)),
|
|
87
|
+
Text(_fmt_value(ch.new_value, ch.unit)),
|
|
88
|
+
_pct_text(ch.percent_change),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return table
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _build_summary(changes: list[Change]) -> Panel:
|
|
95
|
+
counts = {"CRITICAL": 0, "WARNING": 0, "INFO": 0}
|
|
96
|
+
for ch in changes:
|
|
97
|
+
counts[ch.severity] += 1
|
|
98
|
+
|
|
99
|
+
parts = []
|
|
100
|
+
for sev in ("CRITICAL", "WARNING", "INFO"):
|
|
101
|
+
n = counts[sev]
|
|
102
|
+
style = _SEVERITY_STYLE[sev]
|
|
103
|
+
parts.append(Text(f" {n} {sev} ", style=style))
|
|
104
|
+
|
|
105
|
+
content = Text("").join(parts)
|
|
106
|
+
return Panel(content, title="[bold]Summary", expand=False)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@click.group()
|
|
110
|
+
@click.version_option(package_name="DeckLens-Semantic-Diff-for-CAE")
|
|
111
|
+
def main() -> None:
|
|
112
|
+
"""DeckLens: Semantic Diff for CAE input files."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@main.command()
|
|
116
|
+
@click.argument("before", type=click.Path(exists=True, path_type=Path))
|
|
117
|
+
@click.argument("after", type=click.Path(exists=True, path_type=Path))
|
|
118
|
+
@click.option("--no-ai", is_flag=True, default=False, help="Skip Claude AI analysis.")
|
|
119
|
+
@click.option(
|
|
120
|
+
"--format", "output_format",
|
|
121
|
+
type=click.Choice(["text", "json"], case_sensitive=False),
|
|
122
|
+
default="text",
|
|
123
|
+
help="Output format.",
|
|
124
|
+
)
|
|
125
|
+
@click.option("--model", default="claude-opus-4-8", show_default=True,
|
|
126
|
+
help="Claude model ID for AI analysis.")
|
|
127
|
+
@click.option("--min-severity",
|
|
128
|
+
type=click.Choice(["INFO", "WARNING", "CRITICAL"], case_sensitive=False),
|
|
129
|
+
default="INFO", show_default=True,
|
|
130
|
+
help="Minimum severity level to display.")
|
|
131
|
+
def diff(
|
|
132
|
+
before: Path,
|
|
133
|
+
after: Path,
|
|
134
|
+
no_ai: bool,
|
|
135
|
+
output_format: str,
|
|
136
|
+
model: str,
|
|
137
|
+
min_severity: str,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Compare two OpenRadioss deck files and show engineering-aware changes.
|
|
140
|
+
|
|
141
|
+
BEFORE and AFTER are paths to .rad input files.
|
|
142
|
+
"""
|
|
143
|
+
_sev_rank = {"INFO": 0, "WARNING": 1, "CRITICAL": 2}
|
|
144
|
+
min_rank = _sev_rank[min_severity.upper()]
|
|
145
|
+
|
|
146
|
+
# Parse
|
|
147
|
+
with console.status("[bold green]Parsing decks...", spinner="dots"):
|
|
148
|
+
try:
|
|
149
|
+
deck_before = parse_deck(before)
|
|
150
|
+
deck_after = parse_deck(after)
|
|
151
|
+
except Exception as exc:
|
|
152
|
+
console.print(f"[red]Parse error: {exc}")
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
# Diff
|
|
156
|
+
engine = DiffEngine()
|
|
157
|
+
all_changes = engine.diff(deck_before, deck_after)
|
|
158
|
+
changes = [ch for ch in all_changes if _sev_rank[ch.severity] >= min_rank]
|
|
159
|
+
|
|
160
|
+
if output_format == "json":
|
|
161
|
+
import dataclasses
|
|
162
|
+
click.echo(json.dumps([dataclasses.asdict(ch) for ch in changes], indent=2))
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
# Rich text output
|
|
166
|
+
console.print()
|
|
167
|
+
console.rule(f"[bold blue]DeckLens Semantic Diff")
|
|
168
|
+
console.print(f" [dim]Before:[/dim] [white]{before}")
|
|
169
|
+
console.print(f" [dim]After: [/dim] [white]{after}")
|
|
170
|
+
console.print()
|
|
171
|
+
|
|
172
|
+
if not changes:
|
|
173
|
+
console.print("[green]No changes found above the specified severity threshold.[/green]")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
console.print(_build_summary(all_changes))
|
|
177
|
+
console.print()
|
|
178
|
+
console.print(_build_table(changes))
|
|
179
|
+
|
|
180
|
+
# AI analysis
|
|
181
|
+
if not no_ai:
|
|
182
|
+
try:
|
|
183
|
+
from dotenv import load_dotenv
|
|
184
|
+
load_dotenv()
|
|
185
|
+
except ImportError:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
import os
|
|
189
|
+
if not os.environ.get("ANTHROPIC_API_KEY"):
|
|
190
|
+
console.print(
|
|
191
|
+
"\n[yellow]Tip: Set ANTHROPIC_API_KEY in .env for AI analysis. "
|
|
192
|
+
"Use --no-ai to suppress this message.[/yellow]"
|
|
193
|
+
)
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
from .explainer.claude import ClaudeExplainer
|
|
197
|
+
with console.status("[bold green]Generating AI analysis...", spinner="dots"):
|
|
198
|
+
explainer = ClaudeExplainer(model=model)
|
|
199
|
+
explainer.explain(changes, str(before), str(after), console=console)
|