CLAUDE.md разгружен: тяжёлые справочники вынесены в навык kompas-mcp-dev
ci / build (push) Successful in 29s
ci / build (push) Successful in 29s
Корневой CLAUDE.md грузится в каждую сессию, но большая его часть нужна только при доработке сервера. 37 476 → 12 203 символов (~6 300 токенов на сессию). Перенесено в .claude/skills/kompas-mcp-dev/reference/: - com-implementation-facts.md — проверенные COM-цепочки («Key implementation facts»); - plugin-and-release.md — устройство plugin/, ветка dist, релиз, CI, обновление RAG-базы. Релизная кухня положена в навык, а не в plugin/CLAUDE.md: plugin/ раздаётся пользователям, внутренностям сборки там не место. В корневом файле оставлены два правила, которые нельзя откладывать до загрузки навыка — обязательный бамп версии в plugin.json и UTF-8 с BOM для PowerShell плагина. Удалено как выводимое из репозитория: раскладка каталогов, стек и счётчики тестов, список режимов sync-agent-assets.ps1, перечень содержимого каталога SDK. Секция «Navigating the SDK base» удалена как дубль §«Порядок поиска» из определения агента kompas-sdk-research — вдобавок она описывала инструменты, доступные только субагенту. Уникальная процедура обновления базы перенесена в навык.
This commit is contained in:
@@ -6,8 +6,15 @@ description: Внутренняя методика доработки самог
|
||||
# kompas-mcp-dev — доработка MCP-сервера КОМПАС-3D
|
||||
|
||||
Область: код сервера, тесты, упаковка в плагин. Построение деталей — навык `kompas-3d`.
|
||||
Архитектура, проверенные COM-цепочки и подводные камни — в `CLAUDE.md` (единственный
|
||||
архитектурный документ: каталога `docs/` в репозитории нет и заводить его не нужно).
|
||||
Архитектура, принципы и бэклог — в `CLAUDE.md` (каталога `docs/` в репозитории нет и заводить его
|
||||
не нужно). Тяжёлые справочники вынесены сюда, чтобы не висеть в контексте каждой сессии:
|
||||
|
||||
- **`reference/com-implementation-facts.md`** — проверенные COM-цепочки: загрузка interop, STA-диспетчер,
|
||||
построение через API5 `ksPart`, выбор граней/рёбер, МЦХ, снимки, дерево построения, переменные,
|
||||
STEP, прямое редактирование B-rep, формообразующие, массивы и зеркало, отверстия, сборки, чертежи,
|
||||
примитивы эскиза. **Читай перед правкой COM-слоя** и дописывай туда каждую новую проверенную цепочку.
|
||||
- **`reference/plugin-and-release.md`** — устройство `plugin/`, доставка сервера, ветка `dist`, релизный
|
||||
workflow, подводные камни CI, обновление RAG-базы справки SDK.
|
||||
|
||||
## Два слоя (важно не смешивать)
|
||||
|
||||
@@ -40,8 +47,8 @@ description: Внутренняя методика доработки самог
|
||||
5. **Тесты**: unit на валидацию/маппинг перечислений, integration на реальную геометрию
|
||||
(класс наследует `IntegrationTestBase`, артефакты — в `.scratch/`).
|
||||
6. **Документация**: каталог инструментов в `README.md` (+ счётчик в заголовке), проверенная
|
||||
COM-цепочка и подводные камни — в `CLAUDE.md` §«Key implementation facts» и в память
|
||||
(`kompas-*-api*.md`). Правь эти файлы сам — субагента для документации больше нет.
|
||||
COM-цепочка и подводные камни — в `reference/com-implementation-facts.md` (здесь же, в навыке)
|
||||
и в память (`kompas-*-api*.md`). Правь эти файлы сам — субагента для документации больше нет.
|
||||
|
||||
## Проверка
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# Key implementation facts (don't relearn)
|
||||
|
||||
Проверенные COM-цепочки сервера. Перенесено из `CLAUDE.md`, чтобы не грузить эти ~19 КБ
|
||||
в каждую сессию: они нужны при доработке сервера, а не при любом вопросе по репозиторию.
|
||||
Правь этот файл, когда проверил новую цепочку вживую.
|
||||
|
||||
- **Interop assemblies are not shipped** (`KompasAPI7`, `Kompas6API5`, `Kompas6Constants`,
|
||||
`Kompas6Constants3D` belong to ASCON). `libs/kompas-interop` is referenced with `<Private>false</Private>`
|
||||
— compile-time only, absent from `bin/` **and** from the published archive/`deps.json`. At runtime
|
||||
`KompasInteropLoader.Install()` (first line of `Program.cs`) hooks `AssemblyLoadContext.Default.Resolving`;
|
||||
lookup order: `KOMPAS_INTEROP_DIR` → next to `kompas-mcp.exe` → cache → the installed КОМПАС.
|
||||
**The install has no loose interop DLLs** — they live only inside `<install>\SDK\Samples\CSharp.zip`
|
||||
under `Common/`, so the provider unzips them into `%LOCALAPPDATA%\kompas-mcp\interop\<key>` (key = hash
|
||||
of the zip's path+size+mtime, so a КОМПАС upgrade re-extracts; staging dir + `Directory.Move` for
|
||||
atomicity). The install dir comes from `HKCR\<ProgID>\CLSID` → `HKCR\CLSID\{…}\LocalServer32`
|
||||
(`"…\Bin\kHome.Exe"` on v24 Home) — `HKLM\SOFTWARE\ASCON` holds settings, not the path. All interop
|
||||
identities are `Version=1.0.0.0` with **no strong name**, so resolving by simple name is version-agnostic
|
||||
across КОМПАС releases. Failure throws `KompasInteropException` with an actionable message; `Program`
|
||||
logs it at startup (non-fatal — `--version` must work without КОМПАС) and `kompas_status` returns it
|
||||
(it checks interop **before** touching `KompasSession`, otherwise the MCP SDK shows only «An error
|
||||
occurred invoking …»). **The test project copies the DLLs itself**
|
||||
(`<None Include="$(KompasInteropDir)\*.dll">`) — the Linux CI runner has no КОМПАС to resolve from.
|
||||
- **All COM calls run on one dedicated STA thread** (`KompasDispatcher.InvokeAsync`); КОМПАС is
|
||||
single-instance STA. Services never touch COM off that thread.
|
||||
- **Connection**: API5 (`KOMPAS.Application.5` → `KompasObject`) then `ksGetApplication7()` → `IApplication`;
|
||||
the API5 root is needed for `ksPart` 3D building and `ksDocument3D` snapshots. API7 drives app/documents,
|
||||
holes, assemblies, drawings and direct B-rep edit.
|
||||
- **3D via API5 `ksPart`**: `NewEntity(o3d_*)` + matching definition; always check `entity.Create()` return.
|
||||
Entity codes: `o3d_sketch`/`o3d_bossExtrusion`/`o3d_cutExtrusion`/`o3d_bossRotated`/`o3d_fillet`/`o3d_chamfer`,
|
||||
`o3d_shellOperation=43`, `o3d_ribOperation=44`, `o3d_baseEvolution=45` (sweep), `o3d_baseLoft=30`,
|
||||
`o3d_planeOffset=14`, `o3d_meshCopy=35` (linear pattern), `o3d_circularCopy=36`, `o3d_mirrorOperation=48`,
|
||||
`o3d_mirrorAllOperation=49`, `o3d_incline=42` (draft), axes `o3d_axisOX/OY/OZ=71/72/73`. Definitions:
|
||||
`ksBossExtrusionDefinition`/`ksCutExtrusionDefinition`/`ksBossRotatedDefinition`/`ksFilletDefinition`/
|
||||
`ksChamferDefinition`/`ksShellDefinition`/`ksRibDefinition`/`ksBaseEvolutionDefinition`/
|
||||
`ksBaseLoftDefinition`/`ksPlaneOffsetDefinition`/`ksMeshCopyDefinition`/`ksCircularCopyDefinition`/
|
||||
`ksMirrorCopyDefinition`/`ksMirrorCopyAllDefinition`/`ksInclineDefinition`. Cut-through holes use
|
||||
`dtBoth` + `etThroughAll`.
|
||||
- **Face/edge selection**: `ksPart.EntityCollection(o3d_face|o3d_edge)` → `SelectByPoint(x,y,z)` (world mm)
|
||||
→ `GetByIndex(0)`; or by stable index from `list_faces`/`list_edges` (helpers `SelectFaceByIndex`/
|
||||
`SelectEdgeByIndex`, range-checked before mutation; by-index is more reliable than by-point). Classify:
|
||||
`ksFaceDefinition.IsPlanar/IsCylinder/…` + `GetArea(ST_MIX_MM)`; `ksEdgeDefinition.IsLineSeg/IsCircle/IsArc/…`
|
||||
+ `GetLength()`. A sketch axis is a line with system style **3** (осевая).
|
||||
- **МЦХ / mass props**: use **API5 `ksPart.CalcMassInertiaProperties(ST_MIX_MM|ST_MIX_KG)`** →
|
||||
`ksMassInertiaParam` (`v`/`m`/`F`/`xc`/`yc`/`zc`), computed on demand. **Do NOT** use API7
|
||||
`IMassInertiaParam7.Calculate()` — its `Actual` flag sticks after the first calc and won't refresh on
|
||||
API5 geometry changes (stale volume). Bounding box: `ksPart.GetGabarit(full:false, …)`.
|
||||
- **Snapshot**: `ksDocument3D.SaveAsToRasterFormat(file, ksRasterFormatParam)` renders to a temp file (the
|
||||
in-memory `resultArrayBytes` stays empty when a filename is given); read the bytes back. Return to MCP via
|
||||
`ImageContentBlock.FromBytes(bytes, mime)` — **not** `Data = bytes` (Data holds base64). Use
|
||||
`model_snapshot` only for visually-spatial questions; **prefer `describe_model` first** (structural
|
||||
«passport» — bbox + МЦХ + bodies + topology + feature tree + variables, no image token cost).
|
||||
- Logs go to **stderr** (stdout is the MCP channel). Integration tests reuse one КОМПАС via `KompasFixture`;
|
||||
artifacts land in gitignored `.scratch/`. All integration test classes inherit `IntegrationTestBase`
|
||||
(`IAsyncLifetime`) → `DocumentService.CloseAllAsync` after each test (prevents tab accumulation).
|
||||
- **Feature tree API**: read via API5 `ksPart.GetFeature()` → `(ksFeature).SubFeatureCollection(true,false)`
|
||||
→ `ksFeatureCollection`. Casting `(ksFeature)part` does NOT work (RCW QI returns null) — must use
|
||||
`GetFeature()`. `ksFeature.type` returns only a coarse `o3d_entity` and does NOT distinguish operations;
|
||||
the precise type and params come from `ksFeature.GetObject()` → `ksEntity.GetDefinition()` + the matching
|
||||
definition (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`,
|
||||
`ksChamferDefinition.GetChamferParam`, …). Node names («Элемент выдавливания:1») are localized by КОМПАС.
|
||||
Features registered by id in `_features` support patterns/mirror; helpers `RequireSketch`/`RequireFeature`.
|
||||
- **Variables (API5)**: create/read via `ksPart.GetFeature().VariableCollection` (property on the root
|
||||
ksFeature) — **NOT** `ksPart.VariableCollection()` (returns external-only). Expression is the leading
|
||||
field; value is derived. Apply via `ksPart.RebuildModel()` (check the return); after a rebuild the RCW
|
||||
variable is stale → re-read via a fresh collection (`ReadValueFresh`). Dependents recalculate
|
||||
automatically; deletion fails while dependents exist (`RemoveVariable` returns FALSE).
|
||||
**Geometry limitation:** variables drive geometry only in a parametric model (sketch dims linked to
|
||||
variable names); our sketches use literal coordinates → `set_variable` stores/computes the value but does
|
||||
NOT move geometry. Parametric sketches are **unavailable via COM** (`ksCDimWithVariable` is not
|
||||
constructible from external automation — see memory `kompas-parametric-sketch-findings`; do not retry
|
||||
without new information).
|
||||
- **Topology / measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` /
|
||||
`GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` /
|
||||
`GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody`
|
||||
(`IsSolid` / `FaceCollection`); measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`,
|
||||
`unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`).
|
||||
- **«STEP import without history»** is detected structurally: `bodyCount > 0 && no formative features &&
|
||||
features.Count <= bodyCount+1` (only origin + body). A STEP that has been edited («Смещённая плоскость» /
|
||||
«Разрезать» / «Переместить грани» / «Булева операция») already has history.
|
||||
- **STEP import/export COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)`
|
||||
(pass the format code, not a DLL path) → `IConverter.ConverterParameters(cmd)` →
|
||||
`IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`.
|
||||
Export: `ConvertToAdditionFormat` (format codes AP203/AP214/AP242). Param co-classes
|
||||
(`AdditionConvertParameters` etc.) are NOT CoCreatable — only via the converter factories. Assembly
|
||||
traversal (`list_components`): `IKompasDocument3D.TopPart → IPart7.Parts (IParts7)`. Extract a component:
|
||||
`prm.NeedCreateComponentsFiles=true` (writes .m3d files next to the STEP) + `IPart7.OpenSourceDocument`.
|
||||
- **Direct B-rep face editing (`move_face`)** (verified): API7 face via
|
||||
`IPart7.FindObjectsByPoint(x,y,z,true)` → cast to `KompasAPI7.IFace`; containers via runtime COM-QI
|
||||
`(ISurfaceContainer)part`, `(IModelContainer)part`; `FaceMover` (SetFaces + Offset + Direction + Update)
|
||||
moves the face. distance>0 = outward (adds material), <0 = inward. Works on parametric and imported B-rep.
|
||||
The full split-reposition workflow (SplitSolids/BodyRepositions) is driveable; only partly exposed as tools
|
||||
(`split_solid_by_plane`, `move_body`, `boolean_union`).
|
||||
- **Forming ops (API5 definitions)**:
|
||||
- `shell` (`ksShellDefinition`, o3d_shellOperation=43): `thickness` + `thinType` (bool, `= !outward`) +
|
||||
`FaceArray()` (≥1 open face by index). Transient RCWs intentionally not released (consistent with
|
||||
Fillet/Chamfer).
|
||||
- `rib` (`ksRibDefinition`, =44): `SetSketch(sketch)` → `index=0`, `angle`, `side`
|
||||
(`left|right|up|down` → 0/1/2/3 via `SketchGeometry.RibSide()`); `SetThinParam(dtBoth, t/2, t/2)` if
|
||||
symmetric, else `(dtNormal, t, 0)`. Empirical: the rib contour must float in the gap (not touch the body)
|
||||
— КОМПАС extends the web automatically.
|
||||
- `sweep` (`ksBaseEvolutionDefinition`, =45): `SetSketch(profile)` → `PathPartArray().Add(path)` →
|
||||
`sketchShiftType=0` → `SetThinParam(false)` → `Create()`. Profile and path on different (usually ⟂)
|
||||
planes; `profileSketchId != pathSketchId`.
|
||||
- `loft` (`ksBaseLoftDefinition`, =30): `Sketchs().Add(each)` → `SetLoftParam(false,false,true)` →
|
||||
`SetThinParam(false)`. ≥2 closed sections on parallel planes — use `sketch_create_on_offset_plane` for
|
||||
non-base sections.
|
||||
- offset plane (`sketch_create_on_offset_plane`, `ksPlaneOffsetDefinition`, o3d_planeOffset=14):
|
||||
`SetPlane(base)` + `offset` mm + `direction` → `Create()` → sketch on it (`OpenSketchOnOffsetPlaneAsync`).
|
||||
- `draft` (`ksInclineDefinition`, o3d_incline=42 — API5 calls the op **Incline**, NOT Draft): `FaceArray()`
|
||||
(by index) + `SetPlane(neutral base plane)` + `angle` (degrees) + `direction`. **Empirical, opposite to
|
||||
the SDK docs:** `direction=false` = expanding/outward (adds material), `true` = tapering/inward → the code
|
||||
maps `def.direction = !outward`. Validate `0<angle<90`. (`o3d_DraftFromEdges=644` / `IDraftFromEdges` is
|
||||
a different op — not used.)
|
||||
- **Patterns & mirror (API5)**: `linear_pattern` (`ksMeshCopyDefinition`, =35): `SetAxis1(axis)` +
|
||||
`SetCopyParamAlongAxis(true,0,count,step,false)` + `count2=1` (disables the 2nd direction → 1D); features
|
||||
via `OperationArray()`. `circular_pattern` (`ksCircularCopyDefinition`, =36): `SetAxis(axis)`; `count1=1`
|
||||
(radial off), `count2=count` (ring), `step2=angle°` between neighbours, `factor2=false`, `inverce=reverse`;
|
||||
`GetOperationArray().Add(feature)`. `mirror_operation` (`ksMirrorCopyDefinition`, =48): `SetPlane(base)` +
|
||||
`GetOperationArray().Add(feature)`. `mirror_body` (`ksMirrorCopyAllDefinition`, =49): `SetPlane(base)`;
|
||||
`ChooseBodies()` casts to null in the interop and is NOT needed — it mirrors all bodies keeping the original
|
||||
(volume doubles). Empirical: `count` includes the original instance (count=3 → 3 instances). Axes:
|
||||
`CoordinateAxis{X,Y,Z}` → `o3d_axisOX/OY/OZ` (`Core/Modeling/CoordinateAxis.cs`, analogous to `BasePlane.cs`).
|
||||
- **Hole ops (API7 only — `ksHoleDefinition` is absent from the API5 interop; `HoleService`)**:
|
||||
`HoleCore(ksHoleTypeEnum holeType, Action<IHole3D> configure)` — `configure` sets `HoleParameters` **after**
|
||||
`HoleType` is assigned (the type-specific sub-interface is inaccessible until the type is fixed). Placement
|
||||
is shared: `IModelContainer`/`IHole3D`/`IHoleDisposal` + `Points3D`, direction chosen by volume delta,
|
||||
orphan rollback. None are registered in `_features` → no id returned. `hole`=`ksHTBase`, configure=null.
|
||||
`hole_counterbore`=`ksHTCounterbore` + `(ISpotfacingHoleParameters)` (`SpotfacingDiameter`/`SpotfacingDepth`).
|
||||
`hole_countersink`=`ksHTCountersinking` + `(ICountersinkHoleParameters)` (`CountersinkType=ksCTDiameterAngle`,
|
||||
`CountersinkDiameter`/`Angle`). `hole_conic`=`ksHTConic` + `(IConicHoleParameters)` (`ConicType=ksCNAngle`,
|
||||
`ConicAngle`).
|
||||
- **Assembly (API7, `AssemblyService`, namespace `Kompas.Mcp.Core.Assemblies`)**: `assembly_add_component` —
|
||||
`IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)` → `IPart7.Placement` (`IPlacement3D`) →
|
||||
`SetOrigin(x,y,z)` (mm, assembly world CS) → `UpdatePlacement(true)` → `top.RebuildModel(true)`.
|
||||
**Empirical:** `UpdatePlacement` returns FALSE for a manually-positioned component (no mates) — NOT an error.
|
||||
Validate `DocumentType==ksDocumentAssembly` before touching `TopPart`; rollback via `IFeature7.Delete` +
|
||||
`RebuildModel`. `assembly_add_mate` — `top.MateConstraints` (`IMateConstraints3D`) →
|
||||
`Add(MateConstraintType)` → `BaseObject1/2` (faces via `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) →
|
||||
`ParamValue` (distance) → `Update()` → `RebuildModel(true)`; check `mate.Valid` (false → rollback
|
||||
`mate.Owner.Delete`). Types `coincidence` (mc_Coincidence=0) and `distance` (mc_Distance=5, value>0) are
|
||||
verified live; the rest are backlog.
|
||||
- **Drawing (API7, `DrawingService`, namespace `Kompas.Mcp.Core.Drawings`)** — entry via `IKompasDocument2D`;
|
||||
requires an active drawing document (`DocumentType==ksDocumentDrawing`). All coordinates are **view-local**
|
||||
(mm) unless noted. The model file must be saved before placing views.
|
||||
- `drawing_create_standard_views`: `doc2d.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди",
|
||||
projTypes, x, y, scale, dx=20, dy=20)` where `projTypes=object[]{1,3,5}` (Front/Up/Left as SAFEARRAY VT_I4).
|
||||
Success: `created==3`; content check: sum of the new views' `IView.ObjectCount` > 0 else rollback
|
||||
`IView.Delete`. Returns `ViewNumbers` (the `IView.Number` values — address views by these when placing
|
||||
annotations).
|
||||
- `drawing_fill_title_block`: `doc2d.LayoutSheets.ItemByNumber[1].Stamp` (`IStamp`) →
|
||||
`stamp.Text[columnId].Str = text` (indexed property; `IText.Str` overwrites, no Clear needed) →
|
||||
`stamp.Update()`. Fields→columns (`ksStampEnum`): Name=1, Designation=2, Material=3. Non-transactional
|
||||
(all cells → a single `Update`).
|
||||
- **Dimensions go into `(ISymbols2DContainer)view`, NOT the sheet, and are NOT counted in `IView.ObjectCount`**
|
||||
(that counts geometry in `IDrawingContainer`) — verify placement via the container's `*.Count`. Common flow:
|
||||
`Add()` → set coords → `AutoNominalValue=true` → `Update()` → check `Valid` → read
|
||||
`((IDimensionText)dim).NominalValue`; rollback `dim.Delete()` on FALSE/invalid/zero. Leader and angle are in
|
||||
**radians** (`DimensionAngles.ToRadians`). Free placement = set coordinates directly; **associative**
|
||||
(diametral/radial only) = set `BaseObject` to a projected circle → the value is read from geometry.
|
||||
- linear (`ILineDimension`): `X1,Y1,X2,Y2` (extension points) + `X3,Y3` (dimension line) + `Orientation`
|
||||
(`DimensionOrientation{Horizontal,Vertical,Parallel}` → `ksLinDParallel=0`/`Horizontal=1`/`Vertical=2`;
|
||||
parallel needs no explicit Angle).
|
||||
- diametral (`IDiametralDimension`): `Xc,Yc,Radius` + `Angle`; `NominalValue` = 2·Radius.
|
||||
- radial (`IRadialDimension`): `Xc,Yc,Radius` + `Angle`, `DimensionType=true`; **`NominalValue` = Radius,
|
||||
NOT diameter** — contrary to the SDK docs, confirmed by a spike.
|
||||
- **associative** diametral/radial (`associate=true`): instead of coordinates, find a projected circle in
|
||||
`(IDrawingContainer)view.Circles` by a center+radius key (`CircularObjectMatch.SelectMatchIndex`, tol 1 mm;
|
||||
throws on none/ambiguous; concentric disambiguated by radius), set `dim.BaseObject = circle` (`_Circle`
|
||||
implements `IDrawingObject`) → `NominalValue` read from geometry. `RequireViewContainers` gives both QIs;
|
||||
the free path stays on `RequireSymbols2DContainer`. Arc/angular/rough binding — not yet (needs its own
|
||||
spike; `ILineDimension` has no `BaseObject`).
|
||||
- angular (`IAngleDimension`, `AngleDimensions.Add(ksDrADimension=10)`): vertex `Xc,Yc` + side points
|
||||
`X1,Y1`/`X2,Y2` + arc-position `X3,Y3` (positions the arc only, does NOT pick the angle); the measured
|
||||
angle is chosen by `DimensionType` = `AngleDimensionType{Min,Max,More}` (on rays 0°/45° → min=45° /
|
||||
max=135° supplement / more=315° reflex). `NominalValue` in degrees.
|
||||
- `drawing_add_rough` (`Roughs.Add()`/`IRough`: `BranchX0/Y0`, `Angle`) + `(IRoughParams)rough` QI:
|
||||
`SignType` = `RoughSignType{NoProcessing,DeleteMaterial,WithoutDeleteMaterial}`, value (Ra/Rz) =
|
||||
`RoughParamText.Str`.
|
||||
- `drawing_add_text` — **text lives in `IDrawingContainer`, NOT `ISymbols2DContainer`, and NOT in
|
||||
`IView.ObjectCount`**: `(IDrawingContainer)view.DrawingTexts.Add()`/`IDrawingText` (`X/Y/Angle`) +
|
||||
`(IText)dt.Str` = text; count via `DrawingTexts.Count`. Do NOT set `Height` (that's block height, not font).
|
||||
- `drawing_set_technical_requirements` — **document-level**: `(IDrawingDocument)doc.TechnicalDemand` /
|
||||
`ITechnicalDemand`: `td.Text.Str = text` → `td.Update()` (`IsCreated` False→True on first set; overwrites;
|
||||
`\n` multiline).
|
||||
- `drawing_add_leader` — `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`. **Critical order
|
||||
(spike):** a fresh leader has 0 branches → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (the tip) BEFORE
|
||||
`SetBranchTextPosition(textX,textY)`/`Update`, otherwise КОМПАС dies with `RPC_E_SERVERFAULT`. Text —
|
||||
`(ILeader)bl.TextOnShelf.Str`; `ShelfDirection` (enum `ShelfDirection{Auto,Right,Left,Up,Down}`, Auto = leave
|
||||
unset). `AddBranchByPoint`/`SetBranchTextPosition` return bool — check them.
|
||||
- `drawing_set_sheet_format` — `RequireLayoutSheet(n).Format` (`ISheetFormat`): `Format`=`ksDocumentFormatEnum`
|
||||
(`PaperFormat{A0..A5,User}` + `PaperFormats.Parse/ToKompas/FromKompas`); standard → set
|
||||
`VerticalOrientation=!landscape` (W/H auto-computed from enum+orientation, e.g. A3 landscape → 420×297);
|
||||
**User → set `FormatWidth/Height` only, КОМПАС derives `VerticalOrientation` from W/H, ignores the flag and
|
||||
never swaps W/H (spike)** → `sheet.Update()`. A new drawing defaults to A4 portrait.
|
||||
`ValidateFormatDimensions`: User needs W/H>0, standard forbids W/H (must be 0).
|
||||
- `DrawingAnnotationResult{Value(read-back), ViewNumber}` is the shared result for rough/text/leader.
|
||||
- **Richer sketch primitives (API5 `ksDocument2D` wrappers)**: `ksArcBy3Points`; `ksArcByAngle` (centre/radius/
|
||||
start-end angles in degrees + counterClockwise flag); `ksEllipse` via `ksEllipseParam`
|
||||
(`KompasObject.GetParamStruct(ko_EllipseParam=22)`; **the property names are `A`/`B` UPPERCASE in the interop**);
|
||||
`ksRegularPolygon` via `ksRegularPolygonParam` (`ko_RegularPolygonParam=92`; `describe = !inscribed`); a NURBS
|
||||
spline via `ksNurbs(order=4)` + `ksNurbsPoint` loop + `ksEndObj`; `ksPoint`. Param structs are released after
|
||||
use. `PartModeler` is partial: `.cs` (registry/helpers/`NewParam<T>`/reset), `.Sketch.cs` (2D primitives),
|
||||
`.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` (`Core/Modeling`) holds the enum maps
|
||||
+ validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`, `RequireMin`). Point lists use
|
||||
`record SketchPoint(X, Y)` (JSON names `x`/`y`).
|
||||
@@ -0,0 +1,79 @@
|
||||
# Плагин `kompas`, канал поставки и CI
|
||||
|
||||
Перенесено из `CLAUDE.md`: нужно при выпуске релиза и правке CI, а не в каждой сессии.
|
||||
Файл лежит в навыке (`.claude/skills/kompas-mcp-dev/`), а **не** в `plugin/CLAUDE.md`, потому что
|
||||
`plugin/` распространяется пользователям — внутренняя кухня релиза туда попадать не должна.
|
||||
|
||||
## Claude Code plugin (`plugin/`)
|
||||
|
||||
The server and both published skills ship as the plugin **`kompas`** — `plugin/` is a self-contained tree,
|
||||
distributed independently of the rest of the repo.
|
||||
|
||||
- `plugin/.claude-plugin/plugin.json` — manifest: `name: "kompas"`, `version` (currently `0.0.0`).
|
||||
Version is Claude Code's cache key — `/plugin update` and auto-update skip a plugin whose version
|
||||
matches what's installed, so **every release must bump it**.
|
||||
- `plugin/server.lock.json` — pins exactly one server version: `{version, sourceSha, url, sha256}`.
|
||||
`0.0.0` with empty fields = no release published yet.
|
||||
- `plugin/skills/{kompas-3d,kompas-fdm-design}/` — the source of truth for both published skills.
|
||||
- `plugin/commands/doctor.md` — `/kompas:doctor`: compares the expected version (from the lock) against
|
||||
the `KOMPAS_MCP_EXE` override / `install.json` marker / `kompas-mcp.exe --version`, checks whether
|
||||
КОМПАС is installed and running, then `kompas_connect` → `kompas_status`.
|
||||
- `plugin/.mcp.json` — declares the `kompas` MCP server as
|
||||
`powershell -File ${CLAUDE_PLUGIN_ROOT}/scripts/launch-kompas-mcp.ps1`.
|
||||
- `plugin/scripts/KompasMcpBootstrap.psm1` — delivery: `Read-KompasMcpLock` / `Test-KompasMcpInstall` /
|
||||
`Install-KompasMcpServer` / `Resolve-KompasMcpExecutable`. Downloads the release asset, verifies SHA256,
|
||||
unpacks under `%LOCALAPPDATA%\kompas-mcp\.tmp` and atomically moves it to
|
||||
`%LOCALAPPDATA%\kompas-mcp\<version>`, writes an `install.json` marker. An install is trusted only when
|
||||
the marker's `version`/`sha256` match the lock.
|
||||
- `plugin/scripts/launch-kompas-mcp.ps1` — entry point: the `KOMPAS_MCP_EXE` env var wins (dev mode),
|
||||
otherwise deliver per the lock; runs the server and forwards its exit code. Invariant: **not a single
|
||||
byte reaches stdout before the server starts** (stdout is the JSON-RPC channel) — all diagnostics go to
|
||||
stderr prefixed `kompas-mcp launcher:`.
|
||||
- `plugin/README.md` — prerequisites and install; `plugin/adapters/{codex,opencode}/` — config snippets
|
||||
(not separate distributions) wiring the same launcher into Codex and opencode.
|
||||
- CLI flag `--version` (`Core/Startup/CliArguments.IsVersionRequest`, early-exit in `Program.cs` before
|
||||
the host and any КОМПАС contact) — prints the 3-part assembly version; `/kompas:doctor` uses it to
|
||||
cross-check the installed binary against the lock.
|
||||
|
||||
**Gotchas:**
|
||||
- **Plugin PowerShell files must be UTF-8 *with BOM*.** The launcher runs under `powershell` (Windows
|
||||
PowerShell 5.1), which reads BOM-less files in the system ANSI codepage — Cyrillic string literals then
|
||||
corrupt parsing of the whole script. `pwsh` 7 doesn't reproduce this (BOM auto-detected either way).
|
||||
- Switching branch/commit across a `plugin/skills` boundary can dangle the skill junctions mid-checkout —
|
||||
same failure mode and fix as in `CLAUDE.md` §«Skills and their layout».
|
||||
|
||||
## Distribution channel — the `dist` branch, not `main`
|
||||
|
||||
Releases (`.gitea/workflows/release.yml`, tag `vX.Y.Z`): `dotnet publish -r win-x64 --self-contained` →
|
||||
zip → idempotent upsert of a Gitea release + asset via the Gitea API → re-verify the downloaded asset's
|
||||
SHA256 → commit updated `server.lock.json`/`plugin.json` to branch **`dist`**, on top of the tagged commit.
|
||||
A private plugin marketplace (`home-repo-cc`) will reference the plugin via a `git-subdir` source pinned to
|
||||
`ref: dist` — **not added yet**, and there is **no release yet**. Rationale: a release is built from the
|
||||
tagged commit; if the marketplace pointed at `main`, a user could get a newer `plugin/` tree under a
|
||||
version whose binary was built from an older commit.
|
||||
|
||||
`.gitea/workflows/ci.yml` runs on push/PR to `main`: `plugin.json`↔`server.lock.json` consistency check
|
||||
(jq), build, then unit tests filtered `Category=Unit&Requires!=Windows` (245 of the 250). Runner:
|
||||
`gitea-runner` (docker executor, label `ubuntu-latest`).
|
||||
|
||||
CI gotchas:
|
||||
- **SDK 8 doesn't understand the `.slnx` solution format** (`MSBUILD : error MSB1003`) — CI builds and
|
||||
tests explicit project paths (`src/Kompas.Mcp.Host/Kompas.Mcp.Host.csproj`,
|
||||
`tests/Kompas.Mcp.Tests/Kompas.Mcp.Tests.csproj`) instead of the solution. Not reproducible locally with SDK 9+.
|
||||
- **`net8.0-windows` builds and runs unit tests fine on the Linux runner** with
|
||||
`-p:EnableWindowsTargeting=true`. ASCON interop DLLs link as ordinary references; `ole32`/`oleaut32` are
|
||||
only needed at COM runtime, not build time. The 5 `DispatcherTests` are tagged
|
||||
`[Trait("Requires", "Windows")]` and excluded — `Thread.SetApartmentState(STA)` throws
|
||||
`PlatformNotSupportedException` on Linux.
|
||||
- **`.gitignore`'s `.mcp.json` rule is anchored to the repo root** (`/.mcp.json`) — without the leading
|
||||
slash the glob also matched the tracked, portable `plugin/.mcp.json`.
|
||||
- Integration tests never run in CI (they need a running КОМПАС with GUI and a license) — a hard boundary,
|
||||
not a future task.
|
||||
|
||||
## Обновление RAG-базы справки SDK
|
||||
|
||||
Когда выходит новая версия КОМПАС — в репозитории `kompas-sdk-docs`, на машине с установленным КОМПАС:
|
||||
`pwsh tools/update_sdk_docs.ps1 -SdkVersion 25` перегенерирует `sdk/` из `SDK\Help\KOMPAS_SDK_ru-RU.zip`;
|
||||
просмотри диф (**удалённые статьи = сломался парсер**, а не усохший API), закоммить, поставь тег `sdk-vNN`,
|
||||
запушь — Gitea Actions на `rag-node` перевалидирует и переключит алиас `kompas_sdk`. Индексация
|
||||
инкрементальная, по контрольным суммам.
|
||||
@@ -8,10 +8,11 @@ An **MCP (Model Context Protocol) server** that lets an LLM drive the CAD system
|
||||
The server is a COM client of an installed КОМПАС: it exposes CAD operations (documents, 2D sketches,
|
||||
3D parts, assemblies, drawings, STEP round-trip, model inspection) as MCP tools over stdio.
|
||||
|
||||
**There is no `docs/` directory** — it was deleted deliberately (commit `b022a91`). This file and
|
||||
[`README.md`](README.md) are the only prose documentation in the repo; verified COM call-chains live
|
||||
in «Key implementation facts» below plus the memory files (`kompas-*-api*.md`). Do not re-create
|
||||
`docs/ARCHITECTURE.md`, `docs/TODO.md`, `docs/OPEN_QUESTIONS.md` — anything worth keeping goes here.
|
||||
**There is no `docs/` directory** — it was deleted deliberately (commit `b022a91`). Do not re-create
|
||||
`docs/ARCHITECTURE.md`, `docs/TODO.md`, `docs/OPEN_QUESTIONS.md`. Prose documentation lives in exactly
|
||||
three places: this file (always loaded — architecture, principles, backlog, traps), the skill
|
||||
`kompas-mcp-dev` with `reference/*.md` (loaded on demand — verified COM call-chains, release and CI),
|
||||
and [`README.md`](README.md) (the tool catalog). Plus the memory files (`kompas-*-api*.md`).
|
||||
|
||||
Authoritative API references:
|
||||
- **SDK knowledge base in RAG: MCP server `kompas-sdk`** — 2465 articles (one per COM interface/topic),
|
||||
@@ -23,29 +24,11 @@ Authoritative API references:
|
||||
are the last word on numeric constant values)
|
||||
- `libs/kompas-interop/*.dll` — reflect over these to verify any signature a research answer gives you.
|
||||
|
||||
### Navigating the SDK base (MCP `kompas-sdk`, via the subagent)
|
||||
|
||||
The base is distilled from the 26 000-page Help&Manual WebHelp export: one article per COM
|
||||
interface/topic under `sdk/interfaces|enums|structures|guides` (+ `resources/` images), each with
|
||||
frontmatter (`title`, `type`, `api`, `domain`, `tags`, `sdk_version`); methods/properties are `## `
|
||||
headings inside the interface article. Search order:
|
||||
|
||||
- **`grep_knowledge`** for an exact name — method, interface, enum, constant (`SetSideParam`,
|
||||
`ksHoleTypeEnum`, `o3d_incline`). Method names are `## SetSideParam - …` headings. Regex works.
|
||||
- **`search_knowledge`** for task-shaped questions («чем построить массив по сетке»), with filters
|
||||
`type` (`interface`/`enum`/`struct`/`guide`), `tags` (`api7`, `api5`, `3d`, `sketch`, …),
|
||||
`path_prefix` (`sdk/enums/`).
|
||||
- **`get_chunk_context`** to read around a hit; `get_document` only for small articles —
|
||||
`sdk/interfaces/геометрия.md` is 261 KB and will blow the context.
|
||||
- **`knowledge_status`** — indexed commit, document and point counts, when a search looks suspiciously empty.
|
||||
|
||||
Covers the **COM API7/API5** reference this project uses. The cross-platform Qt/C++ **KsAPI** flavour
|
||||
(`ksapi_*`, non-COM) is intentionally **excluded** — ignore unless explicitly working with KsAPI.
|
||||
|
||||
Updating the base when a new КОМПАС ships (in `kompas-sdk-docs`, on the machine with КОМПАС installed):
|
||||
`pwsh tools/update_sdk_docs.ps1 -SdkVersion 25` regenerates `sdk/` from `SDK\Help\KOMPAS_SDK_ru-RU.zip`;
|
||||
review the diff (deleted articles = broken parser, not a shrunken API), commit, tag `sdk-vNN`, push —
|
||||
Gitea Actions on `rag-node` revalidates and swaps the `kompas_sdk` alias. Indexing is incremental by checksum.
|
||||
The base covers the **COM API7/API5** reference this project uses; the cross-platform Qt/C++ **KsAPI**
|
||||
flavour (`ksapi_*`, non-COM) is intentionally **excluded** — ignore unless explicitly working with KsAPI.
|
||||
How to search it is in the subagent's own definition (`.claude/agents/kompas-sdk-research.md`) — those
|
||||
tools are scoped to the subagent, this session cannot call them. How to regenerate the base when a new
|
||||
КОМПАС ships — skill `kompas-mcp-dev`, `reference/plugin-and-release.md`.
|
||||
|
||||
## Current state
|
||||
|
||||
@@ -53,30 +36,15 @@ Gitea Actions on `rag-node` revalidates and swaps the `kompas_sdk` alias. Indexi
|
||||
direct B-rep edit, STEP import/export, assemblies, 2D drawings, structural inspection — implemented and
|
||||
validated live. Also packaged as the Claude Code plugin `kompas` (no release published yet).
|
||||
|
||||
Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**.
|
||||
**84 MCP tools; 381 .NET tests green (250 unit + 131 integration) + 41 Pester tests** for the plugin's
|
||||
PowerShell (not counted in the .NET total).
|
||||
|
||||
The full tool catalog (by group, all 84) lives in [`README.md`](README.md) §«Инструменты» — the single
|
||||
The full tool catalog (by group) lives in [`README.md`](README.md) §«Инструменты» — the single
|
||||
source of truth; do not duplicate it here.
|
||||
|
||||
```
|
||||
src/Kompas.Mcp.Core/ COM layer: STA dispatcher, session, documents, modeling, drawings, conversion, inspection
|
||||
src/Kompas.Mcp.Host/ MCP stdio server; Tools/*.cs = one file per tool group
|
||||
tests/Kompas.Mcp.Tests/ unit (root) + Integration/ (needs a running КОМПАС)
|
||||
libs/kompas-interop/ ASCON interop DLLs — compile-time only, see «Interop assemblies are not shipped»
|
||||
plugin/ Claude Code plugin `kompas`: manifest, launcher, server lock, skills (source of truth)
|
||||
tools/ skill-junction script + Pester tests
|
||||
.claude/agents/ kompas-sdk-research (Haiku) — the only subagent left in this repo
|
||||
.claude/skills/ junctions onto plugin/skills + kompas-mcp-dev (internal, non-junctioned)
|
||||
```
|
||||
|
||||
```powershell
|
||||
dotnet build -c Release # build
|
||||
dotnet test --filter "Category=Unit" # 250 unit tests, no COM
|
||||
dotnet test --filter "Category=Integration" # 131 integration tests, needs running КОМПАС
|
||||
dotnet test --filter "Category=Unit" # no COM needed
|
||||
dotnet test --filter "Category=Integration" # needs a running КОМПАС
|
||||
dotnet run --project src/Kompas.Mcp.Host # start the MCP server (stdio)
|
||||
pwsh -NoProfile -File tools/tests/run-ps-tests.ps1 # 41 Pester tests (plugin PowerShell)
|
||||
pwsh -NoProfile -File tools/tests/run-ps-tests.ps1 # Pester tests for the plugin's PowerShell
|
||||
```
|
||||
|
||||
Principle: **MCP = translating SDK capabilities into general tools**, never task-specific ones. If a
|
||||
@@ -107,12 +75,8 @@ a *general* tool with tests, and describe the method in the `kompas-3d` skill.
|
||||
**Sync** (`tools/sync-agent-assets.ps1`, module `tools/AgentAssets.psm1`): the source of truth is
|
||||
`plugin/skills/{kompas-3d,kompas-fdm-design}`; `.claude/skills/<name>` and `.agents/skills/<name>` are
|
||||
**junctions** onto it created by the script (their paths are pinpointed in `.gitignore` — git sees a
|
||||
junction as a plain directory). Modes are mutually exclusive parameter sets (`-Remove -Check` together
|
||||
is a parameter-binding error):
|
||||
- no flags — link (`Created`/`AlreadyLinked`)
|
||||
- `-Check` — verify, prints `БИТО` and exits 1 on drift
|
||||
- `-AllowCopy` — fall back to a real copy (`Copied`) if a junction can't be created
|
||||
- `-Remove` — unlink (`Removed`/`Missing`/`Skipped`)
|
||||
junction as a plain directory). Its modes are mutually exclusive parameter sets, so `-Remove -Check`
|
||||
together is a parameter-binding error.
|
||||
|
||||
**Gotcha:** switching to a branch/commit predating `plugin/skills` leaves the junctions dangling and
|
||||
`git checkout` fails mid-way (`unable to create file .claude/skills/kompas-fdm-design/SKILL.md: No such
|
||||
@@ -143,270 +107,25 @@ directly.)
|
||||
## Claude Code plugin (`plugin/`)
|
||||
|
||||
The server and both published skills ship as the plugin **`kompas`** — `plugin/` is a self-contained tree,
|
||||
distributed independently of the rest of the repo.
|
||||
distributed independently of the rest of the repo, released from the **`dist`** branch (never `main`).
|
||||
|
||||
- `plugin/.claude-plugin/plugin.json` — manifest: `name: "kompas"`, `version` (currently `0.0.0`).
|
||||
Version is Claude Code's cache key — `/plugin update` and auto-update skip a plugin whose version
|
||||
matches what's installed, so **every release must bump it**.
|
||||
- `plugin/server.lock.json` — pins exactly one server version: `{version, sourceSha, url, sha256}`.
|
||||
`0.0.0` with empty fields = no release published yet.
|
||||
- `plugin/skills/{kompas-3d,kompas-fdm-design}/` — the source of truth for both published skills.
|
||||
- `plugin/commands/doctor.md` — `/kompas:doctor`: compares the expected version (from the lock) against
|
||||
the `KOMPAS_MCP_EXE` override / `install.json` marker / `kompas-mcp.exe --version`, checks whether
|
||||
КОМПАС is installed and running, then `kompas_connect` → `kompas_status`.
|
||||
- `plugin/.mcp.json` — declares the `kompas` MCP server as
|
||||
`powershell -File ${CLAUDE_PLUGIN_ROOT}/scripts/launch-kompas-mcp.ps1`.
|
||||
- `plugin/scripts/KompasMcpBootstrap.psm1` — delivery: `Read-KompasMcpLock` / `Test-KompasMcpInstall` /
|
||||
`Install-KompasMcpServer` / `Resolve-KompasMcpExecutable`. Downloads the release asset, verifies SHA256,
|
||||
unpacks under `%LOCALAPPDATA%\kompas-mcp\.tmp` and atomically moves it to
|
||||
`%LOCALAPPDATA%\kompas-mcp\<version>`, writes an `install.json` marker. An install is trusted only when
|
||||
the marker's `version`/`sha256` match the lock.
|
||||
- `plugin/scripts/launch-kompas-mcp.ps1` — entry point: the `KOMPAS_MCP_EXE` env var wins (dev mode),
|
||||
otherwise deliver per the lock; runs the server and forwards its exit code. Invariant: **not a single
|
||||
byte reaches stdout before the server starts** (stdout is the JSON-RPC channel) — all diagnostics go to
|
||||
stderr prefixed `kompas-mcp launcher:`.
|
||||
- `plugin/README.md` — prerequisites and install; `plugin/adapters/{codex,opencode}/` — config snippets
|
||||
(not separate distributions) wiring the same launcher into Codex and opencode.
|
||||
- CLI flag `--version` (`Core/Startup/CliArguments.IsVersionRequest`, early-exit in `Program.cs` before
|
||||
the host and any КОМПАС contact) — prints the 3-part assembly version; `/kompas:doctor` uses it to
|
||||
cross-check the installed binary against the lock.
|
||||
Manifest/lock layout, the delivery bootstrap, the launcher's stdout invariant, the release workflow and the
|
||||
CI gotchas — skill **`kompas-mcp-dev`**, `reference/plugin-and-release.md`. Two rules that must not wait
|
||||
for a skill load:
|
||||
|
||||
**Gotchas:**
|
||||
- **Plugin PowerShell files must be UTF-8 *with BOM*.** The launcher runs under `powershell` (Windows
|
||||
PowerShell 5.1), which reads BOM-less files in the system ANSI codepage — Cyrillic string literals then
|
||||
corrupt parsing of the whole script. `pwsh` 7 doesn't reproduce this (BOM auto-detected either way).
|
||||
- Switching branch/commit across a `plugin/skills` boundary can dangle the skill junctions mid-checkout —
|
||||
same failure mode and fix as in the skill-layout section above.
|
||||
|
||||
### Distribution channel — the `dist` branch, not `main`
|
||||
|
||||
Releases (`.gitea/workflows/release.yml`, tag `vX.Y.Z`): `dotnet publish -r win-x64 --self-contained` →
|
||||
zip → idempotent upsert of a Gitea release + asset via the Gitea API → re-verify the downloaded asset's
|
||||
SHA256 → commit updated `server.lock.json`/`plugin.json` to branch **`dist`**, on top of the tagged commit.
|
||||
A private plugin marketplace (`home-repo-cc`) will reference the plugin via a `git-subdir` source pinned to
|
||||
`ref: dist` — **not added yet**, and there is **no release yet**. Rationale: a release is built from the
|
||||
tagged commit; if the marketplace pointed at `main`, a user could get a newer `plugin/` tree under a
|
||||
version whose binary was built from an older commit.
|
||||
|
||||
`.gitea/workflows/ci.yml` runs on push/PR to `main`: `plugin.json`↔`server.lock.json` consistency check
|
||||
(jq), build, then unit tests filtered `Category=Unit&Requires!=Windows` (245 of the 250). Runner:
|
||||
`gitea-runner` (docker executor, label `ubuntu-latest`).
|
||||
|
||||
CI gotchas:
|
||||
- **SDK 8 doesn't understand the `.slnx` solution format** (`MSBUILD : error MSB1003`) — CI builds and
|
||||
tests explicit project paths (`src/Kompas.Mcp.Host/Kompas.Mcp.Host.csproj`,
|
||||
`tests/Kompas.Mcp.Tests/Kompas.Mcp.Tests.csproj`) instead of the solution. Not reproducible locally with SDK 9+.
|
||||
- **`net8.0-windows` builds and runs unit tests fine on the Linux runner** with
|
||||
`-p:EnableWindowsTargeting=true`. ASCON interop DLLs link as ordinary references; `ole32`/`oleaut32` are
|
||||
only needed at COM runtime, not build time. The 5 `DispatcherTests` are tagged
|
||||
`[Trait("Requires", "Windows")]` and excluded — `Thread.SetApartmentState(STA)` throws
|
||||
`PlatformNotSupportedException` on Linux.
|
||||
- **`.gitignore`'s `.mcp.json` rule is anchored to the repo root** (`/.mcp.json`) — without the leading
|
||||
slash the glob also matched the tracked, portable `plugin/.mcp.json`.
|
||||
- Integration tests never run in CI (they need a running КОМПАС with GUI and a license) — a hard boundary,
|
||||
not a future task.
|
||||
- **Every release must bump `plugin/.claude-plugin/plugin.json`** — the version is Claude Code's cache key,
|
||||
and `/plugin update` silently skips a plugin whose version already matches what's installed.
|
||||
- **Plugin PowerShell files must be UTF-8 *with BOM*.** The launcher runs under Windows PowerShell 5.1,
|
||||
which reads BOM-less files in the system ANSI codepage — Cyrillic string literals then corrupt parsing of
|
||||
the whole script. `pwsh` 7 doesn't reproduce this, so it passes locally and breaks for the user.
|
||||
|
||||
## Key implementation facts (don't relearn)
|
||||
|
||||
- **Interop assemblies are not shipped** (`KompasAPI7`, `Kompas6API5`, `Kompas6Constants`,
|
||||
`Kompas6Constants3D` belong to ASCON). `libs/kompas-interop` is referenced with `<Private>false</Private>`
|
||||
— compile-time only, absent from `bin/` **and** from the published archive/`deps.json`. At runtime
|
||||
`KompasInteropLoader.Install()` (first line of `Program.cs`) hooks `AssemblyLoadContext.Default.Resolving`;
|
||||
lookup order: `KOMPAS_INTEROP_DIR` → next to `kompas-mcp.exe` → cache → the installed КОМПАС.
|
||||
**The install has no loose interop DLLs** — they live only inside `<install>\SDK\Samples\CSharp.zip`
|
||||
under `Common/`, so the provider unzips them into `%LOCALAPPDATA%\kompas-mcp\interop\<key>` (key = hash
|
||||
of the zip's path+size+mtime, so a КОМПАС upgrade re-extracts; staging dir + `Directory.Move` for
|
||||
atomicity). The install dir comes from `HKCR\<ProgID>\CLSID` → `HKCR\CLSID\{…}\LocalServer32`
|
||||
(`"…\Bin\kHome.Exe"` on v24 Home) — `HKLM\SOFTWARE\ASCON` holds settings, not the path. All interop
|
||||
identities are `Version=1.0.0.0` with **no strong name**, so resolving by simple name is version-agnostic
|
||||
across КОМПАС releases. Failure throws `KompasInteropException` with an actionable message; `Program`
|
||||
logs it at startup (non-fatal — `--version` must work without КОМПАС) and `kompas_status` returns it
|
||||
(it checks interop **before** touching `KompasSession`, otherwise the MCP SDK shows only «An error
|
||||
occurred invoking …»). **The test project copies the DLLs itself**
|
||||
(`<None Include="$(KompasInteropDir)\*.dll">`) — the Linux CI runner has no КОМПАС to resolve from.
|
||||
- **All COM calls run on one dedicated STA thread** (`KompasDispatcher.InvokeAsync`); КОМПАС is
|
||||
single-instance STA. Services never touch COM off that thread.
|
||||
- **Connection**: API5 (`KOMPAS.Application.5` → `KompasObject`) then `ksGetApplication7()` → `IApplication`;
|
||||
the API5 root is needed for `ksPart` 3D building and `ksDocument3D` snapshots. API7 drives app/documents,
|
||||
holes, assemblies, drawings and direct B-rep edit.
|
||||
- **3D via API5 `ksPart`**: `NewEntity(o3d_*)` + matching definition; always check `entity.Create()` return.
|
||||
Entity codes: `o3d_sketch`/`o3d_bossExtrusion`/`o3d_cutExtrusion`/`o3d_bossRotated`/`o3d_fillet`/`o3d_chamfer`,
|
||||
`o3d_shellOperation=43`, `o3d_ribOperation=44`, `o3d_baseEvolution=45` (sweep), `o3d_baseLoft=30`,
|
||||
`o3d_planeOffset=14`, `o3d_meshCopy=35` (linear pattern), `o3d_circularCopy=36`, `o3d_mirrorOperation=48`,
|
||||
`o3d_mirrorAllOperation=49`, `o3d_incline=42` (draft), axes `o3d_axisOX/OY/OZ=71/72/73`. Definitions:
|
||||
`ksBossExtrusionDefinition`/`ksCutExtrusionDefinition`/`ksBossRotatedDefinition`/`ksFilletDefinition`/
|
||||
`ksChamferDefinition`/`ksShellDefinition`/`ksRibDefinition`/`ksBaseEvolutionDefinition`/
|
||||
`ksBaseLoftDefinition`/`ksPlaneOffsetDefinition`/`ksMeshCopyDefinition`/`ksCircularCopyDefinition`/
|
||||
`ksMirrorCopyDefinition`/`ksMirrorCopyAllDefinition`/`ksInclineDefinition`. Cut-through holes use
|
||||
`dtBoth` + `etThroughAll`.
|
||||
- **Face/edge selection**: `ksPart.EntityCollection(o3d_face|o3d_edge)` → `SelectByPoint(x,y,z)` (world mm)
|
||||
→ `GetByIndex(0)`; or by stable index from `list_faces`/`list_edges` (helpers `SelectFaceByIndex`/
|
||||
`SelectEdgeByIndex`, range-checked before mutation; by-index is more reliable than by-point). Classify:
|
||||
`ksFaceDefinition.IsPlanar/IsCylinder/…` + `GetArea(ST_MIX_MM)`; `ksEdgeDefinition.IsLineSeg/IsCircle/IsArc/…`
|
||||
+ `GetLength()`. A sketch axis is a line with system style **3** (осевая).
|
||||
- **МЦХ / mass props**: use **API5 `ksPart.CalcMassInertiaProperties(ST_MIX_MM|ST_MIX_KG)`** →
|
||||
`ksMassInertiaParam` (`v`/`m`/`F`/`xc`/`yc`/`zc`), computed on demand. **Do NOT** use API7
|
||||
`IMassInertiaParam7.Calculate()` — its `Actual` flag sticks after the first calc and won't refresh on
|
||||
API5 geometry changes (stale volume). Bounding box: `ksPart.GetGabarit(full:false, …)`.
|
||||
- **Snapshot**: `ksDocument3D.SaveAsToRasterFormat(file, ksRasterFormatParam)` renders to a temp file (the
|
||||
in-memory `resultArrayBytes` stays empty when a filename is given); read the bytes back. Return to MCP via
|
||||
`ImageContentBlock.FromBytes(bytes, mime)` — **not** `Data = bytes` (Data holds base64). Use
|
||||
`model_snapshot` only for visually-spatial questions; **prefer `describe_model` first** (structural
|
||||
«passport» — bbox + МЦХ + bodies + topology + feature tree + variables, no image token cost).
|
||||
- Logs go to **stderr** (stdout is the MCP channel). Integration tests reuse one КОМПАС via `KompasFixture`;
|
||||
artifacts land in gitignored `.scratch/`. All integration test classes inherit `IntegrationTestBase`
|
||||
(`IAsyncLifetime`) → `DocumentService.CloseAllAsync` after each test (prevents tab accumulation).
|
||||
- **Feature tree API**: read via API5 `ksPart.GetFeature()` → `(ksFeature).SubFeatureCollection(true,false)`
|
||||
→ `ksFeatureCollection`. Casting `(ksFeature)part` does NOT work (RCW QI returns null) — must use
|
||||
`GetFeature()`. `ksFeature.type` returns only a coarse `o3d_entity` and does NOT distinguish operations;
|
||||
the precise type and params come from `ksFeature.GetObject()` → `ksEntity.GetDefinition()` + the matching
|
||||
definition (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`,
|
||||
`ksChamferDefinition.GetChamferParam`, …). Node names («Элемент выдавливания:1») are localized by КОМПАС.
|
||||
Features registered by id in `_features` support patterns/mirror; helpers `RequireSketch`/`RequireFeature`.
|
||||
- **Variables (API5)**: create/read via `ksPart.GetFeature().VariableCollection` (property on the root
|
||||
ksFeature) — **NOT** `ksPart.VariableCollection()` (returns external-only). Expression is the leading
|
||||
field; value is derived. Apply via `ksPart.RebuildModel()` (check the return); after a rebuild the RCW
|
||||
variable is stale → re-read via a fresh collection (`ReadValueFresh`). Dependents recalculate
|
||||
automatically; deletion fails while dependents exist (`RemoveVariable` returns FALSE).
|
||||
**Geometry limitation:** variables drive geometry only in a parametric model (sketch dims linked to
|
||||
variable names); our sketches use literal coordinates → `set_variable` stores/computes the value but does
|
||||
NOT move geometry. Parametric sketches are **unavailable via COM** (`ksCDimWithVariable` is not
|
||||
constructible from external automation — see memory `kompas-parametric-sketch-findings`; do not retry
|
||||
without new information).
|
||||
- **Topology / measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` /
|
||||
`GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` /
|
||||
`GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody`
|
||||
(`IsSolid` / `FaceCollection`); measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`,
|
||||
`unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`).
|
||||
- **«STEP import without history»** is detected structurally: `bodyCount > 0 && no formative features &&
|
||||
features.Count <= bodyCount+1` (only origin + body). A STEP that has been edited («Смещённая плоскость» /
|
||||
«Разрезать» / «Переместить грани» / «Булева операция») already has history.
|
||||
- **STEP import/export COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)`
|
||||
(pass the format code, not a DLL path) → `IConverter.ConverterParameters(cmd)` →
|
||||
`IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`.
|
||||
Export: `ConvertToAdditionFormat` (format codes AP203/AP214/AP242). Param co-classes
|
||||
(`AdditionConvertParameters` etc.) are NOT CoCreatable — only via the converter factories. Assembly
|
||||
traversal (`list_components`): `IKompasDocument3D.TopPart → IPart7.Parts (IParts7)`. Extract a component:
|
||||
`prm.NeedCreateComponentsFiles=true` (writes .m3d files next to the STEP) + `IPart7.OpenSourceDocument`.
|
||||
- **Direct B-rep face editing (`move_face`)** (verified): API7 face via
|
||||
`IPart7.FindObjectsByPoint(x,y,z,true)` → cast to `KompasAPI7.IFace`; containers via runtime COM-QI
|
||||
`(ISurfaceContainer)part`, `(IModelContainer)part`; `FaceMover` (SetFaces + Offset + Direction + Update)
|
||||
moves the face. distance>0 = outward (adds material), <0 = inward. Works on parametric and imported B-rep.
|
||||
The full split-reposition workflow (SplitSolids/BodyRepositions) is driveable; only partly exposed as tools
|
||||
(`split_solid_by_plane`, `move_body`, `boolean_union`).
|
||||
- **Forming ops (API5 definitions)**:
|
||||
- `shell` (`ksShellDefinition`, o3d_shellOperation=43): `thickness` + `thinType` (bool, `= !outward`) +
|
||||
`FaceArray()` (≥1 open face by index). Transient RCWs intentionally not released (consistent with
|
||||
Fillet/Chamfer).
|
||||
- `rib` (`ksRibDefinition`, =44): `SetSketch(sketch)` → `index=0`, `angle`, `side`
|
||||
(`left|right|up|down` → 0/1/2/3 via `SketchGeometry.RibSide()`); `SetThinParam(dtBoth, t/2, t/2)` if
|
||||
symmetric, else `(dtNormal, t, 0)`. Empirical: the rib contour must float in the gap (not touch the body)
|
||||
— КОМПАС extends the web automatically.
|
||||
- `sweep` (`ksBaseEvolutionDefinition`, =45): `SetSketch(profile)` → `PathPartArray().Add(path)` →
|
||||
`sketchShiftType=0` → `SetThinParam(false)` → `Create()`. Profile and path on different (usually ⟂)
|
||||
planes; `profileSketchId != pathSketchId`.
|
||||
- `loft` (`ksBaseLoftDefinition`, =30): `Sketchs().Add(each)` → `SetLoftParam(false,false,true)` →
|
||||
`SetThinParam(false)`. ≥2 closed sections on parallel planes — use `sketch_create_on_offset_plane` for
|
||||
non-base sections.
|
||||
- offset plane (`sketch_create_on_offset_plane`, `ksPlaneOffsetDefinition`, o3d_planeOffset=14):
|
||||
`SetPlane(base)` + `offset` mm + `direction` → `Create()` → sketch on it (`OpenSketchOnOffsetPlaneAsync`).
|
||||
- `draft` (`ksInclineDefinition`, o3d_incline=42 — API5 calls the op **Incline**, NOT Draft): `FaceArray()`
|
||||
(by index) + `SetPlane(neutral base plane)` + `angle` (degrees) + `direction`. **Empirical, opposite to
|
||||
the SDK docs:** `direction=false` = expanding/outward (adds material), `true` = tapering/inward → the code
|
||||
maps `def.direction = !outward`. Validate `0<angle<90`. (`o3d_DraftFromEdges=644` / `IDraftFromEdges` is
|
||||
a different op — not used.)
|
||||
- **Patterns & mirror (API5)**: `linear_pattern` (`ksMeshCopyDefinition`, =35): `SetAxis1(axis)` +
|
||||
`SetCopyParamAlongAxis(true,0,count,step,false)` + `count2=1` (disables the 2nd direction → 1D); features
|
||||
via `OperationArray()`. `circular_pattern` (`ksCircularCopyDefinition`, =36): `SetAxis(axis)`; `count1=1`
|
||||
(radial off), `count2=count` (ring), `step2=angle°` between neighbours, `factor2=false`, `inverce=reverse`;
|
||||
`GetOperationArray().Add(feature)`. `mirror_operation` (`ksMirrorCopyDefinition`, =48): `SetPlane(base)` +
|
||||
`GetOperationArray().Add(feature)`. `mirror_body` (`ksMirrorCopyAllDefinition`, =49): `SetPlane(base)`;
|
||||
`ChooseBodies()` casts to null in the interop and is NOT needed — it mirrors all bodies keeping the original
|
||||
(volume doubles). Empirical: `count` includes the original instance (count=3 → 3 instances). Axes:
|
||||
`CoordinateAxis{X,Y,Z}` → `o3d_axisOX/OY/OZ` (`Core/Modeling/CoordinateAxis.cs`, analogous to `BasePlane.cs`).
|
||||
- **Hole ops (API7 only — `ksHoleDefinition` is absent from the API5 interop; `HoleService`)**:
|
||||
`HoleCore(ksHoleTypeEnum holeType, Action<IHole3D> configure)` — `configure` sets `HoleParameters` **after**
|
||||
`HoleType` is assigned (the type-specific sub-interface is inaccessible until the type is fixed). Placement
|
||||
is shared: `IModelContainer`/`IHole3D`/`IHoleDisposal` + `Points3D`, direction chosen by volume delta,
|
||||
orphan rollback. None are registered in `_features` → no id returned. `hole`=`ksHTBase`, configure=null.
|
||||
`hole_counterbore`=`ksHTCounterbore` + `(ISpotfacingHoleParameters)` (`SpotfacingDiameter`/`SpotfacingDepth`).
|
||||
`hole_countersink`=`ksHTCountersinking` + `(ICountersinkHoleParameters)` (`CountersinkType=ksCTDiameterAngle`,
|
||||
`CountersinkDiameter`/`Angle`). `hole_conic`=`ksHTConic` + `(IConicHoleParameters)` (`ConicType=ksCNAngle`,
|
||||
`ConicAngle`).
|
||||
- **Assembly (API7, `AssemblyService`, namespace `Kompas.Mcp.Core.Assemblies`)**: `assembly_add_component` —
|
||||
`IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)` → `IPart7.Placement` (`IPlacement3D`) →
|
||||
`SetOrigin(x,y,z)` (mm, assembly world CS) → `UpdatePlacement(true)` → `top.RebuildModel(true)`.
|
||||
**Empirical:** `UpdatePlacement` returns FALSE for a manually-positioned component (no mates) — NOT an error.
|
||||
Validate `DocumentType==ksDocumentAssembly` before touching `TopPart`; rollback via `IFeature7.Delete` +
|
||||
`RebuildModel`. `assembly_add_mate` — `top.MateConstraints` (`IMateConstraints3D`) →
|
||||
`Add(MateConstraintType)` → `BaseObject1/2` (faces via `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) →
|
||||
`ParamValue` (distance) → `Update()` → `RebuildModel(true)`; check `mate.Valid` (false → rollback
|
||||
`mate.Owner.Delete`). Types `coincidence` (mc_Coincidence=0) and `distance` (mc_Distance=5, value>0) are
|
||||
verified live; the rest are backlog.
|
||||
- **Drawing (API7, `DrawingService`, namespace `Kompas.Mcp.Core.Drawings`)** — entry via `IKompasDocument2D`;
|
||||
requires an active drawing document (`DocumentType==ksDocumentDrawing`). All coordinates are **view-local**
|
||||
(mm) unless noted. The model file must be saved before placing views.
|
||||
- `drawing_create_standard_views`: `doc2d.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди",
|
||||
projTypes, x, y, scale, dx=20, dy=20)` where `projTypes=object[]{1,3,5}` (Front/Up/Left as SAFEARRAY VT_I4).
|
||||
Success: `created==3`; content check: sum of the new views' `IView.ObjectCount` > 0 else rollback
|
||||
`IView.Delete`. Returns `ViewNumbers` (the `IView.Number` values — address views by these when placing
|
||||
annotations).
|
||||
- `drawing_fill_title_block`: `doc2d.LayoutSheets.ItemByNumber[1].Stamp` (`IStamp`) →
|
||||
`stamp.Text[columnId].Str = text` (indexed property; `IText.Str` overwrites, no Clear needed) →
|
||||
`stamp.Update()`. Fields→columns (`ksStampEnum`): Name=1, Designation=2, Material=3. Non-transactional
|
||||
(all cells → a single `Update`).
|
||||
- **Dimensions go into `(ISymbols2DContainer)view`, NOT the sheet, and are NOT counted in `IView.ObjectCount`**
|
||||
(that counts geometry in `IDrawingContainer`) — verify placement via the container's `*.Count`. Common flow:
|
||||
`Add()` → set coords → `AutoNominalValue=true` → `Update()` → check `Valid` → read
|
||||
`((IDimensionText)dim).NominalValue`; rollback `dim.Delete()` on FALSE/invalid/zero. Leader and angle are in
|
||||
**radians** (`DimensionAngles.ToRadians`). Free placement = set coordinates directly; **associative**
|
||||
(diametral/radial only) = set `BaseObject` to a projected circle → the value is read from geometry.
|
||||
- linear (`ILineDimension`): `X1,Y1,X2,Y2` (extension points) + `X3,Y3` (dimension line) + `Orientation`
|
||||
(`DimensionOrientation{Horizontal,Vertical,Parallel}` → `ksLinDParallel=0`/`Horizontal=1`/`Vertical=2`;
|
||||
parallel needs no explicit Angle).
|
||||
- diametral (`IDiametralDimension`): `Xc,Yc,Radius` + `Angle`; `NominalValue` = 2·Radius.
|
||||
- radial (`IRadialDimension`): `Xc,Yc,Radius` + `Angle`, `DimensionType=true`; **`NominalValue` = Radius,
|
||||
NOT diameter** — contrary to the SDK docs, confirmed by a spike.
|
||||
- **associative** diametral/radial (`associate=true`): instead of coordinates, find a projected circle in
|
||||
`(IDrawingContainer)view.Circles` by a center+radius key (`CircularObjectMatch.SelectMatchIndex`, tol 1 mm;
|
||||
throws on none/ambiguous; concentric disambiguated by radius), set `dim.BaseObject = circle` (`_Circle`
|
||||
implements `IDrawingObject`) → `NominalValue` read from geometry. `RequireViewContainers` gives both QIs;
|
||||
the free path stays on `RequireSymbols2DContainer`. Arc/angular/rough binding — not yet (needs its own
|
||||
spike; `ILineDimension` has no `BaseObject`).
|
||||
- angular (`IAngleDimension`, `AngleDimensions.Add(ksDrADimension=10)`): vertex `Xc,Yc` + side points
|
||||
`X1,Y1`/`X2,Y2` + arc-position `X3,Y3` (positions the arc only, does NOT pick the angle); the measured
|
||||
angle is chosen by `DimensionType` = `AngleDimensionType{Min,Max,More}` (on rays 0°/45° → min=45° /
|
||||
max=135° supplement / more=315° reflex). `NominalValue` in degrees.
|
||||
- `drawing_add_rough` (`Roughs.Add()`/`IRough`: `BranchX0/Y0`, `Angle`) + `(IRoughParams)rough` QI:
|
||||
`SignType` = `RoughSignType{NoProcessing,DeleteMaterial,WithoutDeleteMaterial}`, value (Ra/Rz) =
|
||||
`RoughParamText.Str`.
|
||||
- `drawing_add_text` — **text lives in `IDrawingContainer`, NOT `ISymbols2DContainer`, and NOT in
|
||||
`IView.ObjectCount`**: `(IDrawingContainer)view.DrawingTexts.Add()`/`IDrawingText` (`X/Y/Angle`) +
|
||||
`(IText)dt.Str` = text; count via `DrawingTexts.Count`. Do NOT set `Height` (that's block height, not font).
|
||||
- `drawing_set_technical_requirements` — **document-level**: `(IDrawingDocument)doc.TechnicalDemand` /
|
||||
`ITechnicalDemand`: `td.Text.Str = text` → `td.Update()` (`IsCreated` False→True on first set; overwrites;
|
||||
`\n` multiline).
|
||||
- `drawing_add_leader` — `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`. **Critical order
|
||||
(spike):** a fresh leader has 0 branches → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (the tip) BEFORE
|
||||
`SetBranchTextPosition(textX,textY)`/`Update`, otherwise КОМПАС dies with `RPC_E_SERVERFAULT`. Text —
|
||||
`(ILeader)bl.TextOnShelf.Str`; `ShelfDirection` (enum `ShelfDirection{Auto,Right,Left,Up,Down}`, Auto = leave
|
||||
unset). `AddBranchByPoint`/`SetBranchTextPosition` return bool — check them.
|
||||
- `drawing_set_sheet_format` — `RequireLayoutSheet(n).Format` (`ISheetFormat`): `Format`=`ksDocumentFormatEnum`
|
||||
(`PaperFormat{A0..A5,User}` + `PaperFormats.Parse/ToKompas/FromKompas`); standard → set
|
||||
`VerticalOrientation=!landscape` (W/H auto-computed from enum+orientation, e.g. A3 landscape → 420×297);
|
||||
**User → set `FormatWidth/Height` only, КОМПАС derives `VerticalOrientation` from W/H, ignores the flag and
|
||||
never swaps W/H (spike)** → `sheet.Update()`. A new drawing defaults to A4 portrait.
|
||||
`ValidateFormatDimensions`: User needs W/H>0, standard forbids W/H (must be 0).
|
||||
- `DrawingAnnotationResult{Value(read-back), ViewNumber}` is the shared result for rough/text/leader.
|
||||
- **Richer sketch primitives (API5 `ksDocument2D` wrappers)**: `ksArcBy3Points`; `ksArcByAngle` (centre/radius/
|
||||
start-end angles in degrees + counterClockwise flag); `ksEllipse` via `ksEllipseParam`
|
||||
(`KompasObject.GetParamStruct(ko_EllipseParam=22)`; **the property names are `A`/`B` UPPERCASE in the interop**);
|
||||
`ksRegularPolygon` via `ksRegularPolygonParam` (`ko_RegularPolygonParam=92`; `describe = !inscribed`); a NURBS
|
||||
spline via `ksNurbs(order=4)` + `ksNurbsPoint` loop + `ksEndObj`; `ksPoint`. Param structs are released after
|
||||
use. `PartModeler` is partial: `.cs` (registry/helpers/`NewParam<T>`/reset), `.Sketch.cs` (2D primitives),
|
||||
`.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` (`Core/Modeling`) holds the enum maps
|
||||
+ validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`, `RequireMin`). Point lists use
|
||||
`record SketchPoint(X, Y)` (JSON names `x`/`y`).
|
||||
Verified COM call-chains — interop loading, the STA dispatcher, API5 `ksPart` building, face/edge selection,
|
||||
МЦХ, snapshots, the feature tree, variables, STEP round-trip, direct B-rep edit, forming ops, patterns and
|
||||
mirror, holes, assemblies, 2D drawings, sketch primitives — live in skill **`kompas-mcp-dev`**,
|
||||
`reference/com-implementation-facts.md`. Load that skill before touching the COM layer; append to it when a
|
||||
new chain is verified live. Cross-checked duplicates also sit in memory (`kompas-*-api*.md`).
|
||||
|
||||
## КОМПАС-3D API architecture (the critical context)
|
||||
|
||||
@@ -444,18 +163,9 @@ Constants live in separate libraries: `Kompas6Constants`, `Kompas6Constants3D` (
|
||||
|
||||
## SDK layout (read-only reference, not part of this repo)
|
||||
|
||||
Under `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK`:
|
||||
- `lib\*.tlb` — COM type libraries: `kAPI5.tlb`, `kAPI7.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb`,
|
||||
`kAPI2D5COM.tlb`, `kAPI3D5COM.tlb`.
|
||||
- `lib64\` — 64-bit import libs (`kAPI7.lib`, `kAPI5.lib`, …) and `.a` for C++/Builder.
|
||||
- `Include\` — C++/Pascal headers (`Ks_TLB.h`, `kAPI2D5COM.h`, `LDefin2D.pas`, …).
|
||||
- `KsAPI\` — the **KsAPI** (Qt/C++ cross-platform flavour): `Include\KsAPI.h`, `ksConstants*.h`,
|
||||
`Lib64\ksAPI.lib`, plus a Linux/Debian 12 build guide. Not used by this project.
|
||||
- `Samples\` — zipped examples per language: `CSharp.zip`, `C++.zip`, `Pascal.zip`, `Basic.zip`. Inside
|
||||
`CSharp.zip` the `Step1`…`Step12` and `Step2_API7_2D`/`Step2_API7_3D` projects are the progressive tutorial;
|
||||
`Gayka`, `SlideWrk`, `EventsAuto` are larger feature demos. `Common/` holds the interop assemblies the
|
||||
server extracts at runtime.
|
||||
- `Help\KOMPAS_SDK_ru-RU.zip` — the offline docs the RAG base is built from.
|
||||
The SDK tree under `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK` (`lib\*.tlb`, `lib64\`, `Include\`,
|
||||
`KsAPI\`, `Samples\*.zip`, `Help\KOMPAS_SDK_ru-RU.zip`) is one `ls` away — browse it rather than trusting
|
||||
a copy here. Two things that are **not** visible from a listing:
|
||||
|
||||
To inspect a sample without unpacking the whole archive:
|
||||
`unzip -p "<path>\Samples\CSharp.zip" "Automation/Step2_API7_3D/Step2_API7_3D.cs"`. The `.cs` sample sources
|
||||
|
||||
Reference in New Issue
Block a user