Package authoring

Harn packages are ordinary Harn projects with package metadata, stable exports, tests, and optional connector contracts in harn.toml. They use the same [dependencies], .harn/packages/, and harn.lock workflow as applications.

Create a package

harn new package acme-tools
cd acme-tools
harn test tests/
harn package check
harn package docs
harn package pack

The package template creates:

  • harn.toml with [package] metadata, [exports], and [dependencies]
  • lib/main.harn with a documented public function
  • tests/ smoke tests
  • README.md, LICENSE, docs/api.md
  • CI that installs harn-cli, runs tests, checks docs drift, and runs harn package pack --dry-run

Consumers can add a local package while developing:

harn add ../acme-tools
harn install
harn package list
harn package doctor

Before publishing, replace local path dependencies with registry or git dependencies pinned to a version or rev.

Create a tool package

harn tool new acme-echo
cd acme-echo
harn test tests/
harn package check
harn package docs --check
harn package pack --dry-run

The tool template creates a package with a Harn-native registry builder, [[package.tools]] metadata, a local smoke test that dispatches the tool, and CI that exercises the same publish-readiness checks. The generated package is intentionally ordinary Harn source; consumers install it with harn add, import the stable export, and merge it into their own tool registry:

import { tools } from "acme-echo/tools"

Create an OpenAPI SDK package

harn package scaffold openapi \
  --name acme-sdk-harn \
  --module-name acme_sdk \
  --client-name AcmeClient \
  --spec ./openapi.json \
  --out ./acme-sdk-harn
cd acme-sdk-harn
harn install
harn check src/lib.harn
harn test tests/
harn package check
harn package docs --check
harn package pack --dry-run

The OpenAPI scaffold uses harn-openapi to generate src/lib.harn, stores the input spec under openapi/, writes a scripts/regen.harn regeneration path, adds a package export for the generated module, and creates a smoke test that uses a mocked operation when the spec has a no-argument operation. The package is intentionally focused: it wraps the API described by the OpenAPI document, not an entire provider product line.

Hand-written harness.net calls are fine for private one-off endpoints, temporary admin probes, or gaps that are not in the OpenAPI document. Repeated provider API coverage, connector helpers, and user-facing package exports should use a generated SDK or a small Harn wrapper over one so auth, pagination, rate-limit metadata, and package tests stay centralized.

Create a skill package

harn skill new review-helper

harn skill new is the singular alias for harn skills new. Package authors can publish skills by adding [[package.skills]] entries that point at package-root-relative skill directories containing SKILL.md.

Create a connector package

harn new connector echo-connector
cd echo-connector
harn connector test .
harn package docs
harn publish --dry-run
harn publish

Connector packages use harn connector test . as the CI gate. It runs package metadata validation, harn check, harn lint, harn fmt --check, connector contract fixtures, package-local fixture tests, install/import smoke tests, and standalone Harn doc examples. Use harn connector check . when you only need the lower-level pure-Harn connector contract check.

Connector packages should also declare package-facing capability coverage on their [[providers]] entry so harn check --connector-matrix can compare provider support:

[[providers]]
id = "acme"
connector = { harn = "src/lib.harn" }
capabilities = ["webhook", "oauth", "rate_limit", "pagination", "graphql", "streaming"]

Manifest metadata

Publishable packages should include:

[package]
name = "acme-tools"
version = "0.1.0"
description = "Reusable Harn helpers."
license = "MIT OR Apache-2.0"
repository = "https://github.com/acme/acme-tools"
provenance = "https://github.com/acme/acme-tools/releases/tag/v0.1.0"
harn = ">=0.8,<0.9"
docs_url = "docs/api.md"
permissions = ["tool:read_only"]
host_requirements = ["workspace.read_text"]

[exports]
lib = "lib/main.harn"

[[package.tools]]
name = "read-note"
module = "lib/main.harn"
symbol = "tools"
description = "Read a note through the package tool registry."
permissions = ["tool:read_only"]

[package.tools.input_schema]
type = "object"
required = ["path"]

[package.tools.annotations]
kind = "read"
side_effect_level = "read_only"

[[package.skills]]
name = "review"
path = "skills/review"

[dependencies]
json-helpers = { git = "https://github.com/acme/json-helpers", tag = "v0.1.0" }

harn package check validates required metadata, dependency declarations, stable exports, tool and skill declarations, README/license presence, docs links, and Harn compatibility. Tool declarations must point at a parseable Harn module, provide valid JSON-schema-shaped input_schema and output_schema tables when present, and use policy annotations that match the runtime tool annotation schema. Skill declarations must stay inside the package root and point at a directory with valid SKILL.md front matter. Publish readiness rejects path-only dependencies and unsupported Harn version ranges because they cannot be reproduced from a registry index. Publishable dependencies should use direct git tag/rev pins or registry version ranges; branch dependencies are accepted with a warning because they are moving refs.

API docs

Document exported symbols with doc comments:

/// Return a greeting for `name`.
pub fn greet(name: string) -> string {
  return "Hello, " + name + "!"
}

Generate docs with:

harn package docs

CI should use:

harn package docs --check

Pack and publish dry run

harn package pack validates the package and writes an inspectable artifact directory at .harn/dist/<name>-<version>. It excludes local build state such as .git/, .harn/, target/, and node_modules/.

harn package pack --dry-run
harn package pack

harn publish --dry-run runs the publish-readiness checks and prints the git tag command plus the package-index diff that would be proposed. harn publish requires a clean git worktree, a non-empty CHANGELOG.md entry for the package version, and an unused vX.Y.Z tag. It then tags and pushes the configured remote, and opens a PR against the configured package index. Use --skip-index-pr to push only the tag when the index entry should be composed manually.

Lockfile provenance

harn.lock is the executable record of what was resolved and why. Every write captures:

  • version, generator_version, and protocol_artifact_version at the top level so downstream automation can detect when the lock was produced by an older Harn line.
  • Per-entry source, commit, and content_hash for git/registry deps, plus the resolved tag when a tag was used, package_version, harn_compat, package provenance, and a separate manifest_digest taken from the resolved package's harn.toml.
  • Exported modules, custom tools, skills, package permissions, and host requirements declared by the resolved package. Hosts and CI can inspect this metadata without reading arbitrary package source.
  • A [package.registry] table for entries originally resolved through the registry, preserving the registry source, package name, and exact resolved version so harn package outdated can compare against the registry's latest release without re-reading the manifest. Registry dependencies may remain in harn.toml as semver ranges, for example notion-sdk-harn = { version = ">=1.2,<2.0" }.

Path dependencies remain live-linked; their lockfile entry records the resolved path+file:// URI plus the same package_version and manifest_digest so harn package audit flags drift without rebuilding.

Maturing the package surface

Once a manifest and lockfile are in place, these commands cover the day-to-day automation surface:

  • harn package list — summarize locked packages, materialization status, exported modules/tools/skills, and declared permission or host requirements. Use --json when the output feeds host UI or CI policy.
  • harn package doctor — diagnose missing or stale lockfiles, missing materialized packages, content-hash mismatches, declared host capability gaps, and invalid installed package tool/skill metadata. It is safe for applications: publish-only checks remain under harn package check.
  • harn package outdated — surface registry version bumps and (with --remote) git branch HEAD drift. JSON output matches the report struct documented in cli-reference.md.
  • harn package audit — verify provenance, compatibility, and content integrity in one pass. --json returns a stable code per finding so CI can fail on specific issues (yanked registry pins, content-hash tampering, broken harn ranges) without parsing prose.
  • harn package artifacts check — compare a vendored copy of the protocol artifact manifest (e.g. spec/protocol-artifacts/manifest.json in your repo) against the running Harn. The exit code is non-zero on drift, so a downstream that vendors generated TypeScript or Swift bindings can fail CI when those bindings would change after a Harn bump.

Cross-repo bump workflow

When Harn cuts a new release, downstream package authors and host integrators (Burin Code, Harn Cloud, third-party connectors) need a consistent way to land the bump. The recommended sequence:

  1. Refresh the running Harn binary in the downstream repo (fetch-harn, make setup, or whatever the local script is).
  2. Re-resolve dependencies with harn install so harn.lock rewrites with the current generator_version, protocol_artifact_version, exported package surface, and host requirements.
  3. Inspect with harn package list --json and harn package doctor --json so host requirements and materialized package state are visible before tests start.
  4. Audit with harn package audit --json. If any package declares a harn range that excludes the new line, contact the package owner or pin to a previous tag.
  5. Check vendored protocol bindings with harn package artifacts check path/to/vendored/manifest.json. If it reports drift, regenerate the bindings (make gen-protocol-artifacts inside the downstream repo, or harn package artifacts manifest --output … to refresh the vendored copy).
  6. Surface registry updates with harn package outdated --json and bump dependency versions intentionally; harn update <alias> keeps each upgrade in its own commit.

Steps 2–5 are a single CI job for hosts that vendor Harn artifacts: harn install --frozen --json, then harn package doctor --json, then harn package audit --json, then harn package artifacts check. The --json output is stable across Harn lines, so the same automation script runs against multiple Harn versions during a staged rollout.