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.
Files changed (167) hide show
  1. {lvkit-0.1.0 → lvkit-0.2.0}/PKG-INFO +35 -28
  2. {lvkit-0.1.0 → lvkit-0.2.0}/README.md +32 -27
  3. {lvkit-0.1.0 → lvkit-0.2.0}/pyproject.toml +5 -2
  4. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/cli.py +63 -40
  5. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/subvi.py +4 -2
  6. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/pipeline.py +12 -0
  7. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/primitive_resolver.py +33 -9
  8. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_project_store.py +13 -12
  9. {lvkit-0.1.0 → lvkit-0.2.0}/.gitignore +0 -0
  10. {lvkit-0.1.0 → lvkit-0.2.0}/.pre-commit-config.yaml +0 -0
  11. {lvkit-0.1.0 → lvkit-0.2.0}/LICENSE +0 -0
  12. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/analyze_vi.py +0 -0
  13. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_docs.py +0 -0
  14. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_driver_data.py +0 -0
  15. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/generate_python.py +0 -0
  16. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/populate_vilib.py +0 -0
  17. {lvkit-0.1.0 → lvkit-0.2.0}/scripts/sync_skills.sh +0 -0
  18. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/__init__.py +0 -0
  19. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/_data.py +0 -0
  20. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/README.md +0 -0
  21. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/__init__.py +0 -0
  22. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/ast_optimizer.py +0 -0
  23. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/ast_utils.py +0 -0
  24. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/builder.py +0 -0
  25. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/class_builder.py +0 -0
  26. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/condition_builder.py +0 -0
  27. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/context.py +0 -0
  28. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/dataflow.py +0 -0
  29. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/error_handler.py +0 -0
  30. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/expressions.py +0 -0
  31. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/fragment.py +0 -0
  32. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/function.py +0 -0
  33. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/imports.py +0 -0
  34. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/__init__.py +0 -0
  35. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/base.py +0 -0
  36. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/case.py +0 -0
  37. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/compound.py +0 -0
  38. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/constant.py +0 -0
  39. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/invoke_node.py +0 -0
  40. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/loop.py +0 -0
  41. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/nmux.py +0 -0
  42. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/primitive.py +0 -0
  43. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/printf.py +0 -0
  44. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/property_node.py +0 -0
  45. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/nodes/sequence.py +0 -0
  46. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/stubs.py +0 -0
  47. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/codegen/unresolved.py +0 -0
  48. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/_index.json +0 -0
  49. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/daqmx.json +0 -0
  50. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidcpower.json +0 -0
  51. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidigital.json +0 -0
  52. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nidmm.json +0 -0
  53. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/nifgen.json +0 -0
  54. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/niscope.json +0 -0
  55. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/niswitch.json +0 -0
  56. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/serial.json +0 -0
  57. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/drivers/visa.json +0 -0
  58. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/labview_error_codes.json +0 -0
  59. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/_index.json +0 -0
  60. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/array.json +0 -0
  61. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/file.json +0 -0
  62. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/string.json +0 -0
  63. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/time.json +0 -0
  64. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/openg/variant.json +0 -0
  65. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/primitives-from-pdf.json +0 -0
  66. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/primitives.json +0 -0
  67. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_index.json +0 -0
  68. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_pending_terminals.json +0 -0
  69. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/_types.json +0 -0
  70. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/application-control.json +0 -0
  71. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/array.json +0 -0
  72. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/boolean.json +0 -0
  73. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/cluster.json +0 -0
  74. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/comparison.json +0 -0
  75. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/error-handling.json +0 -0
  76. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/file-io.json +0 -0
  77. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/numeric.json +0 -0
  78. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/other.json +0 -0
  79. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/string.json +0 -0
  80. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/structures.json +0 -0
  81. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/data/vilib/variant.json +0 -0
  82. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/__init__.py +0 -0
  83. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/generate.py +0 -0
  84. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/html_generator.py +0 -0
  85. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/template.css +0 -0
  86. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/docs/utils.py +0 -0
  87. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/extractor.py +0 -0
  88. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/__init__.py +0 -0
  89. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/analysis.py +0 -0
  90. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/construction.py +0 -0
  91. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/core.py +0 -0
  92. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/describe.py +0 -0
  93. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/diff.py +0 -0
  94. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/flowchart.py +0 -0
  95. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/loading.py +0 -0
  96. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/models.py +0 -0
  97. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/operations.py +0 -0
  98. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/graph/queries.py +0 -0
  99. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/labview_error.py +0 -0
  100. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/labview_error_codes.py +0 -0
  101. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/__init__.py +0 -0
  102. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/schemas.py +0 -0
  103. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/server.py +0 -0
  104. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/mcp/tools.py +0 -0
  105. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/models.py +0 -0
  106. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/__init__.py +0 -0
  107. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/constants.py +0 -0
  108. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/defaults.py +0 -0
  109. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/flags.py +0 -0
  110. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/front_panel.py +0 -0
  111. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/metadata.py +0 -0
  112. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/models.py +0 -0
  113. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/naming.py +0 -0
  114. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/node_types.py +0 -0
  115. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/__init__.py +0 -0
  116. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/base.py +0 -0
  117. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/case.py +0 -0
  118. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/constant.py +0 -0
  119. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/loop.py +0 -0
  120. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/nodes/sequence.py +0 -0
  121. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/type_mapping.py +0 -0
  122. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/type_resolution.py +0 -0
  123. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/utils.py +0 -0
  124. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/parser/vi.py +0 -0
  125. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/project_store.py +0 -0
  126. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/py.typed +0 -0
  127. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/__init__.py +0 -0
  128. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-convert/SKILL.md +0 -0
  129. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-describe/SKILL.md +0 -0
  130. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-idiomatic/SKILL.md +0 -0
  131. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-resolve-primitive/SKILL.md +0 -0
  132. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/skill_templates/lvkit-resolve-vilib/SKILL.md +0 -0
  133. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/structure.py +0 -0
  134. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/terminal_collector.py +0 -0
  135. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/type_defaults.py +0 -0
  136. {lvkit-0.1.0 → lvkit-0.2.0}/src/lvkit/vilib_resolver.py +0 -0
  137. {lvkit-0.1.0 → lvkit-0.2.0}/tests/__init__.py +0 -0
  138. {lvkit-0.1.0 → lvkit-0.2.0}/tests/conftest.py +0 -0
  139. {lvkit-0.1.0 → lvkit-0.2.0}/tests/helpers.py +0 -0
  140. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_ast_builder.py +0 -0
  141. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_ast_optimizer.py +0 -0
  142. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_case_parser.py +0 -0
  143. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_codegen_context.py +0 -0
  144. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_compound_codegen.py +0 -0
  145. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_condition_builder.py +0 -0
  146. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_constant_decoding.py +0 -0
  147. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_diff.py +0 -0
  148. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_driver_codegen.py +0 -0
  149. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_dynamic_dispatch.py +0 -0
  150. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_e2e_codegen.py +0 -0
  151. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_error_codegen.py +0 -0
  152. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_error_handling.py +0 -0
  153. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_graceful_degradation.py +0 -0
  154. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_loop_codegen.py +0 -0
  155. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_lvpy.py +0 -0
  156. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_naming.py +0 -0
  157. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_new_codegen.py +0 -0
  158. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parallel_codegen.py +0 -0
  159. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parser.py +0 -0
  160. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_parser_regression.py +0 -0
  161. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_sequence.py +0 -0
  162. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_soft_codegen.py +0 -0
  163. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_driven_fixes.py +0 -0
  164. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_fields.py +0 -0
  165. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_loading.py +0 -0
  166. {lvkit-0.1.0 → lvkit-0.2.0}/tests/test_type_mapping.py +0 -0
  167. {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.1.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 init --skills all # create .lvkit/ + install Claude Code and Copilot skills
54
+ lvkit setup
54
55
  ```
55
56
 
56
- * Use `--skills claude` or `--skills copilot` to install for one AI agent only.
57
- * Use `lvkit init` alone if you don't want any AI agent skills installed.
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 editor skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI 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.
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 init --skills claude # installs .claude/skills/lvkit-*
132
- lvkit init --skills copilot # installs .github/prompts/ + router instruction
133
- lvkit init --skills all # both
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
- If you have a LabVIEW license, you can supplement the bundled mappings with a `.lvkit/` directory derived from your own install. lvkit reads `.lvkit/` first and falls back to its bundled data. The skills installed by `lvkit init` detect whether you're a lvkit maintainer or a downstream user and write mappings to the right destination.
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 (`lvkit init --skills claude`) and let your AI editor write the mapping into `.lvkit/`.
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 init --skills all # create .lvkit/ + install Claude Code and Copilot skills
20
+ lvkit setup
22
21
  ```
23
22
 
24
- * Use `--skills claude` or `--skills copilot` to install for one AI agent only.
25
- * Use `lvkit init` alone if you don't want any AI agent skills installed.
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 editor skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI 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.
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 init --skills claude # installs .claude/skills/lvkit-*
100
- lvkit init --skills copilot # installs .github/prompts/ + router instruction
101
- lvkit init --skills all # both
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
- If you have a LabVIEW license, you can supplement the bundled mappings with a `.lvkit/` directory derived from your own install. lvkit reads `.lvkit/` first and falls back to its bundled data. The skills installed by `lvkit init` detect whether you're a lvkit maintainer or a downstream user and write mappings to the right destination.
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 (`lvkit init --skills claude`) and let your AI editor write the mapping into `.lvkit/`.
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.1.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 init --skills` load
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
- # Init command - create .lvkit/ project store
253
- init_parser = subparsers.add_parser(
254
- "init",
255
- help="Initialize a project-local .lvkit/ resolution store",
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
- init_parser.add_argument(
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
- init_parser.add_argument(
264
- "--skills",
263
+ setup_parser.add_argument(
264
+ "skills",
265
+ nargs="?",
265
266
  choices=["claude", "copilot", "all"],
266
267
  default=None,
267
268
  help=(
268
- "Also install lvkit's resolve workflows into your LLM editor: "
269
- "claude installs lvkit-prefixed Claude Code skills under "
270
- ".claude/skills/; copilot installs per-workflow prompts under "
271
- ".github/prompts/ plus a router at "
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
- init_parser.add_argument(
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 == "init":
298
- return cmd_init(args)
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(args: argparse.Namespace) -> int:
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 cmd_init(args: argparse.Namespace) -> int:
453
- """Handle the init command create a project-local .lvkit/ store."""
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 project store at {store}")
461
- print(f" README: {store / 'README.md'}")
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 skills in ("claude", "all"):
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 skills in ("copilot", "all"):
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, ctx)
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, ctx: CodeGenContext
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
- The primitive definition exists, but a terminal's index doesn't match.
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
- msg = (
100
- f"Terminal resolution needed for primitive"
101
- f" {self.prim_id} ({self.prim_name}).\n"
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
- msg += (
118
- f"\n Fix: add/update terminal in data/primitives.json"
119
- f" under primitive {self.prim_id}"
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 test_cli_init_creates_project_store(tmp_path: Path) -> None:
221
- """`lvkit init <dir>` creates .lvkit/ with template content."""
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", "init", str(tmp_path)],
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 project store" in result.stdout
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 test_cli_init_skills_claude(tmp_path: Path) -> None:
497
- """`lvkit init --skills claude` installs Claude Code skills."""
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", "init",
504
- str(tmp_path), "--skills", "claude",
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 test_cli_init_skills_all(tmp_path: Path) -> None:
517
- """`lvkit init --skills all` installs both Claude and Copilot."""
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", "init",
524
- str(tmp_path), "--skills", "all",
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