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

speckeeper

v0.18.3

Published

TypeScript-first specification validation framework with external SSOT integration

Readme

speckeeper

npm version License: MIT Node.js

TypeScript-first specification validation framework — validate design consistency and external SSOT integrity with full traceability.

Why speckeeper?

Requirements and design documents often drift from implementation. speckeeper treats specifications as code — type-safe, version-controlled, and continuously validated against your actual artifacts (tests, OpenAPI, DDL, IaC).

speckeeper.config.ts (sources)
    │
    ├─► Global Source Scan  → Find spec IDs in OpenAPI / DDL / annotations
    │         │
    │         ▼
    │    MatchMap (specId → matches)
    │         │
    │         ▼
    ├─► Deep Validation     → Model-level structural checks (optional)
    │
design/*.ts
    │
    ├─► speckeeper lint     → Design integrity (IDs, references, phase gates)
    ├─► speckeeper check    → External SSOT validation (global scan + deep validation)
    └─► speckeeper impact   → Change impact analysis with traceability

Features

  • TypeScript as SSOT — Define requirements, architecture, and design in type-safe TypeScript
  • Design validation — Lint rules for ID uniqueness, reference integrity, circular dependencies, and phase gates
  • External SSOT validation — Global scan across OpenAPI, DDL, annotations; optional deep validation per model
  • Traceability — Track relationships across model levels (L0-L3) with impact analysis
  • Scaffold from Mermaid — Generate _models/ skeletons from a mermaid flowchart with class-based artifact resolution
  • Custom models — Extend with domain-specific models (Runbooks, Policies, etc.)
  • Agent-native — Domain-specific semantic reasoning is encapsulated inside the toolchain itself. Higher-level agents do not need to know every design quality heuristic — they invoke speckeeper and consume structured findings
  • CI-ready — Built-in lint, drift detection, and coverage checks

Installation

npm install speckeeper

# Verify installation
npx speckeeper --help

Quick Start

1. Define your metamodel as a Mermaid flowchart

Create a Markdown file (e.g. requirements.md) containing a mermaid flowchart that describes the relationships between your specification entities:

flowchart TB
  subgraph L0[Business]
    UC[Use Cases]
    TERM[Glossary]
  end
  subgraph L1[Requirements]
    FR[Functional Requirements]
    NFR[Non-Functional Requirements]
  end
  subgraph External[External Artifacts]
    API[OpenAPI Spec]
    DDL[Database Schema]
    UT[Unit Tests]
  end

  FR -->|refines| UC
  FR -->|implements| API
  FR -->|verifiedBy| UT
  FR -->|implements| DDL

  class UC,TERM,FR,NFR speckeeper
  class FR,NFR requirement
  class UC usecase
  class TERM term
  class API openapi
  class DDL sqlschema
  class UT test

Key concepts:

  • class ... speckeeper marks nodes as managed by speckeeper
  • Additional class lines assign artifact classes (determines model name/file and node grouping)
  • External node classes (openapi, sqlschema, test) describe the artifact type
  • subgraph determines model level (L0–L3)
  • implements/verifiedBy edges define the relationship semantics

2. Scaffold models

npx speckeeper scaffold --source requirements.md

This generates:

  • design/_models/ — Model classes with base schema and lint rules
  • design/*.ts — Spec data files using defineSpecs()
  • design/index.ts — Entry point via mergeSpecs()

See Scaffold Mermaid Specification for the full input format.

3. Fill in your specifications

Edit spec data files in design/ to add your actual specification data. Each file uses defineSpecs() to pair Model instances with data:

// design/requirements.ts
import { defineSpecs } from 'speckeeper';
import type { Requirement } from './_models/requirement';
import { FunctionalRequirementModel } from './_models/requirement';

const requirements: Requirement[] = [
  {
    id: 'FR-001',
    name: 'User Authentication',
    type: 'functional',
    description: 'Users can authenticate using email and password',
    priority: 'must',
    acceptanceCriteria: [
      { id: 'FR-001-01', description: 'Valid credentials grant access', verificationMethod: 'test' },
      { id: 'FR-001-02', description: 'Invalid credentials show error', verificationMethod: 'test' },
    ],
  },
];

export default defineSpecs(
  [FunctionalRequirementModel.instance, requirements],
);

design/index.ts aggregates all spec files, and speckeeper.config.ts imports the result — no manual wiring needed beyond adding your spec file to design/index.ts.

4. Run validation

# Validate design integrity
npx speckeeper lint

# Check test coverage against requirements
npx speckeeper check test --coverage

# Analyze change impact
npx speckeeper impact FR-001

# LLM-powered quality audit (optional — requires @aaac/runtime + API key)
npm install --save-dev @aaac/runtime
npx speckeeper audit-requirements --adapter openai

Alternative: npx speckeeper init creates a minimal project with generic starter templates. Use this if you prefer to build models from scratch. See Model Definition Guide for details.

CLI Commands

Full CLI reference: docs/cli-reference.md | Machine-readable contract: cli-contract.yaml

Deterministic Commands

| Command | Description | |---------|-------------| | speckeeper init | Initialize a new project with starter templates | | speckeeper build | Generate docs/ and specs/ from TypeScript models | | speckeeper lint | Validate design integrity (ID uniqueness, references, phase gates) | | speckeeper check | Verify consistency with external SSOT | | speckeeper check test --coverage | Verify test coverage for requirements | | speckeeper drift | Detect manual edits to generated docs/ files | | speckeeper impact <id> | Analyze change impact for a specific element | | speckeeper new <type> | Create a new element with auto-generated ID | | speckeeper scaffold | Generate _models/ from a Mermaid flowchart | | speckeeper convert <file> | Convert a TS spec data file to YAML format |

LLM-Powered Commands

| Command | Description | |---------|-------------| | speckeeper audit-requirements | Semantic requirement quality audit via LLM | | speckeeper propose-trace-links | Propose candidate traceability links with confidence scores | | speckeeper explain-impact | Explain impact analysis output in human-readable form (accepts JSON from impact via stdin) | | speckeeper propose-acceptance-criteria | Propose testable acceptance criteria in Given/When/Then format | | speckeeper review | Judge declared review checks per spec (or per target) and record the judgments; see Per-Spec LLM Review |

Utility Commands

| Command | Description | |---------|-------------| | speckeeper extract [--all] [commands...] | Extract embedded CLI contract specification (YAML/JSON) | | speckeeper agents [--format json\|yaml] | Output resolved agent-contracts DSL |

Common LLM Options:

All LLM commands support these additional options:

  • --log-file <path> / -l: Write structured progress log to a file for debugging and monitoring

LLM-powered commands are read-only by default. audit-requirements and explain-impact do not modify files or state. propose-* commands produce proposals; generated output should be reviewed before use. LLM commands do not replace deterministic gates — they are an additional semantic review layer on top of lint, check, and impact.

All LLM commands require @aaac/runtime (optional peer dependency) and an adapter key, and support --show-prompt to inspect the prompt without calling the LLM.

# Audit requirement quality
npx speckeeper audit-requirements --adapter openai

# Propose traceability links
npx speckeeper propose-trace-links --adapter claude

# Explain impact analysis for a PR comment
npx speckeeper impact FR-001 --format json | npx speckeeper explain-impact --adapter openai

# Inspect the prompt without calling the LLM
npx speckeeper audit-requirements --show-prompt

Per-Spec LLM Review

speckeeper review judges each target of a declared check with an LLM and records the judgment in review/<check>/<target>.yaml. speckeeper lint then gates on the records without calling the LLM, so the usual setup is: judge locally, commit the records, and let CI check them.

Declaring checks

A check lives in the config (review.checks, with select()) or on a model next to its lintRules (reviewChecks, one target per spec by default):

import { z } from 'zod';
import { defineConfig, defineReviewOutput, relationsContext } from 'speckeeper';

export default defineConfig({
  models: design.models,
  specs: design.specs,
  review: {
    adapter: 'claude',
    allowApiKey: false,            // refuse to run on a billed API key (exit 13)
    timeoutSeconds: 600,           // upper limit of one judge call (default 600)
    contextProviders: [relationsContext({ depth: 2, relationTypes: ['satisfies', 'refines'] })],
    checks: [{
      id: 'req-verifiability',
      description: 'Each acceptance criterion can be verified',
      select: (registry) => [...registry.models.requirement.keys()].map((id) => ({ id, specIds: [id] })),
      rules: [
        { code: 'AC-VAGUE', severity: 'warning', description: 'A criterion without a number or a decision rule' },
        { code: 'AC-NO-ERROR', severity: 'error', description: 'No criterion for the error or boundary case' },
      ],
      prompt: { file: 'review/prompts/verifiability.md' },
      output: defineReviewOutput({ findingExtra: { reason: z.string() } }),
      modelClass: 'standard',
    }],
    gate: { stale: 'error', openFindings: 'error', blocking: ['error'], judgeChange: 'ignore' },
  },
});

| Field | Meaning | |-------|---------| | select(registry) | Targets ({ id, specIds }); a model's check defaults to one target per spec | | context | A context provider or its ID; the default relations lists the target's specs and the specs reachable over relations, found the way impact finds them | | rules | The points the check reviews: { code, severity, description }. The codes are the project's own. With rules, the judge may only report declared codes (any other is sent back once, then fails the target) and each finding is recorded with its rule's severity, so the blocking gate does not depend on the judge | | prompt | Instructions, inline or { file } (relative to the project root). With rules, it carries what the rules share; without them, it is where the points are described and the judge chooses code and severity | | output | Fields added to the common finding shape with defineReviewOutput (code, severity, message, subject, location, evidence, suggestion are fixed) | | modelClass | fast, standard (default) or thinking, resolved by @aaac/runtime | | verify | { prompt }: re-check each finding in a separate call; false positives are kept with status: false_positive |

Running

npx speckeeper review --dry-run          # what would be judged, why, and the estimated tokens
npx speckeeper review                    # judge missing and stale targets
npx speckeeper review --check req-verifiability --target REQ-012 --force
npx speckeeper review --show-packet --target REQ-012   # the exact input, no LLM call
npx speckeeper review --emit packets     # hand packets to someone else …
npx speckeeper review ingest packets     # … and record their results
npx speckeeper review rebaseline --reason "fixed a typo in the prompt"
npx speckeeper review --prune            # remove records of checks or targets that are gone

Each packet (role, check prompt, context, output schema) is hashed. A target is judged again only when its packet hash changed — a prompt, a spec in its context, or a file the context read — or, with gate.judgeChange: stale, when the configured model changed. --dry-run names the changed inputs and the relation path to a changed spec. rebaseline accepts a change that cannot affect the verdict without judging again, and records the reason, the changed inputs and the git HEAD in the record.

Before calling the LLM, review checks the credentials: ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN (API key), CLAUDE_CODE_OAUTH_TOKEN, or the logged-in Claude Code. Without any (for example in CI) the run is skipped with exit 0; --require-judge makes that exit 14. It then makes one short call to the judge before judging any target: rejected credentials, such as an expired login, are handled the same way (exit 0, or 14 with --require-judge) and the message says how to log in again (run claude and enter /login, or set CLAUDE_CODE_OAUTH_TOKEN); a judge that does not answer stops the run with exit 12. The judge gets no tools and an empty working directory, and each call has a time limit (review.timeoutSeconds, default 600): a judge that does not answer in time is stopped together with its process, the run ends with exit 12, and the records already written are kept.

Where the judge runs. The claude adapter runs Claude Code through the Claude Agent SDK, which picks the Claude Code build for the CPU architecture Node.js runs as. On an Apple silicon Mac, an x64 Node.js (for example an Intel Homebrew Node) makes it pick the x64 build, which hangs at start under Rosetta. review stops before judging in that case and says so; use an arm64 Node.js and install the dependencies again with it, or set review.claudeExecutable to a Claude Code executable to run instead (relative to the project root). --dry-run, --show-packet and --emit do not call the judge and are not affected. A finding that names an unknown spec or field, or quotes text that is not in the context, is sent back once for correction and otherwise fails the target.

Records and the lint gate

Records are written with a fixed key order, so changes show as small diffs. A finding's status is open, false_positive or dismissed (with a statusNote); a status set by a person carries over to a new judgment when the finding's fingerprint (check, target, code, subject or quote, evidence) matches exactly one finding.

| Code | Reported when | Default | |------|---------------|---------| | REVIEW-001 | A target has no record, or its record is stale | error (gate.stale) | | REVIEW-002 | A record has an open finding of a blocking severity (gate.blocking) | error (gate.openFindings) | | REVIEW-003 | A record's check or target no longer exists | warning | | REVIEW-004 | A record was judged by another model (gate.judgeChange: warning) | warning |

review exits 0 whether or not there are findings; lint decides. Exit codes: 1 configuration error or a target whose output stayed invalid, 11 @aaac/runtime missing, 12 adapter error (records already written are kept), 13 API key refused, 14 judge required but unavailable.

Validation Features

Design Integrity (lint)

$ npx speckeeper lint

speckeeper lint

  Design: design/
  Loaded: 17 files

  Running lint checks...

  ✓ No issues found

Checks include:

  • ID uniqueness — No duplicate IDs within model types
  • ID conventions — Enforce naming patterns (e.g., FR-001, COMP-AUTH)
  • Reference integrity — All referenced IDs must exist
  • Circular dependency detection — Prevent reference loops
  • Phase gates — Ensure TBD items are resolved by target phase
  • Custom lint rules — Define model-specific validation

External SSOT Validation (check)

Validate your specifications against actual implementation artifacts. speckeeper performs a global source scan across all configured sources, then optionally runs deep validation using model-specific rules.

The check flow has three levels:

  1. Existence check (automatic) — Is the spec ID found in any configured source?
  2. Structural check (via deepValidation) — Does the matched object's structure match? (e.g. HTTP method, table columns)
  3. Type check (via deepValidation) — Do types match? (e.g. parameter types, column types)

Source configuration

Define global scan sources in speckeeper.config.ts:

// speckeeper.config.ts
import { defineConfig } from 'speckeeper';

export default defineConfig({
  // ...
  sources: [
    {
      type: 'openapi',
      paths: ['api/openapi.yaml'],
      relation: 'implements',
    },
    {
      type: 'ddl',
      paths: ['db/schema.sql'],
      relation: 'implements',
    },
    {
      type: 'annotation',
      paths: ['test/**/*.test.ts', 'tests/**/*.test.ts'],
      relation: 'verifiedBy',
    },
    {
      type: 'annotation',
      paths: ['src/**/*.ts'],
      exclude: ['src/**/*.test.ts'],
      relation: 'implements',
    },
  ],
});

Each source defines:

  • type — Built-in ('openapi', 'ddl', 'annotation') or custom with a scanner plugin
  • paths — Glob patterns for files to scan
  • relation — Whether matches represent 'implements' or 'verifiedBy'

Built-in scanners

| Scanner | Finds spec IDs via | Deep validation | |---------|-------------------|-----------------| | openapi | operationId, path segment, schema name, x-spec-id | HTTP method, parameter names/types, response property names/types | | ddl | Table name (case-insensitive, schema-prefix stripped) | Column names, column types (containment-based) | | annotation | @verifies, @implements, @traces annotations | — |

Annotations work in any comment style (//, #, --, /* */, <!-- -->). Multiple IDs can be comma- or space-separated.

// tests/unit/auth.test.ts
// @verifies FR-001, FR-001-01
describe('User Authentication', () => { ... });
// src/auth/handler.ts
// @implements FR-001
export class AuthHandler { ... }

Deep validation (optional)

Models can define deepValidation to enable Level 2/3 structural checks on matched source objects:

class EntityModel extends Model<typeof EntitySchema> {
  // ... schema, lintRules, etc.

  protected deepValidation: DeepValidationConfig<Entity> = {
    ddl: {
      mapper: (spec) => ({
        tableName: spec.tableName,
        columns: spec.columns.map(c => ({ name: c.name, type: c.type })),
        checkTypes: true,
      }),
    },
    openapi: {
      mapper: (spec) => ({
        path: spec.apiPath,
        method: spec.httpMethod,
        responseProperties: spec.fields.map(f => ({ name: f.name, type: f.type })),
      }),
    },
  };
}

Without deepValidation, speckeeper still performs existence checks for all spec IDs across all configured sources.

Lookup keys (when spec ID differs from external identifier)

By default, the global scanner searches for each spec's id in external sources. When the external identifier differs — for example, entity ID "user" vs DDL table name "users" — define lookupKeys on the model to map per source type:

class EntityModel extends Model<typeof EntitySchema> {
  readonly id = 'entity';
  readonly name = 'Entity';
  readonly idPrefix = 'ENT';
  readonly schema = EntitySchema;

  protected lookupKeys: LookupKeyConfig<Entity> = {
    ddl: (spec) => spec.tableName,
    openapi: (spec) => spec.schemaName ?? spec.id,
  };
}

With this configuration, when scanning DDL sources the scanner searches for spec.tableName instead of spec.id. If a match is found, the result is mapped back to the original spec ID for reporting and deep validation.

lookupKeys is optional per source type — any source type not listed falls back to spec.id.

Custom scanners

For file formats not covered by the built-in scanners, provide a custom SourceScanner plugin:

// speckeeper.config.ts
import { defineConfig } from 'speckeeper';
import type { SourceScanner } from 'speckeeper';

const protoScanner: SourceScanner = {
  findSpecIds(content, specIds, filePath) {
    // Parse protobuf content, find spec IDs in service/message names
    // Return SourceMatch[] with specId, location, and optional context
    return [];
  },
};

export default defineConfig({
  sources: [
    { type: 'proto', paths: ['proto/**/*.proto'], relation: 'implements', scanner: protoScanner },
    // ... other sources
  ],
});
$ npx speckeeper check --verbose

speckeeper check

  Design: design/
  Type:   all

  ✓ All checks passed

Transitive coverage

Narrative-level specs (e.g. UseCases) rarely have direct @verifies UC-001 annotations in code. Instead, they are verified indirectly through a chain: UseCase is satisfied by Requirements, and those Requirements are verified by tests.

Configure coverage.transitiveRelations to enable automatic transitive coverage:

// speckeeper.config.ts
export default defineConfig({
  sources: [/* ... */],
  coverage: {
    transitiveRelations: ['satisfies'],
    threshold: 80, // default; check --coverage exits 1 below it
  },
});

When speckeeper check --coverage runs:

  1. The global scan determines which specs are directly covered (found in external sources)
  2. For each transitive relation type, the framework walks the relation graph
  3. A spec is transitively covered if ALL specs that relate to it via a transitive relation are themselves covered (directly or transitively)

No per-model code is needed. Coverage is computed purely from relation data and config. The check exits 1 when the transitive coverage, or any per-model coverage checker, is below coverage.threshold, and when check <type> names a source type that no configured source has (it would otherwise measure nothing and pass).

$ npx speckeeper check test --coverage

  Transitive coverage (via satisfies)
  ─────────────────────────────────────
  Total:     12
  Covered:   11  (8 direct + 3 transitive)
  Uncovered: 1
  Coverage:  92%

Multi-level chains are supported. For example, with transitiveRelations: ['satisfies', 'verifies'], if TEST-001 verifies FR-001, and FR-001 satisfies UC-001, then UC-001 is transitively covered when TEST-001 is directly matched.

Model Levels & Traceability

speckeeper organizes models by abstraction level:

| Level | Focus | Examples | |-------|-------|----------| | L0 | Business + Domain (Why) | UseCase, Actor, Term | | L1 | Requirements (What) | Requirement, Constraint | | L2 | Design (How) | Component, Entity, Layer | | L3 | Implementation (Build) | Screen, APIRef, TableRef |

Relations between models enable impact analysis:

$ npx speckeeper impact FR-001

FR-001 (Requirement)
├── implements: COMP-AUTH (Component)
├── satisfies: UC-001 (UseCase)
└── verifiedBy: TEST-001 (TestRef)

Relation Types

| Relation | Direction | Description | |----------|-----------|-------------| | implements | spec→external | Spec is implemented as external artifact (OpenAPI, DDL) | | verifiedBy | spec→test | Spec is verified by external test code | | satisfies | L1→L0 | Satisfies a use case | | refines | Same level or lower | Refinement | | verifies | test→implementation | Test verifies implementation code (external, no checker) | | dependsOn | None | Dependency | | relatedTo | None | Association |

See Model Entity Catalog for full details on relation types and level constraints.

Customizing Models

Scaffolded models provide a base schema (id, name, description, relations). You can customize them or add new domain-specific models using core factory functions from speckeeper/dsl:

import { z } from 'zod';
import { Model, RelationSchema } from 'speckeeper';
import type { LintRule, Exporter, ModelLevel } from 'speckeeper';
import { requireField, arrayMinLength } from 'speckeeper/dsl';

const RunbookSchema = z.object({
  id: z.string(),
  name: z.string().min(1),
  description: z.string(),
  severity: z.enum(['critical', 'high', 'medium', 'low']),
  steps: z.array(z.object({
    action: z.string(),
    verification: z.string().optional(),
  })).min(1),
  relations: z.array(RelationSchema).optional(),
});

type Runbook = z.input<typeof RunbookSchema>;

class RunbookModel extends Model<typeof RunbookSchema> {
  readonly id = 'runbook';
  readonly name = 'Runbook';
  readonly idPrefix = 'RB';
  readonly schema = RunbookSchema;
  readonly description = 'Incident runbooks';
  protected modelLevel: ModelLevel = 'L3';

  protected lintRules: LintRule<Runbook>[] = [
    requireField<Runbook>('description', 'error'),
    arrayMinLength<Runbook>('steps', 1),
  ];

  protected exporters: Exporter<Runbook>[] = [];
}

Core DSL factories (speckeeper/dsl) include requireField, arrayMinLength, idFormat, childIdFormat, markdownExporter, annotationCoverage, relationCoverage, and baseSpecSchema. Global scanner utilities (openapiScanner, ddlScanner, annotationScanner, createAnnotationScanner) are also re-exported for advanced use.

CI Integration

name: Design Validation
on:
  pull_request:
    paths: ['design/**', 'speckeeper.config.ts']

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx speckeeper lint --strict
      - run: npx speckeeper check
      - run: npx speckeeper drift --fail-on-drift
      # Optional: LLM semantic audit (requires API key)
      # - run: npx speckeeper audit-requirements --adapter openai --report-format json --fail-on error
      #   env:
      #     OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Agent-Native Toolchain

speckeeper is designed for development workflows where AI agents are first-class participants in design review, traceability analysis, and quality assurance. It encapsulates domain-specific semantic reasoning inside the toolchain itself, returning structured results that humans, CI systems, and AI agents consume in the same way.

Deterministic checks first

Anything that can be validated mechanically is validated deterministically: ID uniqueness, reference integrity, circular dependency detection, phase gate enforcement, external SSOT conformance, drift detection, and impact analysis via relation graph traversal.

Semantic audit inside the toolchain

Domain-specific reasoning that is difficult to express as static rules is handled by LLM-based commands:

  • audit-requirements — Requirement verifiability, ambiguity, granularity, terminology consistency, and design-mixing detection
  • propose-trace-links — Identify candidate traceability links between specs and implementation artifacts with confidence scores and rationale
  • explain-impact — Translate machine-readable impact analysis output into human-readable explanations for PM and executive stakeholders
  • propose-acceptance-criteria — Generate testable acceptance criteria in Given/When/Then or verification format

Structured findings

LLM output is not free-form text. Results conform to typed schemas such as RequirementAuditResult, TraceLinkResult, ImpactExplainResult, and AcceptanceCriteriaResult. Audit-style results are compatible with the common AgentAuditResult / AgentFinding shape so that higher-level workflow agents can aggregate findings across toolchains.

Tool-owned domain knowledge

speckeeper owns the rules and reasoning for design specification quality. Instead of embedding design review heuristics into a top-level agent prompt, domain expertise is encapsulated inside the tool. Higher-level agents only need to invoke the command and interpret the structured output.

Agent-readable interface

Tool capabilities are described in machine-readable form via cli-contract.yaml: artifacts read/written, side effects, risk levels, confirmation requirements, and output schemas.

LLM Adapter Configuration

| Adapter | Default Model | Environment Variable | |---------|---------------|---------------------| | openai | runtime default | OPENAI_API_KEY | | gemini | runtime default | GEMINI_API_KEY | | claude | runtime default | ANTHROPIC_API_KEY | | mock | — | — |

Default models are defined by @aaac/runtime and may change between releases. Use --model to pin a specific model, or AGENT_RUNTIME_MODEL_<CLASS> (for example AGENT_RUNTIME_MODEL_STANDARD) to pin the model a model class resolves to.

# Install the runtime dependency to enable LLM features
npm install --save-dev @aaac/runtime

Technology Stack

| Component | Technology | |-----------|-----------| | Language | TypeScript (Node.js) | | Schema validation | Zod | | LLM integration | @aaac/runtime (optional peer dep) | | Agent DSL | agent-contracts — agent/task/workflow definitions | | CLI contract | cli-contracts — machine-readable interface spec | | Package manager | npm |

Documentation

Compatibility

  • Node.js >= 20.0.0
  • TypeScript >= 5.0

Contributing

# Install dependencies
npm install

# Run tests
npm test

# Lint
npm run lint
npm run lint:design

# Full CI check
npm run ci

License

MIT