Metadata-Version: 2.4
Name: vapor-idx
Version: 0.1.1
Summary: Pure RAM, per-skill, declarative in-memory indexing framework for AI skill authors
Project-URL: Homepage, https://github.com/RebornBeat/vapor-idx
Project-URL: Repository, https://github.com/RebornBeat/vapor-idx
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# vapor-idx

> **Pure RAM. Per-skill. Gone when you're done.**

`vapor-idx` is a zero-dependency, fully declarative, in-memory indexing framework
for AI skill authors. It gives individual skills a structured, queryable,
traversable knowledge store that lives entirely in process memory and disappears
the moment the skill ends — no files, no databases, no persistence, no cross-skill
leakage.

It is RAM — shaped, named, and queryable — scoped to exactly one skill invocation
at a time.

**GitHub:** [https://github.com/RebornBeat/vapor-idx](https://github.com/RebornBeat/vapor-idx)
**Discord:** [https://discord.gg/vapor-idx](https://discord.gg/vapor-idx)

---

## Why does this exist?

When you write a Claude skill that needs to reason over structured data — a code
analysis skill that must track function relationships, a document skill that must
link sections to their references, an image skill that must index pixels spatially,
a 3D skill that must traverse mesh topology — you need somewhere to put that
structure while the skill runs. Without a tool like this, every skill author
re-implements ad-hoc Maps, arrays of objects, and linear scans.

`vapor-idx` provides the scaffold so skill authors declare their data model once
and query it cleanly, without reinventing indexing primitives every time.

**What it is not:**
- It does not persist anything. Ever. By design.
- It does not share state between skills.
- It does not use embeddings, ML models, or vector search.
- It does not have defaults. Every type, field, and index strategy is declared
  explicitly by the skill author.
- It does not enforce opinions about your data shape.

**`vapor.store(type, data)` stores in RAM.** Records live as JavaScript objects,
Python dicts, and Rust structs — inside the running process, held in working
memory, gone when the skill ends. Nothing is written to disk unless the skill
author explicitly chooses to serialise a snapshot themselves.

---

## The broader vision: modality-first indexing

`vapor-idx` is not limited to traditional structured data. Because it indexes any
declared schema into RAM relationships, it can decompose and traverse **any
modality** that can be expressed as typed records with connections.

### Images without ML dependencies

Instead of YOLO, ResNet, or any external model, declare a `Pixel` type and index
every pixel by position and colour. Traverse adjacency relationships. Claude —
acting as the zero-shot intelligence within the skill — applies its own semantic
reasoning over the structured index to identify regions, edges, and objects.
Reconstruct from the indexed structure back to a `.png` file, an SVG, or a CSS
layout — no ML dependency, no external model call, pure RAM.

```typescript
const vapor = createVapor({
  types: {
    Pixel: {
      fields: {
        x:         { type: 'number', index: 'range' },
        y:         { type: 'number', index: 'range' },
        r:         { type: 'number', index: 'range' },
        g:         { type: 'number', index: 'range' },
        b:         { type: 'number', index: 'range' },
        edgeScore: { type: 'number', index: 'range' },
        region:    { type: 'string', index: 'exact'  },
      },
      relationships: {
        ADJACENT_TO: { targetTypes: ['Pixel'],  directed: false, cardinality: 'many-to-many' },
        BELONGS_TO:  { targetTypes: ['Region'], directed: true,  cardinality: 'many-to-one'  },
      },
    },
    Region: {
      fields: {
        label:   { type: 'string', index: 'keyword' },
        area:    { type: 'number', index: 'range'   },
        centerX: { type: 'number', index: 'range'   },
        centerY: { type: 'number', index: 'range'   },
      },
    },
  },
});
```

### 3D mesh without a render engine

Index every vertex, edge, face, and material from an OBJ/STL/GLTF as typed
records with spatial relationships. Traverse mesh topology. Emit a Blender Python
script that reconstructs the full `.blend` file, or reconstruct a clean OBJ —
from indexed structure alone. No rendering engine required during analysis. The
full Blender API surface can be reconstructed from indexed data, including
armatures, physics, materials, animations, and modifiers — all as pure Python.

```typescript
const vapor = createVapor({
  types: {
    Vertex: {
      fields: {
        x:             { type: 'number', index: 'range' },
        y:             { type: 'number', index: 'range' },
        z:             { type: 'number', index: 'range' },
        normalX:       { type: 'number', index: 'none'  },
        normalY:       { type: 'number', index: 'none'  },
        normalZ:       { type: 'number', index: 'none'  },
        materialIndex: { type: 'number', index: 'exact' },
      },
      relationships: {
        CONNECTED_TO: { targetTypes: ['Vertex'], directed: false, cardinality: 'many-to-many' },
        PART_OF:      { targetTypes: ['Face'],   directed: true,  cardinality: 'many-to-many' },
      },
    },
    Face: {
      fields: {
        materialIndex: { type: 'number', index: 'exact' },
        area:          { type: 'number', index: 'range' },
        smoothGroup:   { type: 'number', index: 'exact' },
      },
      relationships: {},
    },
    Material: {
      fields: {
        name:      { type: 'string', index: 'exact' },
        roughness: { type: 'number', index: 'range' },
        metallic:  { type: 'number', index: 'range' },
        baseColorR:{ type: 'number', index: 'range' },
        baseColorG:{ type: 'number', index: 'range' },
        baseColorB:{ type: 'number', index: 'range' },
      },
      relationships: {},
    },
  },
});
```

### Cross-modal reconstruction

Index elements from one modality, traverse semantically, and reconstruct in
another. An indexed SVG design becomes a CSS layout. An indexed audio waveform
becomes a MIDI file. An indexed set of pixels becomes a vector graphic. An indexed
3D mesh becomes a Blender Python script. Claude traverses the relationships and
drives the reconstruction — the index is the bridge, not any ML model.

This works because **Claude is the zero-shot intelligence** when a skill runs.
vapor-idx provides the structure; Claude provides the reasoning. Together they
enable modality understanding and reconstruction without any external model calls.

---

## Monorepo structure

Skills execute in multiple runtimes. A single npm package cannot serve a Python
computer-use skill, and a Python package cannot serve a browser artifact. Each
package is a faithful implementation of the same API contract in its target
language.

```
vapor-idx/
├── README.md
├── TESTING.md                         ← deploy + test guide
├── DOCS/
│   ├── CONCEPT.md
│   ├── API_REFERENCE.md
│   ├── PIXEL_INDEXING.md
│   ├── 3D_INDEXING.md
│   └── CROSS_MODAL.md
├── skills/
│   ├── pixel-analyzer/SKILL.md
│   ├── mesh-analyzer/SKILL.md
│   ├── design-indexer/SKILL.md
│   └── cross-modal-reconstructor/SKILL.md
└── packages/
    ├── vapor-idx/                     ← TypeScript / JavaScript (Node.js + Browser ESM)
    │   ├── package.json
    │   ├── tsconfig.json
    │   └── src/
    │       ├── index.ts
    │       ├── types.ts
    │       ├── VaporInstance.ts
    │       ├── SchemaValidator.ts
    │       ├── RecordStore.ts
    │       ├── RelationshipStore.ts
    │       ├── QueryEngine.ts
    │       ├── TraversalEngine.ts
    │       └── indexes/
    │           ├── ExactIndex.ts
    │           ├── KeywordIndex.ts
    │           ├── PrefixIndex.ts
    │           └── RangeIndex.ts
    │
    ├── vapor-idx-py/                  ← Python 3.10+ (PyPI)
    │   ├── pyproject.toml
    │   └── vapor_idx/
    │       ├── __init__.py
    │       ├── types.py
    │       ├── instance.py
    │       ├── schema.py
    │       ├── record_store.py
    │       ├── relationship_store.py
    │       ├── query_engine.py
    │       ├── traversal_engine.py
    │       └── indexes/
    │           ├── __init__.py
    │           ├── exact.py
    │           ├── keyword.py
    │           ├── prefix.py
    │           └── range_.py
    │
    └── vapor-idx-rs/                  ← Rust (crates.io)
        ├── Cargo.toml
        └── src/
            ├── lib.rs
            ├── types.rs
            ├── instance.rs
            ├── schema.rs
            ├── record_store.rs
            ├── relationship_store.rs
            ├── query_engine.rs
            ├── traversal_engine.rs
            └── indexes/
                ├── mod.rs
                ├── exact.rs
                ├── keyword.rs
                ├── prefix.rs
                └── range.rs
```

---

## Which runtime should I use?

| Environment                        | Runtime      | Install                       |
|------------------------------------|--------------|-------------------------------|
| Claude Code / Node.js skill scripts| Node.js      | `npm install vapor-idx`       |
| Claude web Artifacts (browser ESM) | Browser      | CDN or bundled ESM            |
| Claude computer-use (Python)       | Python 3.10+ | `pip install vapor-idx`       |
| Rust-based skill tooling           | Rust         | `cargo add vapor-idx`         |

---

## Installation

**Node.js / Browser (TypeScript / JavaScript)**
```bash
npm install vapor-idx
```

**Browser ESM (no build step)**
```html
<script type="module">
  import { createVapor } from 'https://esm.sh/vapor-idx';
</script>
```

**Python**
```bash
pip install vapor-idx
```

**Rust**
```toml
[dependencies]
vapor-idx = "0.1"
```

---

## Core concepts

### Schema-first, no defaults

You declare every type, every field, and the index strategy for every field before
storing a single record. There are no auto-detected types, no implicit indexes, no
convenience defaults. The schema is the law of your skill's index.

### Index strategies

| Strategy   | What it indexes              | Supported operations               |
|------------|------------------------------|------------------------------------|
| `none`     | Nothing — stored only        | Retrieval by ID only               |
| `exact`    | Exact value equality         | `eq`, `neq`, `in`, `notIn`         |
| `keyword`  | Tokenised inverted index     | `contains`, free-text `keywords`   |
| `prefix`   | Trie-based prefix matching   | `startsWith`                       |
| `range`    | Sorted numeric values        | `gt`, `lt`, `gte`, `lte`           |

Range indexes correctly handle negative numbers, zero, and positive numbers across
all three language implementations.

### Records are RAM variables

`vapor.store(type, data)` returns a string ID and holds the record in process
memory as a native language value — a JavaScript object, a Python frozen
dataclass, a Rust struct. Nothing touches the filesystem. Records disappear when
the instance is destroyed or the process ends.

### Relationships

Types can declare named relationship kinds between them. Relationships are
directional or bidirectional, with declared cardinality (`one-to-one`,
`one-to-many`, `many-to-many`). They are stored as first-class edges traversable
by the query and traversal engines.

### Traversal

The traversal engine follows relationship edges from a starting node using BFS, up
to a declared depth, with optional filtering applied at each hop. It returns both
the matched records and the full path metadata.

### Snapshots

At any point you can take a snapshot of the entire index state as a plain
serialisable value. Snapshots are RAM-only — serialisation to disk is the caller's
responsibility.

---

## Quick start — TypeScript

```typescript
import { createVapor } from 'vapor-idx';

const vapor = createVapor({
  types: {
    Function: {
      fields: {
        name:       { type: 'string',   index: 'exact'   },
        filePath:   { type: 'string',   index: 'exact'   },
        docstring:  { type: 'string',   index: 'keyword' },
        lineStart:  { type: 'number',   index: 'range'   },
        lineEnd:    { type: 'number',   index: 'range'   },
        isAsync:    { type: 'boolean',  index: 'exact'   },
        tags:       { type: 'string[]', index: 'keyword' },
        visibility: { type: 'string',   index: 'prefix'  },
      },
      relationships: {
        CALLS: {
          targetTypes:  ['Function'],
          directed:     true,
          cardinality:  'many-to-many',
        },
        DEFINED_IN: {
          targetTypes:  ['Module'],
          directed:     true,
          cardinality:  'many-to-one',
        },
      },
    },
    Module: {
      fields: {
        path:     { type: 'string', index: 'exact'   },
        language: { type: 'string', index: 'exact'   },
        summary:  { type: 'string', index: 'keyword' },
      },
    },
  },
});

const modId = vapor.store('Module', {
  path:     'src/parser.ts',
  language: 'typescript',
  summary:  'Parses AST nodes and extracts function signatures',
});

const fnId = vapor.store('Function', {
  name:       'parseFunction',
  filePath:   'src/parser.ts',
  docstring:  'Extracts function name, parameters, and return type from an AST node',
  lineStart:  42,
  lineEnd:    78,
  isAsync:    false,
  tags:       ['parsing', 'ast', 'extraction'],
  visibility: 'public',
});

vapor.relate(fnId, 'DEFINED_IN', modId);

const asyncFns = vapor.query({
  type:  'Function',
  where: { field: 'isAsync', op: 'eq', value: true },
});

const parsingFns = vapor.query({
  type:     'Function',
  keywords: 'ast extraction',
});

const largeFns = vapor.query({
  type:    'Function',
  where:   { field: 'lineEnd', op: 'gt', value: 100 },
  orderBy: { field: 'lineStart', direction: 'asc' },
  limit:   10,
});

const callChain = vapor.traverse({
  from:         fnId,
  relationship: 'CALLS',
  direction:    'outgoing',
  depth:        3,
});

console.log(vapor.stats());

const snap = vapor.snapshot();
const fork = vapor.restore(snap);

vapor.destroy();
```

---

## Quick start — Python

```python
from vapor_idx import create_vapor, QueryOptions, FieldFilter, TraversalOptions, PathOptions

vapor = create_vapor({
    "types": {
        "Function": {
            "fields": {
                "name":       {"type": "string",   "index": "exact"},
                "file_path":  {"type": "string",   "index": "exact"},
                "docstring":  {"type": "string",   "index": "keyword"},
                "line_start": {"type": "number",   "index": "range"},
                "is_async":   {"type": "boolean",  "index": "exact"},
                "tags":       {"type": "string[]", "index": "keyword"},
            },
            "relationships": {
                "CALLS": {
                    "targetTypes": ["Function"],
                    "directed":    True,
                    "cardinality": "many-to-many",
                },
            },
        },
    },
})

fn_id = vapor.store("Function", {
    "name":       "parse_function",
    "file_path":  "parser.py",
    "docstring":  "Extracts function signatures from AST nodes",
    "line_start": 42,
    "is_async":   False,
    "tags":       ["parsing", "ast"],
})

results = vapor.query(QueryOptions(
    type="Function",
    keywords="ast extraction",
))

print(vapor.stats())
vapor.destroy()
```

---

## Quick start — Rust

```rust
use vapor_idx::{
    create_vapor, VaporSchema, TypeDefinition, FieldDefinition,
    FieldType, IndexStrategy, QueryOptions, FieldFilter, FilterOp,
};
use std::collections::HashMap;

fn main() {
    // VaporSchema is a plain struct — initialise with struct literal
    let schema = VaporSchema {
        types: HashMap::from([
            ("Function".to_string(), TypeDefinition {
                fields: HashMap::from([
                    ("name".to_string(), FieldDefinition {
                        field_type: FieldType::String,
                        index:      IndexStrategy::Exact,
                        required:   true,
                    }),
                    ("line_start".to_string(), FieldDefinition {
                        field_type: FieldType::Number,
                        index:      IndexStrategy::Range,
                        required:   true,
                    }),
                ]),
                relationships: HashMap::new(),
            }),
        ]),
    };

    // create_vapor returns VaporResult<VaporInstance>
    let mut vapor = create_vapor(schema).expect("valid schema");

    let fn_id = vapor.store(
        "Function",
        serde_json::json!({ "name": "parse_function", "line_start": 42 }),
    ).unwrap();

    let results = vapor.query(&QueryOptions {
        type_filter: Some(vec!["Function".to_string()]),
        where_filters: vec![FieldFilter {
            field: "line_start".to_string(),
            op:    FilterOp::Gt,
            value: serde_json::json!(10),
        }],
        ..Default::default()
    }).unwrap();

    println!("Found {} results", results.total);
    vapor.destroy();
}
```

---

## API reference

### `createVapor(schema)` / `create_vapor(schema)` → `VaporInstance`

Creates a new isolated index bound to the provided schema. Validates the schema
at construction time. No types, fields, or relationships exist until declared.
In Rust, returns `VaporResult<VaporInstance>`.

---

### `vapor.store(type, data)` → `string` (record ID)

Stores a record of the given type **in RAM** as a native language object. Returns
a generated ID (`vpr_<timestamp>_<counter>`). Throws if the type is not declared
or required fields are missing.

---

### `vapor.get(id)` → `VaporRecord | null`

Returns a single record by ID. O(1) lookup from a HashMap/dict/Map.

---

### `vapor.update(id, partial)` → `void`

Updates fields on an existing record. Re-indexes all changed fields.

---

### `vapor.delete(id)` → `void`

Removes a record and all its relationship edges from the index.

---

### `vapor.relate(sourceId, relType, targetId, metadata?)` → `string` (edge ID)

Creates a relationship edge. Validates both records exist, the relationship type
is declared, and the target type is permitted. Bidirectional relationships create
edges in both directions automatically.

---

### `vapor.unrelate(edgeId)` → `void`

Removes a relationship edge by edge ID.

---

### `vapor.query(options)` → `QueryResult`

Executes a query against the index.

```typescript
interface QueryOptions {
  type?:     string | string[];           // restrict by type
  where?:    FieldFilter | FieldFilter[]; // field-level predicates
  keywords?: string | string[];           // free-text across keyword fields
  logic?:    'AND' | 'OR';               // how multiple where clauses combine
  limit?:    number;
  offset?:   number;
  orderBy?:  { field: string; direction: 'asc' | 'desc' };
}
```

Operators by index strategy:

| Strategy  | Valid operators                    |
|-----------|------------------------------------|
| `exact`   | `eq`, `neq`, `in`, `notIn`         |
| `keyword` | `contains`                         |
| `prefix`  | `startsWith`                       |
| `range`   | `gt`, `lt`, `gte`, `lte`           |
| `none`    | not directly queryable             |

---

### `vapor.traverse(options)` → `TraversalResult`

BFS traversal following relationship edges from a starting node.

```typescript
interface TraversalOptions {
  from:         string;
  relationship: string;
  direction?:   'outgoing' | 'incoming' | 'both';
  depth?:       number;
  filter?:      Omit<QueryOptions, 'limit' | 'offset' | 'orderBy'>;
}
```

---

### `vapor.findPath(options)` → `string[] | null`

Returns the shortest ID path between two records, or `null` if no path exists
within `maxDepth`.

---

### `vapor.getRelationships(id, type?, direction?)` → `VaporRelationship[]`

Returns all relationship edges for a given record, optionally filtered.

---

### `vapor.stats()` → `VaporStats`

Returns record counts by type, relationship counts by type, index sizes, and a
rough memory estimate in bytes.

---

### `vapor.snapshot()` → `VaporSnapshot`

Captures the full current state as a plain serialisable object. RAM only.

---

### `vapor.restore(snapshot)` → `VaporInstance`

Returns a new `VaporInstance` hydrated from a snapshot. Schema hashes must match.

---

### `vapor.destroy()` → `void`

Releases all references. Safe to call multiple times.

---

## Skill authoring guidelines

**Declare only what you need.** Memory scales with index type and cardinality.

**For spatial/pixel/vertex data, always use `range` on coordinate fields.** Range
queries (`gt`/`lt`/`gte`/`lte`) are the foundation of spatial understanding. All
three implementations correctly handle negative coordinates and zero.

**Let Claude traverse — Claude is the zero-shot intelligence.** vapor-idx provides
structure; Claude provides semantic reasoning. A skill does not need an external
ML model to understand modality relationships — it indexes the data and lets Claude
traverse and interpret.

**Destroy explicitly.** Call `vapor.destroy()` when done to release memory
promptly rather than waiting for scope exit or GC.

**Use snapshots for branching logic.** Snapshot before exploring an interpretation;
restore the losing branch.

**Do not pass instances across skill boundaries.** Each skill creates its own
instance from its own schema.

---

## Philosophy

vapor-idx is deliberately minimal. It does not implement:

- Embeddings or vector similarity
- ML-guided ranking or confidence scoring
- Persistence to any storage medium
- Cross-skill or cross-session state
- Schema migrations
- Query optimiser hints
- Transactions or rollback

These are intentional exclusions. The moment you need any of them, you have
outgrown an in-skill ephemeral index and you need a real database.

What vapor-idx does is provide a clean, consistent, language-agnostic API for
declaring structured knowledge in RAM and querying it with zero dependencies.
Combined with Claude's own reasoning, this is sufficient to analyse, reconstruct,
and transform any modality that can be expressed as typed records with connections.

---

## Package publishing

| Package      | Registry  | Import                                    |
|--------------|-----------|-------------------------------------------|
| `vapor-idx`  | npm       | `import { createVapor } from 'vapor-idx'` |
| `vapor-idx`  | PyPI      | `from vapor_idx import create_vapor`      |
| `vapor-idx`  | crates.io | `use vapor_idx::create_vapor;`            |

All three packages are versioned in lockstep.

---

## Documentation

- [DOCS/CONCEPT.md](DOCS/CONCEPT.md) — core philosophy and the RAM-first indexing model
- [DOCS/API_REFERENCE.md](DOCS/API_REFERENCE.md) — complete type definitions and API
- [DOCS/PIXEL_INDEXING.md](DOCS/PIXEL_INDEXING.md) — image and pixel indexing guide
- [DOCS/3D_INDEXING.md](DOCS/3D_INDEXING.md) — 3D mesh and vertex indexing guide
- [DOCS/CROSS_MODAL.md](DOCS/CROSS_MODAL.md) — cross-modal reconstruction guide
- [TESTING.md](TESTING.md) — deploy, test, and validate all three packages with skills

## Skills

- [skills/pixel-analyzer/SKILL.md](skills/pixel-analyzer/SKILL.md)
- [skills/mesh-analyzer/SKILL.md](skills/mesh-analyzer/SKILL.md)
- [skills/design-indexer/SKILL.md](skills/design-indexer/SKILL.md)
- [skills/cross-modal-reconstructor/SKILL.md](skills/cross-modal-reconstructor/SKILL.md)

---

## License

MIT
