Crux
API Reference@use-crux/core

Index

Design-plane contract for discovered Crux definitions, relations, source locations, diagnostics, and lint findings.

import type { ProjectIndexSnapshot } from "@use-crux/core/project-index";
import { serializeProjectIndex } from "@use-crux/core/project-index/serializers";

The Project Index describes what exists in a local Crux project. It is separate from runtime observability: index definitions are prompts, contexts, tools, agents, flows, flow steps, compositions, RAG resources, memory, memory blocks, blackboards, workspaces, safety definitions, Evals, and other design-time resources; spans describe what happened during a run.

@use-crux/core owns the TypeScript contract. The Go devtools backend mirrors the contract manually, builds the service-owned read model, and exposes it to web devtools and the TUI.

Deployment manifest contract

@use-crux/core/project-index exports the portable CruxDeploymentIdentity, ProjectIndexDeploymentManifestV1, and related Zod schemas. A deployment identity keeps three concerns separate: a stable, explicit projectId; an optional content-addressed manifestId; and an optional opaque deploymentId supplied by application or host code. Core does not read environment variables to populate them.

The manifest content is a privacy-safe projection of definitions, source refs, and relations. It may contain canonical repo-relative source locations and fingerprints, but never prompt or source bodies, descriptions, arbitrary metadata, absolute checkout paths, timestamps, or credentials. Its pim_<sha256> identity covers only normalized content; project identity and compiler provenance stay outside the digest. Manifest projection and hashing remain build-time Indexer concerns and are not reachable from this portable runtime subpath.

Build a manifest without starting crux dev, then import its exact immutable content into the local historical Catalog store:

crux manifest --project-id checkout --out .crux/project-index.manifest.json
crux catalog import .crux/project-index.manifest.json

crux manifest uses the same one-shot compiler as crux check. It requires an explicit logical project ID, waits for semantic indexing, and atomically replaces the output only after the Go boundary has recomputed and verified the TypeScript compiler's manifest ID. catalog import repeats that verification and stores the artifact by (projectId, manifestId); importing the same bytes again is idempotent, while different content for an existing tuple is rejected.

Inspect the current Catalog through the running local server:

crux catalog list
crux catalog show agent:writer
crux catalog status
crux catalog explain agent:writer

The stable explanation projection reports definition evidence by AST, semantic, runtime, or Eval phase; producer/fidelity/source provenance; the canonical first-party or resolved extension extractors that actually contributed when that attribution is known; resolved and compiler-reported unresolved relations; diagnostics; lints; and known cache/backend/fallback state. It strips opaque definition/relation metadata and normalizes source locations to repository-relative paths. The v1 command does not select historical deployments; exact historical resolution remains part of run detail and never substitutes the current Catalog.

Snapshot Shape

interface ProjectIndexSnapshot {
  schemaVersion: 1;
  project?: ProjectIdentity;
  lint?: CruxLintConfig;
  indexedAt: string;
  indexing?: ProjectIndexingStatus;
  sourceGraph?: ProjectIndexSourceGraph;
  prompts: PromptMeta[];
  contexts: ContextMeta[];
  tools: ToolMeta[];
  definitions: ProjectDefinition[];
  relations: ProjectRelation[];
  diagnostics: IndexDiagnostic[];
  lintFindings: IndexLintFinding[];
  ruleDescriptors: IndexRuleDescriptor[];
  sources: IndexSourceFile[];
}

prompts, contexts, and tools are runtime snapshot summaries. The canonical authored read model is definitions plus relations, diagnostics, lintFindings, ruleDescriptors, and sources; web devtools and the TUI should render authored primitives from definitions.

indexing is the backend-owned progress/readiness status for the index snapshot. It tells clients whether the index is cold, cached, refreshing, ready, or degraded, and includes separate phase state for the fast AST pass, the semantic enrichment pass, and cache. Clients should render this field directly and should not infer indexing readiness from diagnostics or missing source refs.

sourceGraph is indexer-owned provenance for sources dependency/dependent rows. It records the graph schema version, producer, and capabilities such as source dependencies, reverse dependents, definition ownership, and diagnostic ownership. Incremental planners and future partial index executors should trust source closures only when this marker is present with the required capabilities; older snapshots without it should fall back to full reindex.

Resolved Project Model

ResolvedProjectModel is the config-inspection read model exported from @use-crux/core/project-index. It is not a runtime setup API and it is not a central registry requirement. Local tooling uses it to explain what Crux inferred or explicitly received while keeping policy/config separate from authored source visibility.

import type {
  ProjectModelProvenance,
  ResolvedProjectModel,
} from "@use-crux/core/project-index";

The model contains the selected root, optional package name, config files, source roots, ignored paths, discovered definitions, source-visible relations, Eval discovery settings, and diagnostics. Fields that can be inferred or overridden use ProjectModelField<T> so clients can show whether a value came from source, runtime evidence, filesystem convention, config, or CLI flags.

Source-discovered prompt and context bundles are represented directly on definitions. For example, createPrompts({ support: { answer } }) gives the answer prompt a provenance-bearing path.value of ["support", "answer"]; createContexts(...) does the same for context leaves. Prompt/context use bindings that Crux can prove from source appear in relations, such as prompt.uses_context, with inferred visibility and source provenance. Users do not need to repeat these prompts, contexts, or relationships in crux.config.ts for local inspection.

Tools and skill registries follow the same source-first rule. Exported tool(...) definitions, registry(...) values, and skill.fromRegistry(registryValue, path) calls appear as inferred definitions without config registration. Registry-backed skills link to their registry value with skill.uses_registry; bundled registries such as skillsSh are represented as built-in registry definitions and are not fetched during discovery.

@use-crux/indexer produces this model with resolveProjectModel(...), and @use-crux/local exposes it through crux config inspect for human or --json output.

type ProjectModelProvenance =
  | { kind: "source"; file: string; exportName?: string }
  | { kind: "runtime"; traceId?: string; attribute: string }
  | { kind: "filesystem"; path: string; convention: string }
  | { kind: "config"; path: string; key: string }
  | { kind: "cli"; flag: string };

Diagnostics use stable ProjectModelDiagnosticCode values, such as project_model.source_only_discovery, project_model.missing_stable_id, project_model.dynamic_tool_map_unproven, project_model.prompt_test_dependency_unproven, and project_model.config_import_failed, plus branded diagnostic ids. Definition and relation ids are branded separately. The brands disappear in JSON, but TypeScript callers must construct ids at a boundary with createProjectModelDefinitionId(), createProjectModelRelationId(), or createProjectModelDiagnosticId() instead of passing arbitrary strings through the public contract.

Clients should render Project Model diagnostics as project facts with small suggested fixes. A missing config is allowed: the source-only diagnostic explains the discovery mode, not that prompts, contexts, or tools must be registered in config. Selected source-shape facts, such as missing stable ids, runtime-dependent tool maps, or tested prompts whose context dependencies are only partially proven, are projected into Project Model diagnostics with source provenance.

Producers

The index contract is produced by two packages:

ProducerResponsibility
@use-crux/indexerDiscovers source-authored definitions, relations, source refs, diagnostics, and index lint findings.
@use-crux/localPersists and serves the index read model, joins Eval and runtime evidence, and broadcasts updates to UIs.

Runtime snapshots emitted by withDevtools() can enrich prompt, context, and tool metadata, but source files remain authoritative for authored definitions. Private .crux/evals records are execution evidence, not source truth. See @use-crux/indexer for the indexing package and @use-crux/local for CLI/API access.

The indexing pipeline is designed as fast source truth plus background enrichment. During crux dev, the Go service asks the worker for a bounded static/AST pass first so it can publish useful Project Index data quickly. That first patch may carry an index.source_only diagnostic as a status marker. When semantic enrichment succeeds, the service applies the semantic patch and clears that marker; when semantic enrichment is unavailable or degraded, the marker remains so clients can explain the current fidelity honestly.

The semantic pass can enrich proven aliases, barrels, imported symbols, schema refs, callbacks, and primitive relations without blocking the first snapshot. The Go service owns stale-while-refresh cache loading, patch merging, final read-model state, and realtime publication; TypeScript workers emit neutral index facts/patches.

Semantic Backend Selection

Semantic Project Index behavior is backend-neutral. The default backend uses the JavaScript TypeScript compiler API. Opt in to the experimental native semantic backend with experimental.indexer.native: true or experimental.indexer.native: { engine: 'tsgo', tsserverPath?: string } in crux.config.ts.

When native semantic indexing is selected, TypeScript-Go owns semantic project setup, checker calls, declaration lookup, and AST traversal. Native direct projectors accelerate source shapes that Crux can prove exactly; unsupported shapes continue through the tsgo-backed shared analyzer path instead of falling back to the JavaScript TypeScript semantic backend. Both semantic backends emit the same Project Index facts, relations, diagnostics, lint findings, and source refs through the same read model.

experimental.indexer.nativeAst: true or { frontend: 'oxc' } is a separate static AST/source frontend experiment. It can accelerate the first source pass without selecting the native semantic backend.

Native AST beta runs use Rust/Oxc for first-party Static Index work, but the Project Index contract does not change. TypeScript extension extractors and rules remain compatible through the trusted Node host, and their output must finalize into the same definitions, relations, diagnostics, lint findings, rule descriptors, sources, and source graph rows as the TypeScript Static Index baseline. Clients should treat fallback or Node-start diagnostics as indexing status: they explain why a beta run used TypeScript extension compatibility or degraded setup, not a different Project Index schema.

Full-Fidelity Definitions

Export definitions from discovered modules for full source visibility. Use crux.config.* for explicit policy, trust, ownership, or overrides rather than duplicating every primitive registration. Inline or dynamically generated definitions may still appear as partial entries, but Crux surfaces a diagnostic when it cannot prove IDs, relations, cases, or source locations.

Static relation extraction currently covers common design-time links such as prompt-to-context, agent-to-prompt, agent-to-tool, skill-to-registry, agent handoffs, flow-to-step, statically visible agent/tool/flow-step memory access, RAG pipeline-to-retriever, memory-to-memory-block, and composition-to-agent/flow. Workspaces carry source, namespace, mounts, tool exposure, and asset-storage hints when those values are literal. Source file entries include produced definition ids plus dependency/dependent file edges when known, and sourceGraph records whether those source rows are complete enough for source-closure planning. The Go service owns these definitions and relations so web devtools and the TUI render the same graph.

Routing definitions and catalog facts

The Project Index represents each authored routing policy as a parent plus first-class children:

ParentChildContainment relation
routing.routerrouting.router.routerouter.includes_route
routing.splitrouting.split.routesplit.includes_route
routing.retryrouting.retry.targetretry.uses_target
routing.cascaderouting.cascade.tiercascade.includes_tier
routing.fallbackrouting.fallback.optionfallback.includes_option

Stable authored id values are the join key across the system: runtime spans, routing receipts, decision reports, and these indexed source definitions all correlate through them.

Children remain searchable, lintable, directly inspectable graph definitions. Their metadata.indexPresentation hint tells catalog clients when to fold them under the parent and supplies role/order data; it does not remove them from definitions or relations. Target relations such as router.route.uses_prompt, split.route.uses_prompt, retry.target.uses_prompt, cascade.tier.uses_fallback, and fallback.option.uses_agent connect those children to index-visible prompts, agents, and nested routing policies.

Semantic evidence for a router classify callback or split seed callback annotated with RouteArgs can add parent facts routingContextType and routingContextRequired. Context-free callbacks omit them, and the static syntax pass does not invent annotation-derived types. Router and split route call-profile objects can add a JSON-safe child profile containing literal settings such as temperature, maxTokens, or weight; model remains a target relation rather than a profile setting, and raw-model routes omit profile.

Core owns the JSON-safe Project Index contract. The Indexer emits definitions, relations, facts, source evidence, and metadata.indexPresentation hints into that contract. The Go service preserves and serves the merged read model, while web devtools and the TUI render it without reparsing source or reconstructing routing structure client-side.

Definitions can include supporting source references in sourceRefs. source remains the primary authored call site, while sourceRefs point at separate schema declarations, callback functions, handlers, prompt builders, system prompt constants, validators, policies, config bindings, helper functions, reusable text fragments, and nested schema fragments that Crux can prove from source. The resolver supports same-file identifiers and direct project imports for tool schemas, nested schema identifiers, agent/prompt/context callbacks, prompt use context targets, local context-array constants, prompt/context system constants, direct identifiers and conservative object-property paths injected into static system template strings, safety/scorer callbacks, tool callbacks, Convex Agent config/callback bindings, flow-step callbacks, Convex Agent tool-map contributors, and simple callback-factory arguments. For example, createTool({ parameters: writerSchema, execute: runWriter }) can expose both metadata.inputSchema and refs for writerSchema, nested schemas such as SearchStepSchema, runWriter, and one statically visible helper called by runWriter; prompt({ system: PLANNER_SYSTEM }) can expose the PLANNER_SYSTEM declaration; a context system template that injects formatting.SUPPORTED_ELEMENTS can expose that property declaration with metadata.injected: true and metadata.fragment: true; new Agent(..., { tools, contextHandler, usageHandler }), direct convexAgent({...}), and profile-created crux.convexAgent({...}) can expose prompt, tools, contextHandler, usageHandler, and prepare bindings, tool-map spreads/properties, factory arguments passed into handler or prepare builders, and one helper level.

interface ProjectSourceRef {
  id: string;
  role:
    | "schema"
    | "callback"
    | "handler"
    | "execute"
    | "prompt"
    | "system"
    | "resolver"
    | "validator"
    | "policy"
    | "config"
    | "helper";
  property?: string;
  symbol?: string;
  source: SourceLocation;
  snippet?: SourceSnippet;
  fidelity: "resolved" | "partial";
  description?: string;
  metadata?: {
    schemaKind?: "zod" | "convex-validator" | "json-schema";
    parsedSchema?: boolean;
    nested?: boolean;
    injected?: boolean;
    fragment?: boolean;
    factoryArg?: boolean;
    argumentIndex?: number;
    argumentName?: string;
    toolMapContributor?: "spread" | "property";
    referencedDefinitionIds?: string[];
    dataAccess?: boolean;
  };
}

Clients should render sourceRefs as supporting source locations and should not use them as runtime graph edges. Authored graph links still come from relations; parsed schemas and primitive intelligence still live on metadata. Source file entries can include dependency/dependent edges derived from source refs so file maps can show "this prompt depends on that schema/fragment file" without turning those source dependencies into Crux runtime relations.

Definitions include metadata.runtimeJoin hints where Crux can derive stable runtime identifiers. The object is typed as ProjectRuntimeJoin from @use-crux/core/project-index. These hints are for the Go read models to join spans/resources back to authored definitions without UI-side inference.

runtimeJoin separates authored identity from runtime correlation. definitionId is always the index definition id. spanAttributes contains only stable attributes that runtime spans actually emit. Runtime-only correlation fields stay separate: flowId identifies one executing flow instance and stepId identifies one executing flow-step instance, so authored flow definitions join flow.run spans by primitive plus span name, while authored flow-step definitions join flow.step spans by primitive plus stepLabel/span name. Memory blocks join memory spans through sourceDefinitionId, blockDefinitionId, runtime memoryId, and blockId. Blackboards use memory-shaped spans with memoryType: "blackboard"; clients should not expect a separate blackboardId span attribute.

Definitions can also include metadata.intelligence when Crux has source-backed primitive structure. This is the backend-owned design-plane explanation of how a primitive is shaped, not a runtime trace. Normal flow() definitions expose immediate ordered control metadata, including visible step labels and suspension points. Convex flow({ name, args, handler }) definitions additionally expose validator-derived argsSchema, visible suspension points from flow.waitFor() / flow.suspend(), and flow.step.waits_for_signal relations. Agent definitions expose prompt/tool/handoff dependency intelligence when literal and can also expose visible memory, blackboard, and workspace reads/writes in metadata.intelligence.data. Tool definitions and flow step definitions expose the same visible data access shape when authored code calls store helpers, including workspace operations such as grep, watch, history, diff, rename, move, copy, undo, and finalize. These entries include the source targetVariable, literal key/path when available, and source location; normalized definition links come from relations such as agent.reads_memory, tool.writes_blackboard, flow.step.reads_memory, flow.step.writes_blackboard, and flow.step.writes_workspace. Literal parallel(), pipeline(), consensus(), and swarm() calls expose their visible branches, stages, participants, coordinators, judges/scorers, and shared state through definitions and relations such as composition.parallel.branch, composition.pipeline.stage, parallel.includes_branch, pipeline.includes_stage, pipeline.stage.uses_prompt, pipeline.stage.uses_tool, consensus.includes_agent, consensus.uses_scorer, swarm.includes_agent, swarm.coordinated_by, swarm.uses_blackboard, and swarm.uses_memory. Retrieval recipes can expose rag.recipe.step definitions plus rag.recipe.includes_step, rag.recipe.uses_retriever, rag.recipe.step.uses_retriever, and rag.recipe.step.uses_scorer relations when step arrays use literal identifiers. Workspaces can expose literal tools and mount paths through workspace.exposes_tool and workspace.mounts_path; constraints and guardrails can expose literal target coverage through constraint.applies_to and guardrail.applies_to; evals can expose coverage through eval.covers_definition.

The intelligence contract is additive. If Crux cannot prove a fact from source or runtime data, the field is omitted rather than filled with an empty placeholder. Clients should render definitions, relations, and metadata.intelligence directly and should not reconstruct flow, pipeline, parallel, consensus, or swarm structure by parsing source snippets.

Safety definitions include guardrail, constraint, and toolPolicy rows. Static facts preserve stable policyId, boundary targets such as model.output.text, model.output.media, or tool.call, ordered boundary tuples, built-in strategy helper kinds and statically visible configuration such as guardrail.media({ action: 'strip' }), and safe tool-policy match metadata. Completed media operations retain guardrail.applies_to / constraint.applies_to relations and a source reference for authored safety options so Catalog can present both sides of the attachment without evaluating runtime configuration. The safety lint set flags duplicate policy ids when the index has reliable evidence.

When a candidate source file is import-safe, the indexer imports it in CRUX_INDEX=1 mode and upgrades exported rich primitives to resolved definitions. Today that covers agents, flows, RAG retrievers and pipelines, memory, blackboards, constraints, guardrails, tool policies, and scorers. Static flow.step definitions remain source-derived because flow handles do not expose their internal handler graph at runtime.

Composition index entries are source-derived call sites for the immediate APIs: parallel({ id, agents }), pipeline({ id, steps }), consensus({ id, agents }), and swarm({ id, agents }). Each composition's authored id is its canonical Project Index identity, the same contract agent()/flow()/memory()/guardrail()/constraint()/blackboard() already use. Adapter factory helpers such as createParallel() are not indexed as user-authored composition definitions.

Prompt, context, and tool schemas are exposed on ProjectDefinition.metadata when Crux can resolve or statically project them:

  • prompt definitions: metadata.inputSchema, metadata.outputSchema, metadata.hasOutput, and metadata.settings
  • context definitions: metadata.inputSchema, metadata.priority, metadata.isStatic, and metadata.usedBy
  • tool definitions: metadata.inputSchema

Clients should render these schema fields directly from the definition detail. Partial definitions can still include best-effort schemas when the source uses common inline Zod expressions such as z.object, z.array, z.enum, z.string, z.number, z.boolean, optional, describe, default, min, and max. Tool schemas passed by same-file variable or direct project import are also resolved when the underlying Zod/Convex-validator expression is statically visible; the calling file records a dependency on the imported schema file. Unresolved dynamic schemas are omitted rather than guessed.

Eval source coverage

The Project Index exposes source definitions and coverage relations needed to answer which production definitions an Eval protects. Eval discovery and the Go Eval services remain authoritative for Cases, Eval runs, Baselines, reuse, and Review; clients must not reconstruct those read models by walking compiler definitions.

Generated deployed registries use canonical project-relative source paths and source fingerprints. Runtime evidence can link back to those exact definitions without writing run state into compiler output.

Lint Findings

lintFindings are backend-owned authored-graph findings. They are intentionally separate from diagnostics: diagnostics describe indexer health, import failures, parse failures, or index fidelity issues; lint findings describe actionable design observations about the Crux graph the indexer successfully understood.

ruleDescriptors is the companion rule metadata list. It describes available built-in and extension-provided index lint rules even when none of those rules fired a finding in the current snapshot. Clients should use it for rule docs, suppression affordances, fix affordances, option schemas, and rule filtering instead of hard-coding rule knowledge.

The full lint finding, profile, rule metadata, and suppression contract is documented in Index Lint. Rule-specific reference pages live under Index Lints.

Clients should render lintFindings directly and should not recompute lint rules by walking relations client-side. Missing lint findings means "no backend finding for the current index", not that the UI should infer its own warnings.

Prompt/context/injectable assembly can also affect contracts. When the indexer can resolve an injection path, metadata.intelligence.contract.expandedInputSchema describes the effective input shape and inputContributions explains which injected definition contributed each field. Injection lint rules use that same backend-owned projection for findings such as hidden required input, conflicting injected fields, conditional required input, dynamic injection dependencies, and dynamic injected tool surfaces.

Runtime-observed injection data stays outside ProjectIndexSnapshot. The local devtools API exposes GET /api/project/index/observed-injection?limit=250 for trace-derived evidence such as observed source ids, included/excluded/dropped contribution counts, branch labels, injected tool names, prompt ids, compact run refs, and redacted prompt input-key summaries. The endpoint also adds soft comparison metadata such as indexMatch, toolIndex, and top-level drift entries when runtime observed sources or tools that are not present or not predicted by the current authored index. Clients can compose this endpoint with GET /api/project/index to show possible-versus-observed injection and runtime input validation, but should keep the distinction visible: unobserved runtime data does not mean a source-authored branch is impossible, observed input summaries contain key names rather than values, and observed runtime data should not mutate the authored index contract.

Serializers

serializeProjectIndex() converts known runtime prompt/context/tool snapshots into the Project Index shape. It is mainly used by the devtools integration and custom advanced integrations.

const snapshot = serializeProjectIndex({
  prompts,
  contexts,
  tools,
  project: { root: process.cwd(), name: "my-app" },
});

Clients should read the Go service through GET /api/project/index or GET /api/index rather than importing user source or running serializers themselves.

On this page