{"_id":"@agentproto/app-config","_rev":"3-60d752209a73cab4baa3297312173b08","name":"@agentproto/app-config","dist-tags":{"latest":"0.3.1"},"versions":{"0.2.0":{"name":"@agentproto/app-config","version":"0.2.0","keywords":["agentproto","app-config","yaml","zod","json-schema","contracts","gates","open-standard"],"license":"Apache-2.0","_id":"@agentproto/app-config@0.2.0","maintainers":[{"name":"agentiknet","email":"jeremy@agentik.net"}],"homepage":"https://agentproto.sh/docs","bugs":{"url":"https://github.com/agentproto/ts/issues"},"bin":{"app-config":"bin/app-config.mjs"},"dist":{"shasum":"8566d562363acc19d1530d752068ae97529d5c3b","tarball":"https://registry.npmjs.org/@agentproto/app-config/-/app-config-0.2.0.tgz","fileCount":16,"integrity":"sha512-2e2qbBPx+lNAEmToqjIeSVpsKqk5rwGHxJG7G3go8n1PIFdaWe/ipVDagM4umBIKxhbbVosWFSbCEnqrSqEpyg==","signatures":[{"sig":"MEYCIQCesITRTP7tErfOkdXwi0UlMv1/AALEpCKBVaPESPZcdQIhAKE2epdHkntqq1Gp9CWC0XLIc4iA+LE7KRYfo2tIhUQu","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentproto%2fapp-config@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":186694},"main":"dist/index.mjs","type":"module","_from":"file:agentproto-app-config-0.2.0.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./cli":{"types":"./dist/cli.d.ts","import":"./dist/cli.mjs","default":"./dist/cli.mjs"},"./package.json":"./package.json"},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json","clean":"rm -rf dist","test:watch":"vitest","check-types":"tsc --noEmit"},"_npmUser":{"name":"agentiknet","email":"jeremy@agentik.net"},"_resolved":"/tmp/c044e9c0aa761c64f0516c8e8617bc09/agentproto-app-config-0.2.0.tgz","_integrity":"sha512-2e2qbBPx+lNAEmToqjIeSVpsKqk5rwGHxJG7G3go8n1PIFdaWe/ipVDagM4umBIKxhbbVosWFSbCEnqrSqEpyg==","repository":{"url":"git+https://github.com/agentproto/ts.git","type":"git","directory":"packages/app-config"},"_npmVersion":"10.9.8","description":"@agentproto/app-config — layered YAML config kit for agentproto apps. defineAppConfig({ app, item, itemsKey, defaultsKey }) returns a typed kit: load() merges app-level defaults → app items[] entry → item file (deep for objects, replace for arrays), emits","directories":{},"_nodeVersion":"22.23.2","dependencies":{"yaml":"^2.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.5.4","tsup":"^8.5.1","vitest":"^3.2.4","typescript":"^5.9.3","@types/node":"^25.6.2","@agentproto/tooling":"0.1.0"},"peerDependencies":{"zod":"^4"},"_npmOperationalInternal":{"tmp":"tmp/app-config_0.2.0_1788654507394_0.34947086977795205","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@agentproto/app-config","version":"0.3.0","keywords":["agentproto","app-config","yaml","zod","json-schema","contracts","gates","open-standard"],"license":"Apache-2.0","_id":"@agentproto/app-config@0.3.0","maintainers":[{"name":"agentiknet","email":"jeremy@agentik.net"}],"homepage":"https://agentproto.sh/docs","bugs":{"url":"https://github.com/agentproto/ts/issues"},"bin":{"app-config":"bin/app-config.mjs"},"dist":{"shasum":"7922f85e46614c21d0cd6fc23778613244e5e22f","tarball":"https://registry.npmjs.org/@agentproto/app-config/-/app-config-0.3.0.tgz","fileCount":18,"integrity":"sha512-+AnIyFtr2ufYzzyA2+mEoAsD/UxNCP7nIEhzFf0ugbgh755ejTVE2XzDhaXHIYxqMQu3p8DijfbiKEhRCnZicw==","signatures":[{"sig":"MEQCIAH8iFGtfdNDt0mnuM0uqrlxRszEWzq+z6A3JvLlaVyIAiAPnvFhCLN3I3qi/xrFuUZcoPMUL3hRnVAwwuA1EYYSAw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentproto%2fapp-config@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":229815},"main":"dist/index.mjs","type":"module","_from":"file:agentproto-app-config-0.3.0.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./cli":{"types":"./dist/cli.d.ts","import":"./dist/cli.mjs","default":"./dist/cli.mjs"},"./package.json":"./package.json"},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json","clean":"rm -rf dist","test:watch":"vitest","check-types":"tsc --noEmit"},"_npmUser":{"name":"agentiknet","email":"jeremy@agentik.net"},"_resolved":"/tmp/ffbca64cd375127d2583e2ec282354d0/agentproto-app-config-0.3.0.tgz","_integrity":"sha512-+AnIyFtr2ufYzzyA2+mEoAsD/UxNCP7nIEhzFf0ugbgh755ejTVE2XzDhaXHIYxqMQu3p8DijfbiKEhRCnZicw==","repository":{"url":"git+https://github.com/agentproto/ts.git","type":"git","directory":"packages/app-config"},"_npmVersion":"10.9.8","description":"@agentproto/app-config — layered YAML config kit for agentproto apps. defineAppConfig({ app, item, itemsKey, defaultsKey }) returns a typed kit: load() merges app-level defaults → app items[] entry → item file (deep for objects, replace for arrays), emits","directories":{},"_nodeVersion":"22.23.2","dependencies":{"yaml":"^2.9.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.5.4","tsup":"^8.5.1","vitest":"^3.2.7","typescript":"^5.9.3","@types/node":"^25.9.5","@agentproto/tooling":"0.1.0"},"peerDependencies":{"zod":"^4"},"_npmOperationalInternal":{"tmp":"tmp/app-config_0.3.0_1788960686842_0.39035977203950223","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"_id":"@agentproto/app-config@0.3.1","bin":{"app-config":"bin/app-config.mjs"},"bugs":{"url":"https://github.com/agentproto/ts/issues"},"dist":{"shasum":"33f448d0dd0e2e33556a8bb782493f4ba53edc96","tarball":"https://registry.npmjs.org/@agentproto/app-config/-/app-config-0.3.1.tgz","fileCount":18,"integrity":"sha512-ajwjWtgNKBSAik0H/zLDNiQ47KFo3lIw2ZcJlzRpY1iZuF/0/jnFQrMy8J8GgR26HpcJKO73B8y2g/tUuxfTaA==","signatures":[{"sig":"MEUCIBJwijRckfCcXP/bDS434NlpLNil9dVkZDbWT7Ww1BJTAiEA2SQZSIxa/prZ0DjyiaQFrDEjMy1k8MTt4wCne5mNJZQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDgcMCEbshbYM5AjHnxsXKzomCnR8l48W2/70EE4XUU6gIhAML2mG/jdS+sE1THJZ5+fevB8zlmzR7UP933Ne9egF5K"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentproto%2fapp-config@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":229815},"main":"dist/index.mjs","name":"@agentproto/app-config","type":"module","_from":"file:agentproto-app-config-0.3.1.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./cli":{"types":"./dist/cli.d.ts","import":"./dist/cli.mjs","default":"./dist/cli.mjs"},"./package.json":"./package.json"},"license":"Apache-2.0","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json","clean":"rm -rf dist","test:watch":"vitest","check-types":"tsc --noEmit"},"version":"0.3.1","_npmUser":{"name":"agentiknet","email":"jeremy@agentik.net"},"homepage":"https://agentproto.sh/docs","keywords":["agentproto","app-config","yaml","zod","json-schema","contracts","gates","open-standard"],"_resolved":"/tmp/255203c078103c110b84bd4ed8e706ec/agentproto-app-config-0.3.1.tgz","_integrity":"sha512-ajwjWtgNKBSAik0H/zLDNiQ47KFo3lIw2ZcJlzRpY1iZuF/0/jnFQrMy8J8GgR26HpcJKO73B8y2g/tUuxfTaA==","repository":{"url":"git+https://github.com/agentproto/ts.git","type":"git","directory":"packages/app-config"},"_npmVersion":"10.9.8","description":"@agentproto/app-config — layered YAML config kit for agentproto apps. defineAppConfig({ app, item, itemsKey, defaultsKey }) returns a typed kit: load() merges app-level defaults → app items[] entry → item file (deep for objects, replace for arrays), emits","directories":{},"maintainers":[{"name":"agentiknet","email":"jeremy@agentik.net"}],"_nodeVersion":"22.23.2","dependencies":{"yaml":"^2.9.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.6.5","tsup":"^8.5.1","vitest":"^3.2.7","typescript":"^5.9.3","@types/node":"^25.9.5","@agentproto/tooling":"0.1.0"},"peerDependencies":{"zod":"^4"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/app-config_0.3.1_1789642929882_0.010870697673050156"}}},"time":{"created":"2026-09-06T00:28:27.220Z","modified":"2026-09-17T11:02:10.325Z","0.2.0":"2026-09-06T00:28:27.543Z","0.3.0":"2026-09-09T13:31:27.006Z","0.3.1":"2026-09-17T11:02:09.954Z"},"bugs":{"url":"https://github.com/agentproto/ts/issues"},"license":"Apache-2.0","homepage":"https://agentproto.sh/docs","keywords":["agentproto","app-config","yaml","zod","json-schema","contracts","gates","open-standard"],"repository":{"url":"git+https://github.com/agentproto/ts.git","type":"git","directory":"packages/app-config"},"description":"@agentproto/app-config — layered YAML config kit for agentproto apps. defineAppConfig({ app, item, itemsKey, defaultsKey }) returns a typed kit: load() merges app-level defaults → app items[] entry → item file (deep for objects, replace for arrays), emits","maintainers":[{"name":"agentiknet","email":"jeremy@agentik.net"}],"readme":"# @agentproto/app-config\n\nLayered YAML config kit for agentproto apps — the app-agnostic core that\n`@agstudio/book-config` (collection.yaml → order entry → book.yaml) sits on.\n`defineAppConfig({ app, item, itemsKey, defaultsKey })` returns a typed kit\nthat loads, validates, schemas, contracts, gates, and verifies an app's\nlayered YAML configuration.\n\n## Quick start\n\n```ts\n// app.config.ts\nimport { z } from \"zod\"\nimport { defineAppConfig } from \"@agentproto/app-config\"\n\nexport const kit = defineAppConfig({\n  app: z.object({\n    id: z.string(),\n    items: z.array(z.looseObject({ id: z.string() })).default([]), // entries may carry overrides\n    defaults: z.object({ lang: z.string().default(\"en\") }).default({ lang: \"en\" }),\n  }),\n  item: z.object({\n    id: z.string(), // REQUIRED on every item file\n    title: z.string(),\n    lang: z.string().default(\"en\"),\n  }),\n  itemsKey: \"items\",\n  defaultsKey: \"defaults\",\n})\n\nexport const template = (item) => ({ id: item.id, title: item.value.title })\n```\n\n```yaml\n# config/app.yaml\nid: doc-app\ndefaults:\n  lang: en\nitems:\n  - id: guide          # entry: position in the run order + per-item overrides\n```\n\n```yaml\n# config/items/guide.yaml\nid: guide\ntitle: The Guide\n```\n\n## Precedence\n\n`load(rootDir)` merges, lowest → highest (the same chain book-config\nresolves):\n\n1. **app-level defaults** — `app.yaml` under `defaultsKey` (a nested dot\n   path like `presets.default` works; an array leaf, e.g.\n   `knowledge.defaults`, mounts under its parent segment so\n   `mergeArraysBy` can key it)\n2. **the app `items[]` entry** matched against the item (see\n   `matchKey` below; default `entry.id === item.id`) — run-order position\n   plus per-item overrides\n3. **the item file** — wins on conflict (a file that narrows its slice must\n   not inherit the app-wide default)\n\nMerge is deterministic: objects deep-merge key-by-key, arrays REPLACE\n(order is meaningful configuration, not something to interleave). Two\nescape hatches: `mergeArraysBy` (keyed-array merge —\n`{ knowledge: \"workspace\" }` makes overlay entries with the same\n`workspace` key REPLACE the base entry in place, others append) and\n`project` (a hook over the merged raw value before the item schema parses\nit, for per-field projections like `accent: book.cover?.accent ??\ncollection.cover.accents[book.vertical]`). Merge layers read from the raw\nYAML so schema-stripped keys still merge; validation runs through both\nschemas. `precedence` accepts a permutation of\n`[\"defaults\", \"entry\", \"item\"]` if an app wants the opposite conflict\nresolution.\n\n`load` returns `{ rootDir, appFile, app, items: Map<id, ResolvedItem>, order }`\n— `order` lists entry-matched items first (entry order), then file-only\nitems by filename. Every `ResolvedItem` carries `itemPath` (absolute) and\n`dir` (the directory containing it). An item file without a non-empty\nstring `id` is keyed by its per-item directory name (`manuscripts/<dir>/book.yaml`\n→ `book3-le-seo-augmente`) or, for a flat glob, its file basename.\n\n## Kit surface\n\n- **`load(rootDir, { appFile?, itemsGlob? })`** — defaults\n  `config/app.yaml` and `config/items/*.yaml`. Globs support a flat\n  directory, a leading doublestar segment (recursive), a fixed basename\n  inside per-item directories (`manuscripts/*/book.yaml`), and wildcard\n  directory segments (`groups/team-*/item.yaml`). Throws\n  `AppConfigError` / zod errors.\n- **`jsonSchemas()` / `writeSchemas(dir)`** — JSON Schemas emitted with\n  `io: \"input\"`, so defaulted fields are NOT `required`.\n- **`contracts({ resolved, template, dir })`** — `template(item)` renders\n  each item; `write()` emits `contracts/<id>.contract.json` (sorted keys,\n  deterministic bytes); `check()` diffs the sha256 of the canonical JSON\n  against disk and returns `{ id, file, reason: \"missing\" | \"drifted\" }[]`.\n- **`gates(resolved, rules)`** (async) — declarative rules\n  `{ id, level: \"error\" | \"warn\", test: (ctx) => Finding[] | Promise<Finding[]> }`\n  where `ctx = { resolved, item?, readArtifact(relPath): Promise<string>, contract? }`;\n  `readArtifact` resolves relative to `resolved.rootDir` and rejects on\n  path escape or missing file. `ok` is false only on error-level findings.\n- **`verify(resolved, { rules?, template?, contractsDir?, scopes? })`**\n  (async) — composes gates + `contracts.check` + caller scope functions\n  into one `{ ok, findings, summary: { errors, warnings, skipped } }`;\n  findings are `{ scope, level: \"error\" | \"warn\" | \"skipped\", message,\n  item?, attrs? }` — `attrs` is free-form (`{ book, chapter }`), the shape\n  an app's `verify.command` reports.\n- **`scopes`** may be async and receive a `ScopeContext` handle —\n  `(resolved, ctx) => VerifyFinding[] | Promise<VerifyFinding[]>` where\n  `ctx.readArtifact(relPath)` resolves relative to `resolved.rootDir`\n  (same root-escape guard as gate rules) — so one umbrella scope can read\n  artifacts while emitting error/warn/skipped findings at their own\n  per-finding levels. Sync single-argument scopes keep working.\n- **`gates`** findings may carry their own `level` (`\"error\" | \"warn\"`),\n  overriding the rule's level per finding.\n- **`project`** exposes its projected object on `ResolvedItem.projected`\n  (pre-parse, verbatim). The item schema's output (`ResolvedItem.value`)\n  strips keys it does not declare, so derived fields computed by `project`\n  survive on `projected` instead. Absent when there is no `project` hook.\n\n## Consumer with a non-`id` data model\n\nThe kit is not tied to `id`-keyed items. `@agstudio/book-config`'s real\nmodel: a `collection.yaml` whose `order` entries are `{n, slug, tier}`,\nmatched against each `manuscripts/<book>/book.yaml` by the `n` field;\nseries knowledge defaults merge keyed by `workspace`; the cover accent\nfalls back to the collection's per-vertical accent map:\n\n```ts\nimport { z } from \"zod\"\nimport { defineAppConfig, type AppKit } from \"@agentproto/app-config\"\n\nconst CollectionSchema = z.object({\n  id: z.string(),\n  cover: z.object({ accents: z.record(z.string(), z.string()) }).default({ accents: {} }),\n  knowledge: z.object({ defaults: z.array(KnowledgeSelector).default([]) }).default(...),\n  order: z.array(z.object({ n: z.number().int(), slug: z.string(), tier: z.string() })).default([]),\n})\n\nconst BookSchema = z.object({\n  n: z.number().int(),\n  slug: z.string(),\n  vertical: z.string(),\n  accent: z.string().optional(),\n  lang: z.string().default(\"en\"),\n  knowledge: z.array(KnowledgeSelector).default([]),\n})\n\nexport const kit: AppKit<\n  z.output<typeof CollectionSchema>,\n  z.output<typeof BookSchema>\n> = defineAppConfig({\n  app: CollectionSchema,\n  item: BookSchema,          // no `id` — items are keyed by their directory\n  itemsKey: \"order\",         // the ordered entries are `order[]`\n  defaultsKey: \"knowledge.defaults\", // nested path; array leaf mounts under \"knowledge\"\n  matchKey: { entry: \"n\", item: \"n\" },\n  mergeArraysBy: { knowledge: \"workspace\" }, // keyed merge, not replace\n  project: (merged, ctx) => ({\n    ...merged,\n    accent: merged[\"accent\"] ?? ctx.app.cover.accents[String(merged[\"vertical\"])],\n  }),\n})\n\n// manuscripts/book3-le-seo-augmente/book.yaml → resolved.items.get(\"book3-le-seo-augmente\")\nconst resolved = kit.load(contentFactoryRoot, {\n  appFile: \"collection.yaml\",\n  itemsGlob: \"manuscripts/*/book.yaml\",\n})\n```\n\nScopes and gate rules are generic over the app's own resolved type, so an\napp registers them over `Resolved<Collection, Book>` instead of the loose\ndefault:\n\n```ts\ntype ResolvedBooks = Resolved<z.output<typeof CollectionSchema>, z.output<typeof BookSchema>>\n\nconst scopes: Record<string, ScopeFn<ResolvedBooks>> = {\n  // async scopes get ctx.readArtifact (rootDir-escape-guarded)\n  accents: async (r, ctx) => { /* r.items.get(id)?.value.accent; await ctx.readArtifact(...) */ },\n}\nconst rules: GateRule<ResolvedBooks>[] = [\n  {\n    id: \"line-width\",\n    level: \"error\",\n    test: async (ctx) => {\n      const md = await ctx.readArtifact(`output/${ctx.resolved.order[0]}/chapter.md`)\n      return md.split(\"\\n\").filter((l) => l.length > 100).map((_, i) => ({\n        message: `line too long`, item: \"book3\", attrs: { book: \"book3\", chapter: String(i) },\n      }))\n    },\n  },\n]\n```\n\n## CLI\n\n```sh\nnode --experimental-strip-types node_modules/.bin/app-config check app.config.ts\nnode --experimental-strip-types node_modules/.bin/app-config schema app.config.ts\nnode --experimental-strip-types node_modules/.bin/app-config contracts [--check] app.config.ts\nnode --experimental-strip-types node_modules/.bin/app-config verify app.config.ts\n```\n\nThe config module exports `kit` plus optional `rules`, `template`,\n`contractsDir`, and `scopes`. An app's AIP `verify.command` can simply be\n`node scripts/verify.mjs` calling `runCli` from `@agentproto/app-config/cli`.\n\n## CLI smoke test\n\n```sh\npnpm --filter @agentproto/app-config test   # 34 tests: precedence, schemas, contracts drift, gates (readArtifact/attrs), verify, CLI, consumer-zod fixture\n```\n\n## zod as a peer dependency\n\nzod is a **peerDependency** (`^4`), and the kit's public generic surface\nmentions **no zod types at all**: `defineAppConfig` / `AppKitDefinition`\nconstrain on the kit-owned structural `SchemaLike<Out>` —\n`{ parse(value: unknown): Out }` — and infer `AOut`/`IOut` from the\n`parse` return. A consumer whose zod **minor** differs from the kit's own\ntree (e.g. consumer pins `4.4.3`, kit tree resolves `4.5.4`) type-checks\nat the `defineAppConfig` call site with no cast and no pin bump; the\n`test-fixtures/consumer/` fixture proves exactly that (its own zod copy +\na `tsc --noEmit` contract test). Two consequences:\n\n- `jsonSchemas()` needs zod's introspection: a `SchemaLike` that is not a\n  zod schema (a hand-rolled `{ parse }`) makes `jsonSchemas()` /\n  `writeSchemas()` throw a clear `AppConfigError` instead of being a type\n  error — emit your JSON Schema yourself in that case.\n- `AppKit<AOut, IOut>` is parameterized by the **output** types\n  (`z.output<typeof Schema>`), not the schema types.\n","readmeFilename":"README.md"}