# Forge Import Settings Editor

> Add per-asset import settings with TOML sidecar files, three-layer merge (schema → global → per-asset), a settings editor form with type-specific controls, and single-asset re-processing from the web UI. Use when someone needs configurable per-file processing overrides, settings UI, or sidecar-based configuration.

- **Type:** Skill
- **Install:** `agentstack add skill-nebulavenus-forge-gpu-forge-import-settings-editor`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Nebulavenus](https://agentstack.voostack.com/s/nebulavenus)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** Zlib
- **Upstream author:** [Nebulavenus](https://github.com/Nebulavenus)
- **Source:** https://github.com/Nebulavenus/forge-gpu/tree/main/.claude/skills/forge-import-settings-editor

## Install

```sh
agentstack add skill-nebulavenus-forge-gpu-forge-import-settings-editor
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# forge-import-settings-editor

Add per-asset import settings to a pipeline web UI. Assets get individual
`.import.toml` sidecar files that override global config, and a browser-based
editor form renders type-specific controls for each setting.

## When to use this skill

- Adding per-asset configuration overrides to a processing pipeline
- Building a settings editor form driven by a schema definition
- Implementing three-layer settings merge (defaults → global → per-item)
- Adding TOML sidecar file I/O without a third-party TOML writer
- Triggering single-item re-processing from a web UI

## Key functions and endpoints

| Component | Function / Route | Purpose |
|---|---|---|
| `import_settings.py` | `sidecar_path(source)` | Return `.import.toml` path for a source file |
| `import_settings.py` | `load_sidecar(source)` | Parse sidecar TOML, empty dict if missing |
| `import_settings.py` | `save_sidecar(source, settings)` | Write settings to sidecar file |
| `import_settings.py` | `delete_sidecar(source)` | Remove sidecar (revert to defaults) |
| `import_settings.py` | `merge_settings(global, per_asset)` | Two-layer overlay merge |
| `import_settings.py` | `get_effective_settings(plugin, global, per_asset)` | Three-layer merge with schema defaults |
| `import_settings.py` | `get_schema(plugin_name)` | Return schema dict for a plugin type |
| `server.py` | `GET /api/assets/{id}/settings` | Read schema + global + per-asset + effective |
| `server.py` | `PUT /api/assets/{id}/settings` | Save per-asset overrides as `.import.toml` |
| `server.py` | `DELETE /api/assets/{id}/settings` | Remove sidecar (revert to defaults) |
| `server.py` | `POST /api/assets/{id}/process` | Re-process with effective settings |

## Architecture

Three-layer settings merge:

```text
Schema Defaults ──> Global Config (pipeline.toml) ──> Per-Asset (.import.toml)
       │                      │                              │
       └──────────────────────┴──────────────────────────────┘
                                    │
                              get_effective_settings()
                                    │
                              Effective Settings
```

Each layer overrides the previous. `get_effective_settings()` starts with
schema defaults, overlays global settings, then per-asset overrides.

## Correct order

### Backend

1. Define settings schemas in `import_settings.py` — one dict per plugin type
   with `type`, `label`, `description`, `default`, and optional constraints
2. Implement sidecar CRUD — `sidecar_path`, `load_sidecar`, `save_sidecar`,
   `delete_sidecar`
3. Implement merge functions — `merge_settings`, `get_effective_settings`
4. Add API endpoints to `server.py` — GET, PUT, DELETE for settings, POST for
   process
5. Integrate sidecars into CLI processing — load sidecars in `_process_files()`,
   include settings in fingerprint cache key

### Frontend

1. Add TypeScript types for `ImportSettingsResponse`, `SettingsSchemaField`,
   `ProcessResponse` in `api.ts`
2. Add API functions — `fetchImportSettings`, `saveImportSettings`,
   `deleteImportSettings`, `processAsset`
3. Add UI primitives — `Label`, `Select`, `Switch`, `Separator` in
   `components/ui/`
4. Build `ImportSettings` component with schema-driven form rendering
5. Mount on the asset detail page below the preview panel
6. Wire TanStack Query cache invalidation on mutations

## Settings schema definition

Each plugin type has a schema dict describing its configurable fields:

```python
TEXTURE_SETTINGS_SCHEMA: dict[str, dict] = {
    "max_size": {
        "type": "int",           # Control type: bool, int, float, str, list[float]
        "label": "Max size",     # Human-readable label
        "description": "Clamp width and height to this limit (pixels).",
        "default": 2048,         # Schema default value
        "min": 1,                # Optional: numeric minimum
        "max": 8192,             # Optional: numeric maximum
    },
    "compression": {
        "type": "str",
        "label": "GPU compression",
        "description": "GPU block-compression codec.",
        "default": "none",
        "options": ["none", "basisu", "astc"],  # Optional: enum values
    },
    "normal_map": {
        "type": "bool",
        "label": "Normal map",
        "description": "Treat as a normal map (BC5, linear color space).",
        "default": False,
    },
}

SETTINGS_SCHEMAS: dict[str, dict[str, dict]] = {
    "texture": TEXTURE_SETTINGS_SCHEMA,
    "mesh": MESH_SETTINGS_SCHEMA,
}
```

## TOML sidecar format

Sidecars are flat key-value TOML files. Only overridden keys are present:

```toml
normal_map = true
compression = "basisu"
basisu_quality = 200
```

Write TOML with string formatting — no third-party serializer needed for flat
tables. Check `isinstance(value, bool)` before `isinstance(value, int)` since
`bool` is a subclass of `int` in Python.

## Frontend control mapping

| Schema `type` | UI control | Notes |
|---|---|---|
| `bool` | `Switch` toggle | `onCheckedChange` callback |
| `str` with `options` | `Select` dropdown | Renders `` per entry |
| `int` | `Input type="number"` | Uses `min`/`max` from schema |
| `float` | `Input type="number"` | `step={0.01}` |
| `list[float]` | `Input type="text"` | Comma-separated, parsed on change |

## Common mistakes

1. **Forgetting bool-before-int check in TOML formatting.** Python's `bool` is
   a subclass of `int`, so `isinstance(True, int)` is `True`. Always check
   `isinstance(value, bool)` first.

2. **Not including settings in the fingerprint cache key.** If only the content
   hash is cached, changing a sidecar does not trigger reprocessing. Combine
   the content hash with a JSON digest of the merged settings.

3. **Mutating the global settings dict during merge.** `merge_settings()` must
   return a new dict — never `.update()` the input.

4. **Not invalidating the right TanStack Query keys.** After processing, both
   `["asset", id]` and `["assets"]` (list) must be invalidated so the status
   badge and preview update.

5. **Missing error handling for malformed sidecars.** `load_sidecar()` should
   catch `TOMLDecodeError` and raise a clear `ValueError`. The CLI should
   log a warning and fall back to global settings; the API should return 400.

## Ready-to-use template

### import_settings.py — core pattern

```python
from pathlib import Path
import tomllib

SIDECAR_SUFFIX = ".import.toml"

def sidecar_path(source: Path) -> Path:
    return source.parent / (source.name + SIDECAR_SUFFIX)

def load_sidecar(source: Path) -> dict:
    path = sidecar_path(source)
    if not path.is_file():
        return {}
    try:
        with path.open("rb") as f:
            return tomllib.load(f)
    except tomllib.TOMLDecodeError as exc:
        raise ValueError(f"Malformed sidecar {path}: {exc}") from exc

def get_effective_settings(plugin_name: str, global_s: dict, per_asset: dict) -> dict:
    schema = SETTINGS_SCHEMAS.get(plugin_name, {})
    result = {key: spec["default"] for key, spec in schema.items()}
    result.update(global_s)
    result.update(per_asset)
    return result
```

### API endpoint pattern

```python
@app.get("/api/assets/{asset_id}/settings")
async def get_import_settings(asset_id: str):
    asset = _get_cached_asset(asset_id)
    plugin_name = _plugin_name_for_type(asset.asset_type)
    schema = get_schema(plugin_name)
    global_settings = config.plugin_settings.get(plugin_name, {})
    per_asset = load_sidecar(Path(asset.source_path))
    effective = get_effective_settings(plugin_name, global_settings, per_asset)
    return {
        "schema_fields": schema,
        "global_settings": global_settings,
        "per_asset": per_asset,
        "effective": effective,
        "has_overrides": bool(per_asset),
    }
```

### React component pattern

```tsx
const { data: settings } = useQuery({
  queryKey: ["settings", assetId],
  queryFn: () => fetchImportSettings(assetId),
})

const saveMutation = useMutation({
  mutationFn: (overrides) => saveImportSettings(assetId, overrides),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["settings", assetId] })
  },
})
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Nebulavenus](https://github.com/Nebulavenus)
- **Source:** [Nebulavenus/forge-gpu](https://github.com/Nebulavenus/forge-gpu)
- **License:** Zlib

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-nebulavenus-forge-gpu-forge-import-settings-editor
- Seller: https://agentstack.voostack.com/s/nebulavenus
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
