npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@happyvertical/smrt-prompts

v0.60.14

Published

Typed prompt registry, override resolution, and tenant-aware prompt settings for SMRT

Readme

@happyvertical/smrt-prompts

Typed prompt definitions, tenant-aware prompt overrides, and runtime prompt resolution for s-m-r-t applications.

Installation

pnpm add @happyvertical/smrt-prompts

Quick start

import { definePrompt, resolvePrompt } from '@happyvertical/smrt-prompts';

// 1. Register a prompt's defaults at startup
definePrompt({
  key: 'projects.issue.incorporateFeedback',
  template: 'Rewrite the issue body incorporating this feedback: {feedback}',
  ai: {
    profile: 'default',
    params: { temperature: 0.4 },
  },
});

// 2. Resolve at runtime (auto-uses tenant context from AsyncLocalStorage)
const resolved = await resolvePrompt('projects.issue.incorporateFeedback', {
  vars: { feedback: '...' },
});

// resolved.template — the merged template with overrides applied
// resolved.ai     — the merged AI configuration (profile, model, params)

What this package provides

  • definePrompt() — code-first prompt registration in a global process registry
  • resolvePrompt() — layered resolution: code default → config override → stored app override → stored tenant override → runtime override
  • PromptOverride — model for app-level and tenant-level prompt settings, stored in _smrt_prompt_overrides
  • PromptOverrideService / PromptSettingsService — the authorized write path and the read/write helper for a management screen (see "Management UI" below)
  • Named AI profiles loaded from packages.prompts config — prompts select profile names, profiles resolve to provider/model in config
  • TTL cache keyed by (key, tenantId), invalidated on override save/delete

Stored overrides support partial fields, so applications can override only the template, profile, model, or AI params without forking the rest of a prompt.

Generated surfaces

PromptOverride ships with generated REST routes, MCP tools, and CLI commands closed (api: false, cli: false, mcp: false). Override rows are authorization state for every scope, so write them only from server code through PromptOverrideService, which asks an authorizer you bind to the current caller before every write and fails closed:

const service = new PromptOverrideService(overrides, (request) =>
  request.scopeType === 'tenant' && request.scopeId === session.tenantId &&
  session.permissions.has('prompts.manage'),
);
await service.setTemplateOverride(key, 'tenant', session.tenantId, 'New text.');

Management UI

The package ships the settings screen so consuming apps do not each rebuild it: PromptSettingsService (root export) produces the rows, and PromptSettingsPanel (the ./svelte subpath) renders them. The root export stays Svelte-free, so a server-only consumer is unaffected; svelte is an optional peer dependency.

Consumer recipe

1. Register the prompt in code (see Quick start above), including editable if you want tenants or the app-wide default to be overridable:

definePrompt({
  key: 'projects.issue.incorporateFeedback',
  template: 'Rewrite the issue body incorporating this feedback: {feedback}',
  description: 'Rewrites an issue body from reviewer feedback.',
  editable: { template: true },
});

2. Load the rows in your server load. The load is where your permission check lives — the package makes no authorization decision:

// src/routes/settings/prompts/+page.server.ts
export async function load(event) {
  const auth = requirePermission(event, APP_PERMISSIONS.tenantAdmin);
  const settings = await PromptSettingsService.create({ db });
  return {
    tenantId: auth.tenantId,
    prompts: await settings.listPromptSettings({ tenantId: auth.tenantId }),
  };
}

listPromptSettings() returns one PromptSettingsRow per registered key — the effective template for the requested tenant, the raw override text at each scope (appTemplate/tenantTemplate), what reverting each scope produces (appDefaultTemplate/inheritedTemplate), and supplyingLevel. The row is structurally assignable to the panel's PromptSettingsView, so no mapping step is needed.

3. Render the panel:

<script lang="ts">
  import { PromptSettingsPanel } from '@happyvertical/smrt-prompts/svelte';
  let { data, form } = $props();
</script>

<PromptSettingsPanel
  prompts={data.prompts}
  contextKey={data.tenantId}
  formAction="?/savePrompt"
  message={form?.message}
  error={form?.error}
/>

The panel is presentational: it takes rows and callbacks, fetches nothing and authorizes nothing. Each row renders the key, description, effective text, and which level supplied it, plus a checkbox + text editor for the tenant scope (and, when showApp/appEditable are set, the app scope). Unchecking the box reverts that scope — the preview text next to it shows exactly what reverting produces. Supply onSave instead of formAction to drive it from a client handler.

4. Wire the write with your own permission check. setPromptOverride() refuses any key without a definition and any field the definition's editable config disallows, then delegates to PromptOverrideService, which asks your authorizer and fails closed:

export const actions = {
  savePrompt: async (event) => {
    const auth = requirePermission(event, APP_PERMISSIONS.tenantAdmin);
    const form = await event.request.formData();
    const settings = await PromptSettingsService.create(
      { db },
      {
        // Your decision, bound to this caller. Returning anything but `true` denies.
        authorize: (request) =>
          request.scopeType === 'tenant' && request.scopeId === auth.tenantId,
      },
    );

    try {
      await settings.setTenantPromptOverride(
        String(form.get('key') ?? ''),
        auth.tenantId,
        form.has('tenantOverride') ? String(form.get('tenantTemplate') ?? '') : null,
      );
    } catch (error) {
      if (error instanceof UnknownPromptKeyError) return fail(400, { error: 'Unknown prompt.' });
      if (error instanceof PromptFieldNotEditableError) return fail(400, { error: 'Not editable.' });
      if (error instanceof InvalidPromptScopeError) return fail(400, { error: 'Invalid scope.' });
      if (error instanceof PromptOverrideAuthorizationError) return fail(403, { error: 'Not permitted.' });
      throw error;
    }
    return { message: 'Prompt overrides saved.' };
  },
};

Constructing the service without an authorize option makes it read-only: every write then fails closed with PromptOverrideAuthorizationError. That is the right shape for a load function.

What the consumer still owns

  • the route, its navigation entry, and the page shell;
  • the permission check on both the load and the action;
  • the authorize callback (this package never decides who may write);
  • resolving the session's tenantId — a tenant id from the URL or a form field is a selector, not authorization;
  • registering prompts with definePrompt() at startup.

Prompt keys are persisted identifiers

Overrides are stored keyed by the exact key string and resolved by that string at runtime. Renaming a key orphans every existing override, silently reverting all tenants to the registry default — the change looks harmless in a diff and shows up as prompt text mysteriously resetting. Treat a key as immutable once shipped. A rename needs a data migration that rewrites _smrt_prompt_overrides.key, not an edit.

Documentation