mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-11 23:27:25 +08:00
feat: add tinystruct-patterns skill and bootstrapping guidance (#1336)
* feat: add tinystruct-patterns skill and bootstrapping guidance * docs(skills): restructuralize tinystruct-patterns to standard skill format - Reorganize SKILL.md and all reference documents into "When to Use", "How It Works", and "Examples" sections to conform to project standards. - Refine data-handling.md example to return Builder objects directly, leveraging framework auto-serialization. - Simplify @Action examples in routing.md for better readability. - Clarify framework mechanics including CLI bootstrapping via ApplicationManager.init(), event-driven architecture, and regex-based routing. * Update skills/tinystruct-patterns/references/testing.md Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update skills/tinystruct-patterns/SKILL.md Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update skills/tinystruct-patterns/references/routing.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update skills/tinystruct-patterns/references/testing.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * Update skills/tinystruct-patterns/references/testing.md Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
131
skills/tinystruct-patterns/SKILL.md
Normal file
131
skills/tinystruct-patterns/SKILL.md
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
name: tinystruct-patterns
|
||||
description: Use when developing application modules or microservices with the tinystruct Java framework. Covers routing, context management, JSON handling with Builder, and CLI/HTTP dual-mode patterns.
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# tinystruct Development Patterns
|
||||
|
||||
Architecture and implementation patterns for building modules with the **tinystruct** Java framework – a lightweight system where CLI and HTTP are equal citizens.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Creating new `Application` modules by extending `AbstractApplication`.
|
||||
- Defining routes and command-line actions using `@Action`.
|
||||
- Handling per-request state via `Context`.
|
||||
- Performing JSON serialization using the native `Builder` component.
|
||||
- Configuring database connections or system settings in `application.properties`.
|
||||
- Generating or re-generating the standard `bin/dispatcher` entry point via `ApplicationManager.init()`.
|
||||
- Debugging routing conflicts (Actions) or CLI argument parsing.
|
||||
|
||||
## How It Works
|
||||
|
||||
The tinystruct framework treats any method annotated with `@Action` as a routable endpoint for both terminal and web environments. Applications are created by extending `AbstractApplication`, which provides core lifecycle hooks like `init()` and access to the request `Context`.
|
||||
|
||||
Routing is handled by the `ActionRegistry`, which automatically maps path segments to method arguments and injects dependencies. For data-only services, the native `Builder` component should be used for JSON serialization to maintain a zero-dependency footprint. The framework also includes a utility in `ApplicationManager` to bootstrap the project's execution environment by generating the `bin/dispatcher` script.
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Application (MyService)
|
||||
```java
|
||||
public class MyService extends AbstractApplication {
|
||||
@Override
|
||||
public void init() {
|
||||
this.setTemplateRequired(false); // Disable .view lookup for data/API apps
|
||||
}
|
||||
|
||||
@Override public String version() { return "1.0.0"; }
|
||||
|
||||
@Action("greet")
|
||||
public String greet() {
|
||||
return "Hello from tinystruct!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parameterized Routing (getUser)
|
||||
```java
|
||||
// Handles /api/user/123 (Web) or "bin/dispatcher api/user/123" (CLI)
|
||||
@Action("api/user/(\\d+)")
|
||||
public String getUser(int userId) {
|
||||
return "User ID: " + userId;
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Mode Disambiguation (login)
|
||||
```java
|
||||
@Action(value = "login", mode = Mode.HTTP_POST)
|
||||
public boolean doLogin() {
|
||||
// Process login logic
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Native JSON Data Handling (getData)
|
||||
```java
|
||||
@Action("api/data")
|
||||
public Builder getData() throws ApplicationException {
|
||||
Builder builder = new Builder();
|
||||
builder.put("status", "success");
|
||||
Builder nested = new Builder();
|
||||
nested.put("id", 1);
|
||||
nested.put("name", "James");
|
||||
builder.put("data", nested);
|
||||
return builder;
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings are managed in `src/main/resources/application.properties`.
|
||||
|
||||
```properties
|
||||
# Database
|
||||
driver=org.h2.Driver
|
||||
database.url=jdbc:h2:~/mydb
|
||||
|
||||
# App specific
|
||||
my.service.endpoint=https://api.example.com
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
Use JUnit 5 to test actions by verifying they are registered in the `ActionRegistry`.
|
||||
|
||||
```java
|
||||
@Test
|
||||
void testActionRegistration() {
|
||||
Application app = new MyService();
|
||||
app.init();
|
||||
|
||||
ActionRegistry registry = ActionRegistry.getInstance();
|
||||
assertNotNull(registry.get("greet"));
|
||||
}
|
||||
```
|
||||
|
||||
## Red Flags & Anti-patterns
|
||||
|
||||
| Symptom | Correct Pattern |
|
||||
|---|---|
|
||||
| Importing `com.google.gson` or `com.fasterxml.jackson` | Use `org.tinystruct.data.component.Builder`. |
|
||||
| `FileNotFoundException` for `.view` files | Call `setTemplateRequired(false)` in `init()` for API-only apps. |
|
||||
| Annotating `private` methods with `@Action` | Actions must be `public` to be registered by the framework. |
|
||||
| Hardcoding `main(String[] args)` in apps | Use `bin/dispatcher` as the entry point for all modules. |
|
||||
| Manual `ActionRegistry` registration | Prefer the `@Action` annotation for automatic discovery. |
|
||||
|
||||
## Technical Reference
|
||||
|
||||
Detailed guides are available in the `references/` directory:
|
||||
|
||||
- [Architecture & Config](references/architecture.md) — Abstractions, Package Map, Properties
|
||||
- [Routing & @Action](references/routing.md) — Annotation details, Modes, Parameters
|
||||
- [Data Handling](references/data-handling.md) — Using the native `Builder` for JSON
|
||||
- [System & Usage](references/system-usage.md) — Context, Sessions, Events, CLI usage
|
||||
- [Testing Patterns](references/testing.md) — JUnit 5 integration and ActionRegistry testing
|
||||
|
||||
## Reference Source Files (Internal)
|
||||
|
||||
- `src/main/java/org/tinystruct/AbstractApplication.java` — Core base class
|
||||
- `src/main/java/org/tinystruct/system/annotation/Action.java` — Annotation & Modes
|
||||
- `src/main/java/org/tinystruct/application/ActionRegistry.java` — Routing Engine
|
||||
- `src/main/java/org/tinystruct/data/component/Builder.java` — JSON/Data Serializer
|
||||
77
skills/tinystruct-patterns/references/architecture.md
Normal file
77
skills/tinystruct-patterns/references/architecture.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# tinystruct Architecture and Configuration
|
||||
|
||||
## When to Use
|
||||
|
||||
Choose **tinystruct** when you need a lightweight, high-performance Java framework that treats CLI and HTTP as equal citizens. It is ideal for building microservices, command-line utilities, and data-driven applications where a small footprint and zero-dependency JSON handling are required. Use it when you want to write logic once and expose it via both a terminal and a web server without modification.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Core Architecture
|
||||
|
||||
The framework operates on a singleton `ActionRegistry` that maps URL patterns (or command strings) to `Action` objects. When a request arrives, the system resolves the path and invokes the corresponding method handle.
|
||||
|
||||
#### Key Abstractions
|
||||
|
||||
| Class/Interface | Role |
|
||||
|---|---|
|
||||
| `AbstractApplication` | Base class for all tinystruct applications. Extend this. |
|
||||
| `@Action` annotation | Maps a method to a URI path (web) or command name (CLI). The single routing primitive. |
|
||||
| `ActionRegistry` | Singleton that maps URL patterns to `Action` objects via regex. Never instantiate directly. |
|
||||
| `Action` | Wraps a `MethodHandle` + regex pattern + priority + `Mode` for dispatch. |
|
||||
| `Context` | Per-request state store. Access via `getContext()`. Holds CLI args and HTTP request/response. |
|
||||
| `Dispatcher` | CLI entry point (`bin/dispatcher`). Reads `--import` to load applications. |
|
||||
| `HttpServer` | Built-in Netty-based HTTP server. Start with `bin/dispatcher start --import org.tinystruct.system.HttpServer`. |
|
||||
|
||||
### Package Map
|
||||
|
||||
```
|
||||
org.tinystruct/
|
||||
├── AbstractApplication.java ← extend this
|
||||
├── Application.java ← interface
|
||||
├── ApplicationException.java ← checked exception
|
||||
├── ApplicationRuntimeException.java ← unchecked exception
|
||||
├── application/
|
||||
│ ├── Action.java ← runtime action wrapper
|
||||
│ ├── ActionRegistry.java ← singleton route registry
|
||||
│ └── Context.java ← request context
|
||||
├── system/
|
||||
│ ├── annotation/Action.java ← @Action annotation + Mode enum
|
||||
│ ├── Dispatcher.java ← CLI dispatcher
|
||||
│ ├── HttpServer.java ← built-in HTTP server
|
||||
│ ├── EventDispatcher.java ← event bus
|
||||
│ └── Settings.java ← reads application.properties
|
||||
├── data/component/Builder.java ← JSON serialization (use instead of Gson/Jackson)
|
||||
└── http/ ← Request, Response, Constants
|
||||
```
|
||||
|
||||
### Template Behavior and Dispatch Flow
|
||||
|
||||
By default, the framework assumes a view template is required. If `templateRequired` is `true`, `toString()` looks for a `.view` file in `src/main/resources/themes/<ClassName>.view`. Use `getContext()` to manage state and `setVariable("name", value)` to pass data to templates, which use `[%name%]` for interpolation.
|
||||
|
||||
## Examples
|
||||
|
||||
### Minimal Application Initialization
|
||||
```java
|
||||
@Override
|
||||
public void init() {
|
||||
this.setTemplateRequired(false); // Skip .view template lookup for data-only apps
|
||||
}
|
||||
```
|
||||
|
||||
### Action Definition and CLI Invocation
|
||||
```java
|
||||
@Action("hello")
|
||||
public String hello() {
|
||||
return "Hello, tinystruct!";
|
||||
}
|
||||
```
|
||||
**Execution via Dispatcher:**
|
||||
```bash
|
||||
bin/dispatcher hello
|
||||
```
|
||||
|
||||
### Configuration Access
|
||||
Located at `src/main/resources/application.properties`:
|
||||
```java
|
||||
String port = this.getConfiguration("server.port");
|
||||
```
|
||||
35
skills/tinystruct-patterns/references/data-handling.md
Normal file
35
skills/tinystruct-patterns/references/data-handling.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# tinystruct Data Handling (JSON)
|
||||
|
||||
## When to Use
|
||||
|
||||
Prefer `org.tinystruct.data.component.Builder` in scenarios where you need a lightweight, high-performance JSON solution with **zero external dependencies**. It is specifically designed to keep your tinystruct applications lean and fast, making it the ideal choice for microservices and CLI tools where including heavy libraries like Jackson or Gson would be overkill.
|
||||
|
||||
## How It Works
|
||||
|
||||
The `Builder` class provides a simple key-value interface for both creating and reading JSON structures. It integrates directly with `AbstractApplication` result handling; when an action method returns a `Builder` object, the framework automatically serializes it to the response stream. This prevents the need for manual string conversion and ensures consistent data formatting across your application modules.
|
||||
|
||||
## Examples
|
||||
|
||||
### Serialization
|
||||
```java
|
||||
import org.tinystruct.data.component.Builder;
|
||||
|
||||
// Create and populate
|
||||
Builder response = new Builder();
|
||||
response.put("status", "success");
|
||||
response.put("count", 42);
|
||||
response.put("data", someList);
|
||||
|
||||
return response; // {"status":"success","count":42,...}
|
||||
```
|
||||
|
||||
### Parsing
|
||||
```java
|
||||
import org.tinystruct.data.component.Builder;
|
||||
|
||||
// Parse a JSON string
|
||||
Builder parsed = new Builder();
|
||||
parsed.parse(jsonString);
|
||||
|
||||
String status = parsed.get("status").toString();
|
||||
```
|
||||
57
skills/tinystruct-patterns/references/routing.md
Normal file
57
skills/tinystruct-patterns/references/routing.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# tinystruct @Action Routing Reference
|
||||
|
||||
## When to Use
|
||||
|
||||
Use the `@Action` annotation in your applications to define routes for both CLI commands and HTTP endpoints. It is appropriate whenever you need to map logic to a specific path, handle parameterized requests (e.g., retrieving a resource by ID), or restrict execution to specific HTTP methods (GET, POST, etc.) while maintaining a consistent command structure across environments.
|
||||
|
||||
## How It Works
|
||||
|
||||
The `ActionRegistry` parses `@Action` annotations to build a routing table. For parameterized methods, the framework automatically maps Java parameter types (int, String, etc.) to corresponding regex segments to generate an internal matching pattern. For instance, `getUser(int id)` generates a regex targeting digits, while `search(String query)` targets generic path segments.
|
||||
|
||||
When a request is dispatched, the `ActionRegistry` automatically injects dependencies like `Request` and `Response` into the action method if they are specified as parameters, drawing them directly from the current request's `Context`. Execution is further filtered by the `Mode` value, allowing a single path to invoke different logic depending on whether the trigger was a terminal command or a specific type of HTTP request.
|
||||
|
||||
### Mode Values
|
||||
|
||||
| Mode | When it triggers |
|
||||
|---|---|
|
||||
| `DEFAULT` | Both CLI and HTTP (GET, POST, etc.) |
|
||||
| `CLI` | CLI dispatcher only |
|
||||
| `HTTP_GET` | HTTP GET only |
|
||||
| `HTTP_POST` | HTTP POST only |
|
||||
| `HTTP_PUT` | HTTP PUT only |
|
||||
| `HTTP_DELETE` | HTTP DELETE only |
|
||||
| `HTTP_PATCH` | HTTP PATCH only |
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Action Declaration
|
||||
```java
|
||||
@Action(
|
||||
value = "path/subpath", // required: URI segment or CLI command
|
||||
description = "What it does", // shown in --help output
|
||||
mode = Mode.HTTP_POST, // default: Mode.DEFAULT (both CLI + HTTP)
|
||||
options = {}, // CLI option flags
|
||||
example = "curl -X POST http://localhost:8080/path/subpath/42"
|
||||
)
|
||||
public String myAction(int id) { ... }
|
||||
```
|
||||
|
||||
### Parameterized Paths (Regex Generation)
|
||||
```java
|
||||
@Action("user/{id}")
|
||||
public String getUser(int id) { ... }
|
||||
// → pattern: ^/?user/(-?\d+)$
|
||||
|
||||
@Action("search")
|
||||
public String search(String query) { ... }
|
||||
// → pattern: ^/?search/([^/]+)$
|
||||
```
|
||||
|
||||
### Request and Response Injection
|
||||
```java
|
||||
@Action(value = "upload", mode = Mode.HTTP_POST)
|
||||
public String upload(Request<?, ?> req, Response<?, ?> res) throws ApplicationException {
|
||||
// req.getParameter("file"), res.setHeader(...), etc.
|
||||
return "ok";
|
||||
}
|
||||
```
|
||||
74
skills/tinystruct-patterns/references/system-usage.md
Normal file
74
skills/tinystruct-patterns/references/system-usage.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# tinystruct System and Usage Reference
|
||||
|
||||
## When to Use
|
||||
|
||||
Use the system and usage patterns described here when you need to handle stateful interactions across CLI and HTTP modes, manage user sessions in web applications, or implement loosely coupled communication between application modules using an event-driven architecture.
|
||||
|
||||
## How It Works
|
||||
|
||||
The framework's `Context` serves as the primary data store for request-specific state. In CLI mode, flags passed as `--key value` are automatically parsed and stored in the `Context` with the `--` prefix, allowing action methods to retrieve command parameters easily. For web applications, the system provides standard session management via the `Request` object, enabling the storage of user data across multiple HTTP requests.
|
||||
|
||||
The internal `EventDispatcher` facilitates an asynchronous event bus. By defining custom `Event` classes and registering handlers (typically within an application's `init()` method), you can trigger background tasks—such as sending emails or logging audit trails—without blocking the main execution path.
|
||||
|
||||
## Examples
|
||||
|
||||
### Context and CLI Arguments
|
||||
```java
|
||||
@Action("echo")
|
||||
public String echo() {
|
||||
// CLI: bin/dispatcher echo --words "Hello World"
|
||||
Object words = getContext().getAttribute("--words");
|
||||
if (words != null) return words.toString();
|
||||
return "No words provided";
|
||||
}
|
||||
```
|
||||
|
||||
### Session Management (Web Mode)
|
||||
```java
|
||||
@Action(value = "login", mode = Mode.HTTP_POST)
|
||||
public String login(Request request) {
|
||||
request.getSession().setAttribute("userId", "42");
|
||||
return "Logged in";
|
||||
}
|
||||
|
||||
@Action("profile")
|
||||
public String profile(Request request) {
|
||||
Object userId = request.getSession().getAttribute("userId");
|
||||
if (userId == null) return "Not logged in";
|
||||
return "User: " + userId;
|
||||
}
|
||||
```
|
||||
|
||||
### Event System
|
||||
```java
|
||||
// 1. Define an event
|
||||
public class OrderCreatedEvent implements org.tinystruct.system.Event<Order> {
|
||||
private final Order order;
|
||||
public OrderCreatedEvent(Order order) { this.order = order; }
|
||||
|
||||
@Override public String getName() { return "order_created"; }
|
||||
@Override public Order getPayload() { return order; }
|
||||
}
|
||||
|
||||
// 2. Register a handler
|
||||
EventDispatcher.getInstance().registerHandler(OrderCreatedEvent.class, event -> {
|
||||
CompletableFuture.runAsync(() -> sendConfirmationEmail(event.getPayload()));
|
||||
});
|
||||
|
||||
// 3. Dispatch
|
||||
EventDispatcher.getInstance().dispatch(new OrderCreatedEvent(newOrder));
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
# CLI mode
|
||||
bin/dispatcher hello
|
||||
bin/dispatcher echo --words "Hello" --import com.example.HelloApp
|
||||
|
||||
# HTTP server (listens on :8080 by default)
|
||||
bin/dispatcher start --import org.tinystruct.system.HttpServer
|
||||
|
||||
# Database utilities
|
||||
bin/dispatcher generate --table users
|
||||
bin/dispatcher sql-query "SELECT * FROM users"
|
||||
```
|
||||
59
skills/tinystruct-patterns/references/testing.md
Normal file
59
skills/tinystruct-patterns/references/testing.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# tinystruct Testing Patterns
|
||||
|
||||
## When to Use
|
||||
|
||||
Use the testing patterns described here when writing units tests for your tinystruct applications with **JUnit 5**. These patterns are essential for verifying that your `@Action` methods return the correct results and that your routing logic is properly registered within the singleton `ActionRegistry`.
|
||||
|
||||
## How It Works
|
||||
|
||||
Testing tinystruct applications requires a specific setup to ensure framework-level features like annotation processing and configuration management are active. By creating a new instance of your application and passing it a `Settings` object in the `setUp()` method, you trigger the `init()` lifecycle. This ensures all `@Action` methods are discovered and registered.
|
||||
|
||||
Because the `ActionRegistry` is a singleton, it is critical to maintain isolation between tests by properly initializing your application state before each test execution, preventing side effects from leaking across the test suite.
|
||||
|
||||
## Examples
|
||||
|
||||
### Unit Testing an Application
|
||||
```java
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.tinystruct.application.ActionRegistry;
|
||||
import org.tinystruct.system.Settings;
|
||||
|
||||
class MyAppTest {
|
||||
|
||||
private MyApp app;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
app = new MyApp();
|
||||
Settings config = new Settings();
|
||||
app.setConfiguration(config);
|
||||
app.init(); // triggers @Action annotation processing
|
||||
}
|
||||
void testHello() throws Exception {
|
||||
// Direct invocation via the application object
|
||||
Object result = app.invoke("hello");
|
||||
Assertions.assertEquals("Hello, tinystruct!", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGreet() throws Exception {
|
||||
// Invocation with arguments
|
||||
Object result = app.invoke("greet", new Object[]{"James"});
|
||||
Assertions.assertEquals("Hello, James!", result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing via ActionRegistry
|
||||
If you need to test the routing logic itself, use the `ActionRegistry` singleton to verify path matching:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void testRouting() {
|
||||
ActionRegistry registry = ActionRegistry.getInstance();
|
||||
// Verify a path matches an action
|
||||
Action action = registry.getAction("greet/James");
|
||||
Assertions.assertNotNull(action);
|
||||
}
|
||||
```
|
||||
Reference: `src/test/java/org/tinystruct/application/ActionRegistryTest.java`
|
||||
Reference in New Issue
Block a user