lvkit 0.1.0__tar.gz → 0.2.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.
- {lvkit-0.1.0 → lvkit-0.2.0}/PKG-INFO +35 -28
- {lvkit-0.1.0 → lvkit-0.2.0}/README.md +32 -27
- {lvkit-0.1.0 → lvkit-0.2.0}/pyproject.toml +5 -2
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/cli.py +63 -40
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/subvi.py +4 -2
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/pipeline.py +12 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/primitive_resolver.py +33 -9
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_project_store.py +13 -12
- {lvkit-0.1.0 → lvkit-0.2.0}/.gitignore +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/.pre-commit-config.yaml +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/LICENSE +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/analyze_vi.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_docs.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_driver_data.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_python.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/populate_vilib.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/scripts/sync_skills.sh +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/_data.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/README.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/ast_optimizer.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/ast_utils.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/builder.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/class_builder.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/condition_builder.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/context.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/dataflow.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/error_handler.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/expressions.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/fragment.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/function.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/imports.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/base.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/case.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/compound.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/constant.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/invoke_node.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/loop.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/nmux.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/primitive.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/printf.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/property_node.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/sequence.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/stubs.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/unresolved.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/_index.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/daqmx.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidcpower.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidigital.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidmm.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nifgen.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/niscope.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/niswitch.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/serial.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/visa.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/labview_error_codes.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/_index.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/array.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/file.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/string.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/time.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/variant.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/primitives-from-pdf.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/primitives.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_index.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_pending_terminals.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_types.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/application-control.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/array.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/boolean.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/cluster.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/comparison.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/error-handling.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/file-io.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/numeric.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/other.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/string.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/structures.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/variant.json +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/generate.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/html_generator.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/template.css +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/utils.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/extractor.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/analysis.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/construction.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/core.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/describe.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/diff.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/flowchart.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/loading.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/models.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/operations.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/queries.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/labview_error.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/labview_error_codes.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/schemas.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/server.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/tools.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/models.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/constants.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/defaults.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/flags.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/front_panel.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/metadata.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/models.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/naming.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/node_types.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/base.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/case.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/constant.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/loop.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/sequence.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/type_mapping.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/type_resolution.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/utils.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/vi.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/project_store.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/py.typed +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-convert/SKILL.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-describe/SKILL.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-idiomatic/SKILL.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-resolve-primitive/SKILL.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-resolve-vilib/SKILL.md +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/structure.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/terminal_collector.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/type_defaults.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/vilib_resolver.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/__init__.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/conftest.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/helpers.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_ast_builder.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_ast_optimizer.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_case_parser.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_codegen_context.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_compound_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_condition_builder.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_constant_decoding.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_diff.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_driver_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_dynamic_dispatch.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_e2e_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_error_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_error_handling.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_graceful_degradation.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_loop_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_lvpy.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_naming.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_new_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parallel_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parser.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parser_regression.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_sequence.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_soft_codegen.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_driven_fixes.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_fields.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_loading.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_mapping.py +0 -0
- {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_vi_graph.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lvkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Understand and convert LabVIEW VIs to Python without a LabVIEW license
|
|
5
5
|
Project-URL: Repository, https://github.com/pragmatest-dev/lvkit
|
|
6
6
|
Project-URL: Issues, https://github.com/pragmatest-dev/lvkit/issues
|
|
@@ -28,6 +28,8 @@ Requires-Dist: mcp>=0.9.0
|
|
|
28
28
|
Requires-Dist: networkx
|
|
29
29
|
Requires-Dist: pydantic>=2.0.0
|
|
30
30
|
Requires-Dist: pylabview
|
|
31
|
+
Provides-Extra: visualize
|
|
32
|
+
Requires-Dist: pyvis; extra == 'visualize'
|
|
31
33
|
Description-Content-Type: text/markdown
|
|
32
34
|
|
|
33
35
|
# lvkit
|
|
@@ -40,7 +42,6 @@ lvkit parses `.vi`, `.ctl`, `.lvclass`, and `.lvlib` files directly into queryab
|
|
|
40
42
|
|
|
41
43
|
- [Quick Start](#quick-start)
|
|
42
44
|
- [What you can do with it](#what-you-can-do-with-it)
|
|
43
|
-
- [CLI Commands](#cli-commands)
|
|
44
45
|
- [How it works](#how-it-works)
|
|
45
46
|
- [AI and IDE integration](#ai-and-ide-integration)
|
|
46
47
|
- [Cleanroom approach](#cleanroom-approach)
|
|
@@ -50,11 +51,29 @@ lvkit parses `.vi`, `.ctl`, `.lvclass`, and `.lvlib` files directly into queryab
|
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
53
|
pip install lvkit
|
|
53
|
-
lvkit
|
|
54
|
+
lvkit setup
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
For a global install: `pipx install lvkit` or `uv tool install lvkit`.
|
|
58
|
+
|
|
59
|
+
`lvkit setup` creates a `.lvkit/` resolution store and installs AI agent skills:
|
|
60
|
+
|
|
61
|
+
- Auto-detects Claude Code (`CLAUDE.md` / `.claude/`) and Copilot (`.github/copilot-instructions.md` / `.github/instructions/` / `.github/agents.md`)
|
|
62
|
+
- Pass `claude`, `copilot`, or `all` to be explicit
|
|
63
|
+
- Use `--no-skills` to create the `.lvkit/` store without installing any skills
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---------|-------------|
|
|
67
|
+
| `lvkit describe` | Human-readable VI description with signature and operations |
|
|
68
|
+
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
69
|
+
| `lvkit diff` | Compare two VI versions — terminals, operations, wiring |
|
|
70
|
+
| `lvkit visualize` | Mermaid flowchart or interactive dependency graph |
|
|
71
|
+
| `lvkit generate` | Generate Python from a VI, library, or class (experimental — see [Cleanroom approach](#cleanroom-approach)) |
|
|
72
|
+
| `lvkit structure` | Inspect `.lvlib` or `.lvclass` structure |
|
|
73
|
+
| `lvkit setup` | Install AI agent skills; create `.lvkit/` resolution store |
|
|
74
|
+
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
75
|
+
|
|
76
|
+
`lvkit visualize --format interactive` requires `pip install lvkit[visualize]`. All other commands work on a bare `pip install lvkit`.
|
|
58
77
|
|
|
59
78
|
## What you can do with it
|
|
60
79
|
|
|
@@ -92,22 +111,7 @@ lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--place
|
|
|
92
111
|
|
|
93
112
|
`--placeholder-on-unresolved` lets the build succeed when mappings are missing — unresolved calls become inline `raise PrimitiveResolutionNeeded(...)` in the output so you can track them down at runtime.
|
|
94
113
|
|
|
95
|
-
Coverage is incremental — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
96
|
-
|
|
97
|
-
## CLI Commands
|
|
98
|
-
|
|
99
|
-
| Command | Description |
|
|
100
|
-
|---------|-------------|
|
|
101
|
-
| `lvkit describe` | Human-readable VI description with signature and operations |
|
|
102
|
-
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
103
|
-
| `lvkit diff` | Compare two VI versions — terminals, operations, wiring |
|
|
104
|
-
| `lvkit visualize` | Mermaid flowchart or interactive dependency graph |
|
|
105
|
-
| `lvkit generate` | Generate Python from a VI, library, or class |
|
|
106
|
-
| `lvkit structure` | Inspect `.lvlib` or `.lvclass` structure |
|
|
107
|
-
| `lvkit init` | Create `.lvkit/` resolution store; install AI editor skills |
|
|
108
|
-
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
109
|
-
|
|
110
|
-
`lvkit visualize --format interactive` requires `pip install pyvis`. All other commands work on a bare `pip install lvkit`.
|
|
114
|
+
Coverage is incremental and results will vary — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
111
115
|
|
|
112
116
|
## How it works
|
|
113
117
|
|
|
@@ -125,17 +129,18 @@ See [`docs/graph-reference.md`](docs/graph-reference.md) for the full graph type
|
|
|
125
129
|
|
|
126
130
|
The CLI works standalone from any terminal or CI script. For deeper IDE integration, lvkit ships two optional layers.
|
|
127
131
|
|
|
128
|
-
**AI
|
|
132
|
+
**AI agent skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI agent can describe VIs, convert them, and resolve unknowns without you writing prompts. All five workflows call the CLI under the hood — no MCP server required.
|
|
129
133
|
|
|
130
134
|
```bash
|
|
131
|
-
lvkit
|
|
132
|
-
lvkit
|
|
133
|
-
lvkit
|
|
135
|
+
lvkit setup # auto-detect from project layout
|
|
136
|
+
lvkit setup claude # installs .claude/skills/lvkit-*
|
|
137
|
+
lvkit setup copilot # installs .github/prompts/ + router instruction
|
|
138
|
+
lvkit setup all # both
|
|
134
139
|
```
|
|
135
140
|
|
|
136
141
|
Five workflows ship: `lvkit-describe`, `lvkit-convert`, `lvkit-resolve-primitive`, `lvkit-resolve-vilib`, `lvkit-idiomatic`.
|
|
137
142
|
|
|
138
|
-
**MCP server** — for interactive IDE sessions where your AI needs to load a graph, walk wires, and ask follow-up questions across multiple VIs:
|
|
143
|
+
**MCP server** — for interactive IDE sessions where your AI agent needs to load a graph, walk wires, and ask follow-up questions across multiple VIs:
|
|
139
144
|
|
|
140
145
|
```json
|
|
141
146
|
{
|
|
@@ -168,11 +173,13 @@ Coverage is incremental. When `lvkit generate` encounters an unmapped primitive
|
|
|
168
173
|
|
|
169
174
|
### Project-local resolution store (`.lvkit/`)
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
You can supplement the bundled mappings with a `.lvkit/` directory in your project root. lvkit reads `.lvkit/` first and falls back to its bundled data.
|
|
177
|
+
|
|
178
|
+
Run `lvkit setup --no-skills` to create the store with a README that documents the file layout and JSON formats for adding primitive and vi.lib mappings manually.
|
|
172
179
|
|
|
173
180
|
When `lvkit generate` hits an unknown, you have two options:
|
|
174
181
|
|
|
175
|
-
1. **Resolve up front** — install the resolve skills
|
|
182
|
+
1. **Resolve up front** — run `lvkit setup` to install the resolve skills and let your AI agent write the mapping into `.lvkit/`.
|
|
176
183
|
2. **Defer to runtime** — pass `--placeholder-on-unresolved`. lvkit emits an inline `raise PrimitiveResolutionNeeded(...)` in the generated Python with full diagnostic context. The build succeeds; runtime fails at the unresolved call.
|
|
177
184
|
|
|
178
185
|
## Development
|
|
@@ -8,7 +8,6 @@ lvkit parses `.vi`, `.ctl`, `.lvclass`, and `.lvlib` files directly into queryab
|
|
|
8
8
|
|
|
9
9
|
- [Quick Start](#quick-start)
|
|
10
10
|
- [What you can do with it](#what-you-can-do-with-it)
|
|
11
|
-
- [CLI Commands](#cli-commands)
|
|
12
11
|
- [How it works](#how-it-works)
|
|
13
12
|
- [AI and IDE integration](#ai-and-ide-integration)
|
|
14
13
|
- [Cleanroom approach](#cleanroom-approach)
|
|
@@ -18,11 +17,29 @@ lvkit parses `.vi`, `.ctl`, `.lvclass`, and `.lvlib` files directly into queryab
|
|
|
18
17
|
|
|
19
18
|
```bash
|
|
20
19
|
pip install lvkit
|
|
21
|
-
lvkit
|
|
20
|
+
lvkit setup
|
|
22
21
|
```
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
For a global install: `pipx install lvkit` or `uv tool install lvkit`.
|
|
24
|
+
|
|
25
|
+
`lvkit setup` creates a `.lvkit/` resolution store and installs AI agent skills:
|
|
26
|
+
|
|
27
|
+
- Auto-detects Claude Code (`CLAUDE.md` / `.claude/`) and Copilot (`.github/copilot-instructions.md` / `.github/instructions/` / `.github/agents.md`)
|
|
28
|
+
- Pass `claude`, `copilot`, or `all` to be explicit
|
|
29
|
+
- Use `--no-skills` to create the `.lvkit/` store without installing any skills
|
|
30
|
+
|
|
31
|
+
| Command | Description |
|
|
32
|
+
|---------|-------------|
|
|
33
|
+
| `lvkit describe` | Human-readable VI description with signature and operations |
|
|
34
|
+
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
35
|
+
| `lvkit diff` | Compare two VI versions — terminals, operations, wiring |
|
|
36
|
+
| `lvkit visualize` | Mermaid flowchart or interactive dependency graph |
|
|
37
|
+
| `lvkit generate` | Generate Python from a VI, library, or class (experimental — see [Cleanroom approach](#cleanroom-approach)) |
|
|
38
|
+
| `lvkit structure` | Inspect `.lvlib` or `.lvclass` structure |
|
|
39
|
+
| `lvkit setup` | Install AI agent skills; create `.lvkit/` resolution store |
|
|
40
|
+
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
41
|
+
|
|
42
|
+
`lvkit visualize --format interactive` requires `pip install lvkit[visualize]`. All other commands work on a bare `pip install lvkit`.
|
|
26
43
|
|
|
27
44
|
## What you can do with it
|
|
28
45
|
|
|
@@ -60,22 +77,7 @@ lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--place
|
|
|
60
77
|
|
|
61
78
|
`--placeholder-on-unresolved` lets the build succeed when mappings are missing — unresolved calls become inline `raise PrimitiveResolutionNeeded(...)` in the output so you can track them down at runtime.
|
|
62
79
|
|
|
63
|
-
Coverage is incremental — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
64
|
-
|
|
65
|
-
## CLI Commands
|
|
66
|
-
|
|
67
|
-
| Command | Description |
|
|
68
|
-
|---------|-------------|
|
|
69
|
-
| `lvkit describe` | Human-readable VI description with signature and operations |
|
|
70
|
-
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
71
|
-
| `lvkit diff` | Compare two VI versions — terminals, operations, wiring |
|
|
72
|
-
| `lvkit visualize` | Mermaid flowchart or interactive dependency graph |
|
|
73
|
-
| `lvkit generate` | Generate Python from a VI, library, or class |
|
|
74
|
-
| `lvkit structure` | Inspect `.lvlib` or `.lvclass` structure |
|
|
75
|
-
| `lvkit init` | Create `.lvkit/` resolution store; install AI editor skills |
|
|
76
|
-
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
77
|
-
|
|
78
|
-
`lvkit visualize --format interactive` requires `pip install pyvis`. All other commands work on a bare `pip install lvkit`.
|
|
80
|
+
Coverage is incremental and results will vary — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
79
81
|
|
|
80
82
|
## How it works
|
|
81
83
|
|
|
@@ -93,17 +95,18 @@ See [`docs/graph-reference.md`](docs/graph-reference.md) for the full graph type
|
|
|
93
95
|
|
|
94
96
|
The CLI works standalone from any terminal or CI script. For deeper IDE integration, lvkit ships two optional layers.
|
|
95
97
|
|
|
96
|
-
**AI
|
|
98
|
+
**AI agent skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI agent can describe VIs, convert them, and resolve unknowns without you writing prompts. All five workflows call the CLI under the hood — no MCP server required.
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
|
-
lvkit
|
|
100
|
-
lvkit
|
|
101
|
-
lvkit
|
|
101
|
+
lvkit setup # auto-detect from project layout
|
|
102
|
+
lvkit setup claude # installs .claude/skills/lvkit-*
|
|
103
|
+
lvkit setup copilot # installs .github/prompts/ + router instruction
|
|
104
|
+
lvkit setup all # both
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
Five workflows ship: `lvkit-describe`, `lvkit-convert`, `lvkit-resolve-primitive`, `lvkit-resolve-vilib`, `lvkit-idiomatic`.
|
|
105
108
|
|
|
106
|
-
**MCP server** — for interactive IDE sessions where your AI needs to load a graph, walk wires, and ask follow-up questions across multiple VIs:
|
|
109
|
+
**MCP server** — for interactive IDE sessions where your AI agent needs to load a graph, walk wires, and ask follow-up questions across multiple VIs:
|
|
107
110
|
|
|
108
111
|
```json
|
|
109
112
|
{
|
|
@@ -136,11 +139,13 @@ Coverage is incremental. When `lvkit generate` encounters an unmapped primitive
|
|
|
136
139
|
|
|
137
140
|
### Project-local resolution store (`.lvkit/`)
|
|
138
141
|
|
|
139
|
-
|
|
142
|
+
You can supplement the bundled mappings with a `.lvkit/` directory in your project root. lvkit reads `.lvkit/` first and falls back to its bundled data.
|
|
143
|
+
|
|
144
|
+
Run `lvkit setup --no-skills` to create the store with a README that documents the file layout and JSON formats for adding primitive and vi.lib mappings manually.
|
|
140
145
|
|
|
141
146
|
When `lvkit generate` hits an unknown, you have two options:
|
|
142
147
|
|
|
143
|
-
1. **Resolve up front** — install the resolve skills
|
|
148
|
+
1. **Resolve up front** — run `lvkit setup` to install the resolve skills and let your AI agent write the mapping into `.lvkit/`.
|
|
144
149
|
2. **Defer to runtime** — pass `--placeholder-on-unresolved`. lvkit emits an inline `raise PrimitiveResolutionNeeded(...)` in the generated Python with full diagnostic context. The build succeeds; runtime fails at the unresolved call.
|
|
145
150
|
|
|
146
151
|
## Development
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lvkit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Understand and convert LabVIEW VIs to Python without a LabVIEW license"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -45,6 +45,9 @@ dependencies = [
|
|
|
45
45
|
"pydantic>=2.0.0",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
visualize = ["pyvis"]
|
|
50
|
+
|
|
48
51
|
[project.urls]
|
|
49
52
|
Repository = "https://github.com/pragmatest-dev/lvkit"
|
|
50
53
|
Issues = "https://github.com/pragmatest-dev/lvkit/issues"
|
|
@@ -64,7 +67,7 @@ dev = [
|
|
|
64
67
|
[tool.hatch.build.targets.wheel]
|
|
65
68
|
# Bundled JSON data and skill templates live INSIDE the package at
|
|
66
69
|
# src/lvkit/data/ and src/lvkit/skill_templates/ so they ride along with
|
|
67
|
-
# the wheel automatically. The resolvers and `lvkit
|
|
70
|
+
# the wheel automatically. The resolvers and `lvkit setup` load
|
|
68
71
|
# them via paths relative to the package or via importlib.resources.
|
|
69
72
|
packages = ["src/lvkit"]
|
|
70
73
|
|
|
@@ -249,30 +249,39 @@ def main() -> int:
|
|
|
249
249
|
)
|
|
250
250
|
_add_project_root_arg(diff_parser)
|
|
251
251
|
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
"
|
|
255
|
-
help="
|
|
252
|
+
# Setup command - install AI editor skills and create .lvkit/ store
|
|
253
|
+
setup_parser = subparsers.add_parser(
|
|
254
|
+
"setup",
|
|
255
|
+
help="Install AI editor skills and create project-local .lvkit/ store",
|
|
256
256
|
)
|
|
257
|
-
|
|
257
|
+
setup_parser.add_argument(
|
|
258
258
|
"directory",
|
|
259
259
|
nargs="?",
|
|
260
260
|
default=".",
|
|
261
261
|
help="Directory in which to create .lvkit/ (default: current directory)",
|
|
262
262
|
)
|
|
263
|
-
|
|
264
|
-
"
|
|
263
|
+
setup_parser.add_argument(
|
|
264
|
+
"skills",
|
|
265
|
+
nargs="?",
|
|
265
266
|
choices=["claude", "copilot", "all"],
|
|
266
267
|
default=None,
|
|
267
268
|
help=(
|
|
268
|
-
"
|
|
269
|
-
"
|
|
270
|
-
".
|
|
271
|
-
".github/
|
|
272
|
-
".github/instructions/lvkit.instructions.md; all does both."
|
|
269
|
+
"AI agent to install skills for: claude, copilot, or all. "
|
|
270
|
+
"Omit to auto-detect from project layout (CLAUDE.md / .claude/ "
|
|
271
|
+
"for Claude Code; .github/copilot-instructions.md / "
|
|
272
|
+
".github/instructions/ / .github/agents.md for Copilot)."
|
|
273
273
|
),
|
|
274
274
|
)
|
|
275
|
-
|
|
275
|
+
setup_parser.add_argument(
|
|
276
|
+
"--no-skills",
|
|
277
|
+
action="store_true",
|
|
278
|
+
help=(
|
|
279
|
+
"Create the .lvkit/ resolution store and README without installing "
|
|
280
|
+
"any AI editor skills. Use this if you want to add primitive or "
|
|
281
|
+
"vi.lib mappings manually."
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
setup_parser.add_argument(
|
|
276
285
|
"--force",
|
|
277
286
|
action="store_true",
|
|
278
287
|
help="Overwrite existing skill files even if they have local edits",
|
|
@@ -294,8 +303,8 @@ def main() -> int:
|
|
|
294
303
|
return cmd_visualize(args)
|
|
295
304
|
elif args.command == "diff":
|
|
296
305
|
return cmd_diff(args)
|
|
297
|
-
elif args.command == "
|
|
298
|
-
return
|
|
306
|
+
elif args.command == "setup":
|
|
307
|
+
return cmd_setup(args)
|
|
299
308
|
else:
|
|
300
309
|
parser.print_help()
|
|
301
310
|
return 0
|
|
@@ -397,7 +406,7 @@ def cmd_structure(args: argparse.Namespace) -> int:
|
|
|
397
406
|
return 1
|
|
398
407
|
|
|
399
408
|
|
|
400
|
-
def cmd_mcp(
|
|
409
|
+
def cmd_mcp(_args: argparse.Namespace) -> int:
|
|
401
410
|
"""Handle the mcp command - run MCP server."""
|
|
402
411
|
from .mcp.server import main as mcp_main
|
|
403
412
|
|
|
@@ -449,21 +458,51 @@ def cmd_describe(args: argparse.Namespace) -> int:
|
|
|
449
458
|
return 1
|
|
450
459
|
|
|
451
460
|
|
|
452
|
-
def
|
|
453
|
-
"""
|
|
461
|
+
def _detect_ai_editors(root: Path) -> list[str]:
|
|
462
|
+
"""Detect which AI editors are configured in the project."""
|
|
463
|
+
editors = []
|
|
464
|
+
if (root / ".claude").is_dir() or (root / "CLAUDE.md").is_file():
|
|
465
|
+
editors.append("claude")
|
|
466
|
+
if (
|
|
467
|
+
(root / ".github" / "copilot-instructions.md").is_file()
|
|
468
|
+
or (root / ".github" / "instructions").is_dir()
|
|
469
|
+
or (root / ".github" / "agents.md").is_file()
|
|
470
|
+
):
|
|
471
|
+
editors.append("copilot")
|
|
472
|
+
return editors
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def cmd_setup(args: argparse.Namespace) -> int:
|
|
476
|
+
"""Handle the setup command — install AI skills and create .lvkit/ store."""
|
|
454
477
|
root = Path(args.directory).resolve()
|
|
455
478
|
if not root.is_dir():
|
|
456
479
|
print(f"Error: Not a directory: {root}", file=sys.stderr)
|
|
457
480
|
return 1
|
|
458
481
|
|
|
459
482
|
store = init_project_store(root)
|
|
460
|
-
print(f"Initialized
|
|
461
|
-
|
|
483
|
+
print(f"Initialized .lvkit/ store at {store}")
|
|
484
|
+
|
|
485
|
+
if args.no_skills:
|
|
486
|
+
return 0
|
|
487
|
+
|
|
488
|
+
# Resolve which editors to install for
|
|
489
|
+
explicit = args.skills
|
|
490
|
+
if explicit == "all":
|
|
491
|
+
editors = ["claude", "copilot"]
|
|
492
|
+
elif explicit in ("claude", "copilot"):
|
|
493
|
+
editors = [explicit]
|
|
494
|
+
else:
|
|
495
|
+
# Auto-detect from project layout
|
|
496
|
+
editors = _detect_ai_editors(root)
|
|
497
|
+
if not editors:
|
|
498
|
+
print(
|
|
499
|
+
"No AI agent detected. Run with --skills claude or --skills copilot "
|
|
500
|
+
"to install skills explicitly."
|
|
501
|
+
)
|
|
502
|
+
return 0
|
|
462
503
|
|
|
463
|
-
# Optional: install LLM editor skills
|
|
464
|
-
skills = getattr(args, "skills", None)
|
|
465
504
|
force = getattr(args, "force", False)
|
|
466
|
-
if
|
|
505
|
+
if "claude" in editors:
|
|
467
506
|
try:
|
|
468
507
|
written = install_claude_skills(root, force=force)
|
|
469
508
|
except FileExistsError as e:
|
|
@@ -475,7 +514,7 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
475
514
|
print(f" {p}")
|
|
476
515
|
else:
|
|
477
516
|
print("Claude Code skills already up to date.")
|
|
478
|
-
if
|
|
517
|
+
if "copilot" in editors:
|
|
479
518
|
try:
|
|
480
519
|
copilot_written = install_copilot_skills(root, force=force)
|
|
481
520
|
except FileExistsError as e:
|
|
@@ -488,22 +527,6 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
488
527
|
else:
|
|
489
528
|
print("Copilot files already up to date.")
|
|
490
529
|
|
|
491
|
-
print()
|
|
492
|
-
print("Next steps:")
|
|
493
|
-
print(
|
|
494
|
-
" - Create .lvkit/primitives.json to override primitive mappings"
|
|
495
|
-
" (use lvkit's bundled primitives.json as a reference)"
|
|
496
|
-
)
|
|
497
|
-
print(
|
|
498
|
-
" - Add vi.lib mappings to .lvkit/vilib/<category>.json and register them"
|
|
499
|
-
" in .lvkit/vilib/_index.json"
|
|
500
|
-
)
|
|
501
|
-
print(" - lvkit will check .lvkit/ before its bundled data when resolving.")
|
|
502
|
-
if not skills:
|
|
503
|
-
print(
|
|
504
|
-
" - Run `lvkit init --skills all` to install resolve workflows"
|
|
505
|
-
" into Claude Code and/or Copilot."
|
|
506
|
-
)
|
|
507
530
|
return 0
|
|
508
531
|
|
|
509
532
|
|
|
@@ -34,7 +34,7 @@ def generate(node: SubVIOperation, ctx: CodeGenContext) -> CodeFragment:
|
|
|
34
34
|
# Look up VI — polymorphic variant if selected, otherwise base name
|
|
35
35
|
vilib_vi = None
|
|
36
36
|
if node.poly_variant_name:
|
|
37
|
-
vilib_vi = _resolve_poly_variant(subvi_name, node
|
|
37
|
+
vilib_vi = _resolve_poly_variant(subvi_name, node)
|
|
38
38
|
if not vilib_vi:
|
|
39
39
|
# Variant not in JSON — fail or emit inline raise
|
|
40
40
|
return _emit_vilib_resolution(node, ctx, vilib_vi=None)
|
|
@@ -236,7 +236,7 @@ def _generate_inline(
|
|
|
236
236
|
)
|
|
237
237
|
|
|
238
238
|
def _resolve_poly_variant(
|
|
239
|
-
base_name: str, node: Operation
|
|
239
|
+
base_name: str, node: Operation
|
|
240
240
|
) -> VIEntry | None:
|
|
241
241
|
"""Resolve a polymorphic VI to its specific variant.
|
|
242
242
|
|
|
@@ -367,6 +367,7 @@ def _build_arguments(
|
|
|
367
367
|
),
|
|
368
368
|
available=[],
|
|
369
369
|
vi_name=ctx.vi_name if ctx else None,
|
|
370
|
+
kind="vilib" if vilib_vi is not None else "subvi",
|
|
370
371
|
)
|
|
371
372
|
|
|
372
373
|
# Check if this parameter is an enum typedef - generate enum reference
|
|
@@ -521,6 +522,7 @@ def _build_output_bindings(
|
|
|
521
522
|
),
|
|
522
523
|
available=[],
|
|
523
524
|
vi_name=ctx.vi_name if ctx else None,
|
|
525
|
+
kind="vilib" if vilib_vi is not None else "subvi",
|
|
524
526
|
)
|
|
525
527
|
|
|
526
528
|
bindings[term_id] = f"{result_var}.{field}"
|
|
@@ -20,6 +20,7 @@ from typing import Any
|
|
|
20
20
|
from lvkit.codegen import ClassBuilder, ClassConfig, build_module
|
|
21
21
|
from lvkit.codegen.ast_utils import to_function_name, to_module_name
|
|
22
22
|
from lvkit.graph import InMemoryVIGraph
|
|
23
|
+
from lvkit.project_store import find_project_store
|
|
23
24
|
from lvkit.structure import parse_lvclass
|
|
24
25
|
from lvkit.terminal_collector import get_collector
|
|
25
26
|
from lvkit.vilib_resolver import VILibResolver
|
|
@@ -661,6 +662,17 @@ def {func_name}(*args, **kwargs) -> Any:
|
|
|
661
662
|
print(f" stub: {stub_count}")
|
|
662
663
|
print(f" error: {error_count}")
|
|
663
664
|
|
|
665
|
+
if error_count > 0:
|
|
666
|
+
store = find_project_store()
|
|
667
|
+
if store:
|
|
668
|
+
print(f"\n To resolve errors, add mappings to {store}/")
|
|
669
|
+
print(f" See {store / 'README.md'} for file formats and instructions.")
|
|
670
|
+
print(" AI agent skills can add these automatically — run `lvkit setup` if not installed.")
|
|
671
|
+
else:
|
|
672
|
+
print("\n To resolve errors:")
|
|
673
|
+
print(" - Run `lvkit setup` to install AI agent skills that resolve unknowns automatically.")
|
|
674
|
+
print(" - Or run `lvkit setup --no-skills` to create a .lvkit/ store for manual mappings.")
|
|
675
|
+
|
|
664
676
|
# Save terminal observations for incremental collection
|
|
665
677
|
collector = get_collector()
|
|
666
678
|
if collector.data.get("observations"):
|
|
@@ -75,7 +75,9 @@ class PrimitiveResolutionNeeded(Exception):
|
|
|
75
75
|
class TerminalResolutionNeeded(Exception):
|
|
76
76
|
"""Raised when a specific wired terminal cannot be resolved to a known index.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
kind="primitive": a built-in LabVIEW primitive with a missing terminal index
|
|
79
|
+
kind="vilib": a vilib VI with a missing terminal index in data/vilib/
|
|
80
|
+
kind="subvi": a user project VI whose terminal name could not be resolved
|
|
79
81
|
"""
|
|
80
82
|
|
|
81
83
|
def __init__(
|
|
@@ -86,6 +88,7 @@ class TerminalResolutionNeeded(Exception):
|
|
|
86
88
|
terminal_type: str | None,
|
|
87
89
|
available: list[dict[str, str | int | None]],
|
|
88
90
|
vi_name: str | None = None,
|
|
91
|
+
kind: str = "primitive",
|
|
89
92
|
):
|
|
90
93
|
self.prim_id = str(prim_id)
|
|
91
94
|
self.prim_name = prim_name
|
|
@@ -93,13 +96,20 @@ class TerminalResolutionNeeded(Exception):
|
|
|
93
96
|
self.terminal_type = terminal_type
|
|
94
97
|
self.available = available
|
|
95
98
|
self.vi_name = vi_name
|
|
99
|
+
self.kind = kind
|
|
96
100
|
super().__init__(self._format_message())
|
|
97
101
|
|
|
98
102
|
def _format_message(self) -> str:
|
|
99
|
-
|
|
100
|
-
f"Terminal resolution needed for
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
if self.kind == "vilib":
|
|
104
|
+
header = f"Terminal resolution needed for vilib VI '{self.prim_name}'."
|
|
105
|
+
elif self.kind == "subvi":
|
|
106
|
+
header = f"Terminal resolution needed for project VI '{self.prim_name}'."
|
|
107
|
+
else:
|
|
108
|
+
header = (
|
|
109
|
+
f"Terminal resolution needed for primitive"
|
|
110
|
+
f" {self.prim_id} ({self.prim_name})."
|
|
111
|
+
)
|
|
112
|
+
msg = header + "\n"
|
|
103
113
|
if self.vi_name:
|
|
104
114
|
msg += f" In VI: {self.vi_name}\n"
|
|
105
115
|
msg += (
|
|
@@ -114,10 +124,24 @@ class TerminalResolutionNeeded(Exception):
|
|
|
114
124
|
)
|
|
115
125
|
if not self.available:
|
|
116
126
|
msg += " (none available)\n"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
if self.kind == "vilib":
|
|
128
|
+
msg += (
|
|
129
|
+
f"\n Fix: add/update terminal index in"
|
|
130
|
+
f" .lvkit/data/vilib/<category>.json (project-local)"
|
|
131
|
+
f" or data/vilib/<category>.json (upstream)"
|
|
132
|
+
f" under VI '{self.prim_name}'"
|
|
133
|
+
)
|
|
134
|
+
elif self.kind == "subvi":
|
|
135
|
+
msg += (
|
|
136
|
+
f"\n Fix: ensure '{self.prim_name}' is reachable via --search-path"
|
|
137
|
+
f" and its terminal names are present in the VI's front panel"
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
msg += (
|
|
141
|
+
f"\n Fix: add primitive {self.prim_id} to"
|
|
142
|
+
f" .lvkit/data/primitives.json (project-local)"
|
|
143
|
+
f" or data/primitives.json (upstream)"
|
|
144
|
+
)
|
|
121
145
|
return msg
|
|
122
146
|
|
|
123
147
|
|
|
@@ -217,20 +217,21 @@ def test_no_project_store_default_behavior() -> None:
|
|
|
217
217
|
# ============================================================
|
|
218
218
|
|
|
219
219
|
|
|
220
|
-
def
|
|
221
|
-
"""`lvkit
|
|
220
|
+
def test_cli_setup_creates_project_store(tmp_path: Path) -> None:
|
|
221
|
+
"""`lvkit setup <dir> --no-skills` creates .lvkit/ store without installing skills."""
|
|
222
222
|
import subprocess
|
|
223
223
|
import sys
|
|
224
224
|
|
|
225
225
|
result = subprocess.run(
|
|
226
|
-
[sys.executable, "-m", "lvkit.cli", "
|
|
226
|
+
[sys.executable, "-m", "lvkit.cli", "setup", str(tmp_path), "--no-skills"],
|
|
227
227
|
capture_output=True,
|
|
228
228
|
text=True,
|
|
229
229
|
check=True,
|
|
230
230
|
)
|
|
231
|
-
assert "Initialized
|
|
231
|
+
assert "Initialized .lvkit/" in result.stdout
|
|
232
232
|
assert (tmp_path / ".lvkit" / "README.md").exists()
|
|
233
233
|
assert (tmp_path / ".lvkit" / "vilib" / "_index.json").exists()
|
|
234
|
+
assert not (tmp_path / ".claude").exists()
|
|
234
235
|
|
|
235
236
|
|
|
236
237
|
# ============================================================
|
|
@@ -493,15 +494,15 @@ def test_install_copilot_skills_force_overwrites(tmp_path: Path) -> None:
|
|
|
493
494
|
assert "mode: agent" in edited.read_text()
|
|
494
495
|
|
|
495
496
|
|
|
496
|
-
def
|
|
497
|
-
"""`lvkit
|
|
497
|
+
def test_cli_setup_skills_claude(tmp_path: Path) -> None:
|
|
498
|
+
"""`lvkit setup claude` installs Claude Code skills."""
|
|
498
499
|
import subprocess
|
|
499
500
|
import sys
|
|
500
501
|
|
|
501
502
|
result = subprocess.run(
|
|
502
503
|
[
|
|
503
|
-
sys.executable, "-m", "lvkit.cli", "
|
|
504
|
-
str(tmp_path), "
|
|
504
|
+
sys.executable, "-m", "lvkit.cli", "setup",
|
|
505
|
+
str(tmp_path), "claude",
|
|
505
506
|
],
|
|
506
507
|
capture_output=True,
|
|
507
508
|
text=True,
|
|
@@ -513,15 +514,15 @@ def test_cli_init_skills_claude(tmp_path: Path) -> None:
|
|
|
513
514
|
).is_file()
|
|
514
515
|
|
|
515
516
|
|
|
516
|
-
def
|
|
517
|
-
"""`lvkit
|
|
517
|
+
def test_cli_setup_skills_all(tmp_path: Path) -> None:
|
|
518
|
+
"""`lvkit setup all` installs both Claude and Copilot."""
|
|
518
519
|
import subprocess
|
|
519
520
|
import sys
|
|
520
521
|
|
|
521
522
|
result = subprocess.run(
|
|
522
523
|
[
|
|
523
|
-
sys.executable, "-m", "lvkit.cli", "
|
|
524
|
-
str(tmp_path), "
|
|
524
|
+
sys.executable, "-m", "lvkit.cli", "setup",
|
|
525
|
+
str(tmp_path), "all",
|
|
525
526
|
],
|
|
526
527
|
capture_output=True,
|
|
527
528
|
text=True,
|
|
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
|