create-multi-steps-plugin
GitHub用于创建符合规范的 TIS 多步骤插件,处理具有逻辑依赖的属性(如省市区联动),自动生成宿主类、步骤类及描述文件。
Trigger Scenarios
Install
npx skills add datavane/tis --skill create-multi-steps-plugin -g -y
SKILL.md
Frontmatter
{
"name": "create-multi-steps-plugin",
"description": "Create a TIS multi-steps plugin with dependent properties configured through multiple steps"
}
TIS Multi-Steps Plugin Creator
This skill helps you create a well-structured TIS multi-steps plugin that conforms to TIS multi-steps plugin specifications. Multi-steps plugins are used when plugin properties have logical dependencies (e.g., selecting a province before showing cities in that province).
What This Skill Does
When invoked, this skill will:
- Analyze the user-provided context to understand the plugin's multi-step functionality
- Design the multi-steps plugin structure with host plugin and step plugins
- Generate the complete implementation including:
- Host Plugin Class: Implements
MultiStepsSupportHostandIPluginStore.ManipuldateProcessor - Host Descriptor: Implements
MultiStepsSupportHostDescriptorwith step definitions - Step Plugin Classes: Each step extends
OneStepOfMultiSteps - Step Descriptors: Extend
OneStepOfMultiSteps.BasicDescwith navigation logic - Property descriptor files (
.json) for all plugins - Optional help documentation (
.md) for complex properties
- Host Plugin Class: Implements
When to Use This Skill
Use this skill when:
- User explicitly requests to invoke this skill
- During assisted programming, you discover properties with semantic dependencies (like province → city), and the user confirms the need for a multi-steps plugin
Reference Example
The primary reference example is:
- Host Plugin:
/Users/mozhenghua/j2ee_solution/project/plugins/tis-ontology-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/impl/valuetype/DefaultOntologyValueType.java - Parent Class:
tis-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/OntologyValueType.java - Step 1:
/Users/mozhenghua/j2ee_solution/project/plugins/tis-ontology-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/impl/valuetype/MetadataOfValueType.java - Step 2:
/Users/mozhenghua/j2ee_solution/project/plugins/tis-ontology-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/impl/valuetype/ConstraintsOfValueType.java
TIS Multi-Steps Plugin Specifications
Core Requirements for Host Plugin
1. Implement MultiStepsSupportHost Interface
The plugin class (or its parent) MUST implement com.qlangtech.tis.extension.MultiStepsSupportHost interface.
Reference: tis-plugin/src/main/java/com/qlangtech/tis/extension/MultiStepsSupportHost.java
Example (parent class):
public abstract class OntologyValueType extends Ontology
implements IdentityName, MultiStepsSupportHost, IPluginStore.ManipuldateProcessor {
// ...
}
2. Define stepsPlugin Property
The plugin class (or its parent) MUST have a protected OneStepOfMultiSteps[] stepsPlugin property and implement two methods from MultiStepsSupportHost:
protected OneStepOfMultiSteps[] stepsPlugin;
@Override
public void setSteps(OneStepOfMultiSteps[] stepsPlugin) {
this.stepsPlugin = Objects.requireNonNull(stepsPlugin, "stepsPlugin can not be null");
final int FIXED_VALUE_TYPE_STEPS_LENGTH = 2; // Number of steps
if (stepsPlugin.length != FIXED_VALUE_TYPE_STEPS_LENGTH) {
throw new IllegalStateException("stepsPlugin.length must be equal to " + FIXED_VALUE_TYPE_STEPS_LENGTH);
}
}
// IMPORTANT: Must add @JSONField(serialize = false) annotation
@JSONField(serialize = false)
@Override
public OneStepOfMultiSteps[] getMultiStepsSavedItems() {
return stepsPlugin;
}
Important Notes:
setSteps()should validate the array length matches the expected number of stepsgetMultiStepsSavedItems()MUST be annotated with@JSONField(serialize = false)to prevent duplicate serialization
3. Implement IPluginStore.ManipuldateProcessor
The plugin class (or its parent) MUST implement com.qlangtech.tis.plugin.IPluginStore.ManipuldateProcessor interface to handle persistence logic.
Reference: See tis-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/OntologyValueType.java:L152
@Override
public void manipuldateProcess(IPluginContext currentCtx) {
// Custom persistence logic
// Example: Save to custom storage, validate cross-step data, etc.
}
4. Host Descriptor Requirements
The inner @TISExtension Descriptor class MUST implement com.qlangtech.tis.extension.MultiStepsSupportHostDescriptor interface.
Reference: tis-plugin/src/main/java/com/qlangtech/tis/extension/MultiStepsSupportHostDescriptor.java
Must implement two methods:
@TISExtension
public static class DefaultDesc extends Ontology.BasicDesc
implements MultiStepsSupportHostDescriptor<OntologyValueType> {
@Override
public Class<OntologyValueType> getHostClass() {
return OntologyValueType.class;
}
@Override
public List<OneStepOfMultiSteps.BasicDesc> getStepDescriptionList() {
// Return step descriptors IN ORDER
return List.of(new MetadataOfValueType.Desc(), new ConstraintsOfValueType.Desc());
}
}
Important Notes:
getStepDescriptionList()returns step descriptor instances in execution order- The order in the list defines the step sequence (first element = Step 1, second = Step 2, etc.)
Core Requirements for Step Plugins
1. Extend OneStepOfMultiSteps
Each step plugin MUST extend com.qlangtech.tis.extension.OneStepOfMultiSteps.
Reference: tis-plugin/src/main/java/com/qlangtech/tis/extension/OneStepOfMultiSteps.java
public class MetadataOfValueType extends OneStepOfMultiSteps
implements OntologyValueType.IMetadataOfValueType {
@FormField(ordinal = 0, validate = {Validator.require})
public String name;
// Other properties...
}
2. Optional: Override processPreSaved()
Step plugins can override processPreSaved() to execute business logic when the user submits the step:
@Override
public void processPreSaved(IPluginContext pluginContext) {
// Custom validation or processing logic
super.processPreSaved(pluginContext);
}
3. Step Descriptor Requirements
The inner @TISExtension Descriptor MUST extend OneStepOfMultiSteps.BasicDesc and implement key methods:
@TISExtension
public static class Desc extends OneStepOfMultiSteps.BasicDesc {
@Override
public String getStepDescription() {
return "Metadata"; // Step display name
}
@Override
public Step getStep() {
return Step.Step1; // Which step this is (Step1, Step2, etc.)
}
@Override
public Optional<BasicDesc> nextPluginDesc(OneStepOfMultiSteps current) {
// Return the next step's descriptor
return Optional.of(new ConstraintsOfValueType.Desc());
// For final step: return Optional.empty();
}
@Override
public boolean isFinalStep() {
return false; // true for the last step
}
}
Important Notes:
getStep()must return the correctStepenum value (Step1, Step2, ..., Step7)nextPluginDesc()returns the next step's descriptor instance, orOptional.empty()for the final stepisFinalStep()returnstrueonly for the last step in the sequence
4. Step Plugin Properties
Step plugins follow the same property rules as regular TIS plugins:
- All properties must be
public - Annotated with
@FormField - Have corresponding
.jsondescriptor files - Can use all standard validators and field types
- See
/create-pluginskill for detailed property specifications
Additional Specifications
5. Override getDescriptor() with @JSONField
If the host plugin class overrides the parent's getDescriptor() method, it MUST add @JSONField(serialize = false) annotation:
Reference: See tis-plugin/src/main/java/com/qlangtech/tis/plugin/ontology/OntologyObjectType.java
@JSONField(serialize = false)
@Override
public Descriptor<Ontology> getDescriptor() {
return super.getDescriptor();
}
6. Identity Support (Optional)
If the multi-steps plugin needs to be "identifiable" (parent class doesn't already implement IdentityName):
Ask the user whether the plugin should be identifiable. If yes:
- Implement
com.qlangtech.tis.plugin.IdentityNameinterface - Implement
identityValue()method - Add a dummy identity field (TIS requirement):
public abstract class OntologyValueType extends Ontology
implements IdentityName, MultiStepsSupportHost {
/**
* Caution: This field is currently unused but required since we implement IdentityName
*/
@FormField(identity = true, ordinal = 0, validate = {Validator.require, Validator.identity})
public String useless;
@Override
public String identityValue() {
// Usually return identity from first step
for (OneStepOfMultiSteps step : getMultiStepsSavedItems()) {
if (step instanceof IMetadataOfValueType meta) {
return meta.getName();
}
}
throw new IllegalStateException("illegal name have not been set");
}
}
Instructions for Claude
When the user invokes /create-multi-steps-plugin, follow this workflow:
Step 1: Analyze Context
- Ask the user to provide context about the multi-steps plugin they want to create (or the user may provide it directly)
- Analyze the context to determine:
- Plugin's purpose and multi-step workflow
- Number of steps required (2-7 steps)
- Properties for each step
- Dependency relationships between steps (how earlier steps influence later steps)
- Parent class to extend (if any)
- Whether the plugin needs to be identifiable
Step 2: Design Multi-Steps Plugin Structure
Determine the plugin organization:
Host Plugin:
- Class name and package
- Parent class (if extending an existing plugin class)
- Whether it implements
IdentityName(ask user if parent doesn't) - Number of steps
- Any host-level methods or logic
For Each Step (Step 1, Step 2, ..., Step N):
- Step class name and package
- Step description (display name)
- Properties with types, FormFieldType, and validators
- Business logic in
processPreSaved()if needed - How this step's properties depend on previous steps
- Whether this is the final step
Property Descriptor Files:
- Which properties need
.jsondescriptors - Which properties need
.mdhelp files
Step 3: Confirm Design with User
Before implementation, present:
-
Host Plugin:
- Class name, parent class, interfaces
- Number of steps
- Identity support (yes/no)
- Persistence logic requirements
-
Step Details (for each step):
- Step number and class name
- Step description
- Properties list with types and validators
- Dependency on previous steps
- Business logic requirements
-
Files to Generate:
- Host plugin: Java + JSON + optional MD
- Step 1 plugin: Java + JSON + optional MD
- Step 2 plugin: Java + JSON + optional MD
- ... (for each step)
Wait for user approval before proceeding.
Step 4: Implement Multi-Steps Plugin
Generate files in the correct structure:
A. Host Plugin Java Class
File structure:
{package_path}/{HostPluginName}.java
Must include:
- Package and imports
- Class declaration implementing required interfaces:
- Extends parent class (if any)
- Implements
MultiStepsSupportHost(or parent does) - Implements
IPluginStore.ManipuldateProcessor(or parent does) - Optionally implements
IdentityName
- Properties:
protected OneStepOfMultiSteps[] stepsPlugin;(if not in parent)- Identity field if implementing
IdentityName
- Methods:
setSteps()with validationgetMultiStepsSavedItems()with@JSONField(serialize = false)manipuldateProcess()for persistence logicidentityValue()if implementingIdentityName- Getter methods for accessing specific steps (optional but recommended)
- Inner Descriptor class:
- Annotated with
@TISExtension - Implements
MultiStepsSupportHostDescriptor<HostPluginClass> - Implements
getHostClass()returning the host class - Implements
getStepDescriptionList()returning step descriptors in order - Overrides
getDisplayName()
- Annotated with
Example structure:
package com.qlangtech.tis.plugin.example;
import com.qlangtech.tis.extension.*;
import com.qlangtech.tis.plugin.IPluginStore;
// ... other imports
public class ExampleMultiStepsPlugin extends ParentClass
implements MultiStepsSupportHost, IPluginStore.ManipuldateProcessor {
protected OneStepOfMultiSteps[] stepsPlugin;
@Override
public void setSteps(OneStepOfMultiSteps[] stepsPlugin) {
this.stepsPlugin = Objects.requireNonNull(stepsPlugin);
if (stepsPlugin.length != 2) {
throw new IllegalStateException("Expected 2 steps");
}
}
@JSONField(serialize = false)
@Override
public OneStepOfMultiSteps[] getMultiStepsSavedItems() {
return stepsPlugin;
}
@Override
public void manipuldateProcess(IPluginContext currentCtx) {
// Custom persistence logic
}
// Optional: Typed getters for steps
public Step1Plugin getStep1() {
return (Step1Plugin) stepsPlugin[Step.Step1.getStepIndex()];
}
@TISExtension
public static class DefaultDesc extends ParentDesc
implements MultiStepsSupportHostDescriptor<ExampleMultiStepsPlugin> {
@Override
public Class<ExampleMultiStepsPlugin> getHostClass() {
return ExampleMultiStepsPlugin.class;
}
@Override
public List<OneStepOfMultiSteps.BasicDesc> getStepDescriptionList() {
return List.of(new Step1Plugin.Desc(), new Step2Plugin.Desc());
}
@Override
public String getDisplayName() {
return "Example Multi-Steps Plugin";
}
}
}
B. Host Plugin Property Descriptors
File: src/main/resources/{package_path}/{HostPluginName}.json
Follow the same JSON format rules as regular TIS plugins (see /create-plugin skill).
Only include properties directly defined in the host plugin (not properties from step plugins).
If the host plugin only has the stepsPlugin array and identity field, the JSON might be minimal or focus on those fields.
C. Step Plugin Java Classes (for each step)
File structure:
{package_path}/{StepPluginName}.java
Must include:
- Package and imports
- Class declaration:
- Extends
OneStepOfMultiSteps - Optionally implements interfaces for type safety
- Extends
- Public properties with
@FormFieldannotations - Business methods (if needed)
- Optional: Override
processPreSaved()for step-specific logic - Inner Descriptor class:
- Annotated with
@TISExtension - Extends
OneStepOfMultiSteps.BasicDesc - Implements required methods:
getStepDescription()- step display namegetStep()- returns Step enum (Step1, Step2, etc.)nextPluginDesc()- returns next step or Optional.empty()isFinalStep()- true for last step
- Validation methods for properties
- Annotated with
Example structure:
package com.qlangtech.tis.plugin.example;
import com.qlangtech.tis.extension.*;
import com.qlangtech.tis.plugin.annotation.*;
// ... other imports
public class Step1Plugin extends OneStepOfMultiSteps {
@FormField(ordinal = 0, validate = {Validator.require})
public String name;
@FormField(ordinal = 1, type = FormFieldType.ENUM, validate = {Validator.require})
public int type;
@Override
public void processPreSaved(IPluginContext pluginContext) {
// Save this step's data to context for next step
pluginContext.getContext().put(Step1Plugin.class.getName(), this);
super.processPreSaved(pluginContext);
}
@TISExtension
public static class Desc extends OneStepOfMultiSteps.BasicDesc {
@Override
public String getStepDescription() {
return "Basic Settings";
}
@Override
public Step getStep() {
return Step.Step1;
}
@Override
public Optional<BasicDesc> nextPluginDesc(OneStepOfMultiSteps current) {
return Optional.of(new Step2Plugin.Desc());
}
@Override
public boolean isFinalStep() {
return false;
}
// Validation methods as needed
public boolean validateName(IFieldErrorHandler msgHandler, Context context,
String fieldName, String value) {
// Validation logic
return true;
}
}
}
For Step 2 and later steps:
- Can access previous step's data using
OneStepOfMultiSteps.getPreviousStepInstance():
public class Step2Plugin extends OneStepOfMultiSteps {
@FormField(ordinal = 0, type = FormFieldType.ENUM, validate = {Validator.require})
public int option;
// Method to provide dynamic options based on Step 1
public static List<Option> availableOptions() {
// Get Step 1 instance from context
Step1Plugin step1 = OneStepOfMultiSteps.getPreviousStepInstance(Step1Plugin.class);
// Return options based on step1.type
return getOptionsForType(step1.type);
}
@TISExtension
public static class Desc extends OneStepOfMultiSteps.BasicDesc {
@Override
public String getStepDescription() {
return "Advanced Settings";
}
@Override
public Step getStep() {
return Step.Step2;
}
@Override
public Optional<BasicDesc> nextPluginDesc(OneStepOfMultiSteps current) {
return Optional.empty(); // This is the final step
}
@Override
public boolean isFinalStep() {
return true;
}
}
}
D. Step Plugin Property Descriptors
For each step plugin: src/main/resources/{package_path}/{StepPluginName}.json
Follow the same JSON format rules as regular TIS plugins.
Example:
{
"name": {
"label": "Name",
"help": "Unique identifier for this configuration"
},
"type": {
"label": "Type",
"help": "Select the configuration type"
}
}
E. Optional Markdown Help Files
For host or step plugins with complex properties: {PluginName}.md
Follow the same rules as regular TIS plugins (see /create-plugin skill).
Use ## propertyName headers for detailed property documentation.
Step 5: Verify Implementation
Check all requirements are satisfied:
Host Plugin Checklist:
- Implements
MultiStepsSupportHost(directly or via parent) - Has
protected OneStepOfMultiSteps[] stepsPluginproperty - Implements
setSteps()with validation - Implements
getMultiStepsSavedItems()with@JSONField(serialize = false) - Implements
IPluginStore.ManipuldateProcessor - Descriptor implements
MultiStepsSupportHostDescriptor - Descriptor's
getStepDescriptionList()returns steps in correct order - If implements
IdentityName, has identity field andidentityValue()method - If overrides
getDescriptor(), has@JSONField(serialize = false) - JSON descriptor file exists with correct format
- Optional MD file if needed
Step Plugin Checklist (for each step):
- Extends
OneStepOfMultiSteps - All properties are
publicwith@FormField - Descriptor extends
OneStepOfMultiSteps.BasicDesc - Descriptor implements all required methods correctly:
getStepDescription()returns meaningful namegetStep()returns correct Step enumnextPluginDesc()returns correct next step or emptyisFinalStep()is correct
- JSON descriptor file exists with correct format
- Optional MD file if needed
- If step accesses previous step data, uses
getPreviousStepInstance()correctly
Cross-Step Dependencies:
- Step sequence is logical and correctly ordered
- Data passing between steps is properly implemented
- Dynamic options/validation based on previous steps work correctly
Step 6: Provide Usage Instructions
Explain to the user:
- Where all plugin files were created (host + all steps)
- How the multi-step workflow operates
- How to build and test the plugin
- How data flows between steps
- Any special configuration or registration needed
Important Considerations
1. Step Ordering
The order in getStepDescriptionList() is critical:
return List.of(new Step1.Desc(), new Step2.Desc()); // Step1 → Step2
This must match:
- The
Stepenum returned by each descriptor'sgetStep()method - The navigation in
nextPluginDesc()methods - The array indices when accessing
stepsPlugin[]
2. Data Passing Between Steps
Steps communicate via IPluginContext:
In Step N's processPreSaved():
pluginContext.getContext().put(StepNPlugin.class.getName(), this);
In Step N+1:
StepNPlugin prevStep = OneStepOfMultiSteps.getPreviousStepInstance(StepNPlugin.class);
int selectedType = prevStep.type; // Use previous step's data
3. Dynamic Options Based on Previous Steps
When a step's dropdown options depend on a previous step:
public static List<Option> availableOptions() {
Step1Plugin step1 = OneStepOfMultiSteps.getPreviousStepInstance(Step1Plugin.class);
// Generate options based on step1's values
return generateOptionsFor(step1.selectedValue);
}
4. Validation Across Steps
For validation that spans multiple steps, implement in the host plugin's manipuldateProcess():
@Override
public void manipuldateProcess(IPluginContext currentCtx) {
Step1Plugin step1 = (Step1Plugin) stepsPlugin[0];
Step2Plugin step2 = (Step2Plugin) stepsPlugin[1];
// Validate cross-step business rules
if (!isCompatible(step1.type, step2.option)) {
throw new IllegalStateException("Incompatible configuration");
}
}
5. JSON Descriptor Format
Use the same object-to-object format as regular plugins:
- ✅ Correct:
{"propertyName": {"label": "...", "help": "..."}} - ❌ Wrong:
{"formFields": [{"key": "propertyName", ...}]}
6. Number of Steps
TIS supports up to 7 steps (Step1 through Step7). Choose the minimum number of steps needed for clear user workflow.
7. Final Step Requirements
The last step MUST:
- Return
truefromisFinalStep() - Return
Optional.empty()fromnextPluginDesc()
8. Identity Field Requirement
If implementing IdentityName, you must add a dummy identity field even if unused. This is a TIS framework requirement.
Example Interaction
User: /create-multi-steps-plugin
I want to create a user registration plugin with two steps:
- Step 1: Select province (dropdown)
- Step 2: Select city (dropdown based on selected province)
Version History
- 95bcb21 Current 2026-09-08 17:31


