{"_id":"@bernierllc/onboarding-feature-plugin","_rev":"3-a275244ba597d629f5a4fb822a95ebe2","name":"@bernierllc/onboarding-feature-plugin","dist-tags":{"latest":"0.2.1"},"versions":{"0.0.1":{"name":"@bernierllc/onboarding-feature-plugin","version":"0.0.1","keywords":["oidc","trusted-publishing","setup"],"_id":"@bernierllc/onboarding-feature-plugin@0.0.1","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"dist":{"shasum":"f2390767466255a0c9e80d7094589635678921d7","tarball":"https://registry.npmjs.org/@bernierllc/onboarding-feature-plugin/-/onboarding-feature-plugin-0.0.1.tgz","fileCount":2,"integrity":"sha512-5x9cdW4bc6xAQFzo0K7uMTFM/IdUksXZ/EBbAsF7XtAqI+0v+U4+dAquMsIEJGZRUApvknem0f6DoqQLoDeKpw==","signatures":[{"sig":"MEYCIQD3Wi1BwymuNLYueWDi6mHHLxE6ani4J6xsKkqBaOJo3AIhAJm2UjdSIzzd2Yiory/oEJMj2dP/o9BT/hj4j0GnrX6u","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2111},"_npmUser":{"name":"mkbernier","email":"mkbernier@gmail.com"},"_npmVersion":"11.12.1","description":"OIDC trusted publishing setup package for @bernierllc/onboarding-feature-plugin","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/onboarding-feature-plugin_0.0.1_1781634232382_0.39602708794256625","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bernierllc/onboarding-feature-plugin","version":"0.2.0","keywords":["onboarding","plugin","registry","ai","agent","runtime","bernierllc"],"author":{"name":"Bernier LLC"},"license":"Bernier LLC","_id":"@bernierllc/onboarding-feature-plugin@0.2.0","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"homepage":"https://github.com/bernierllc/tools#readme","bugs":{"url":"https://github.com/bernierllc/tools/issues"},"dist":{"shasum":"81fa15617c772e9f3f0f2acc78eac1b87d0c8b2e","tarball":"https://registry.npmjs.org/@bernierllc/onboarding-feature-plugin/-/onboarding-feature-plugin-0.2.0.tgz","fileCount":23,"integrity":"sha512-8Q9p9pzPgzXirlA4zI1I1CVp08Da/2hqi2GdIZ9KAgPn52dZVxhki89GIjVIZF+yNeZKjI+nH8veOdM77ySUHw==","signatures":[{"sig":"MEYCIQC06JRBhwdEVMokhzWZi6SlGqzD2sp+/BvtbglNpq6P3gIhALXwOnzp/puOHGCacxYs38kqgpJ3/1G/ijia4e6BfGw0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45649},"main":"dist/index.js","_from":"file:bernierllc-onboarding-feature-plugin-0.2.0.tgz","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"scripts":{"lint":"eslint src/**/*.ts","test":"jest","build":"tsc","clean":"rm -rf dist","prebuild":"npm run clean","test:run":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:99b4dbab-2faa-47e7-bba9-4752f34c1c82"}},"_resolved":"/home/runner/work/tools/tools/packages/core/onboarding-feature-plugin/bernierllc-onboarding-feature-plugin-0.2.0.tgz","_integrity":"sha512-8Q9p9pzPgzXirlA4zI1I1CVp08Da/2hqi2GdIZ9KAgPn52dZVxhki89GIjVIZF+yNeZKjI+nH8veOdM77ySUHw==","repository":{"url":"git+https://github.com/bernierllc/tools.git","type":"git","directory":"packages/core/onboarding-feature-plugin"},"_npmVersion":"11.17.0","description":"Plugin contract and registry for extending onboarding agents with goals, tools, side-panel slots, and handoffs.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"@bernierllc/logger":"1.7.0","@bernierllc/schema-validator":"3.0.0","@bernierllc/onboarding-config-core":"0.2.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.1.2","typescript":"^5.3.3","@types/jest":"^29.5.12","@types/node":"^20.11.19"},"_npmOperationalInternal":{"tmp":"tmp/onboarding-feature-plugin_0.2.0_1781801858371_0.31698777401857714","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@bernierllc/onboarding-feature-plugin","version":"0.2.1","description":"Plugin contract and registry for extending onboarding agents with goals, tools, side-panel slots, and handoffs.","main":"dist/index.js","types":"dist/index.d.ts","keywords":["onboarding","plugin","registry","ai","agent","runtime","bernierllc"],"author":{"name":"Bernier LLC"},"license":"Bernier LLC","repository":{"type":"git","url":"git+https://github.com/bernierllc/tools.git","directory":"packages/core/onboarding-feature-plugin"},"dependencies":{"@bernierllc/logger":"1.7.1","@bernierllc/onboarding-config-core":"0.2.1","@bernierllc/schema-validator":"3.0.0"},"devDependencies":{"@types/jest":"^29.5.12","@types/node":"^20.11.19","eslint":"^8.0.0","jest":"^29.7.0","rimraf":"^5.0.0","ts-jest":"^29.1.2","typescript":"^5.3.3"},"engines":{"node":">=18.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build":"tsc","test":"jest","test:watch":"jest --watch","test:run":"jest","test:coverage":"jest --coverage","lint":"eslint src/**/*.ts","clean":"rm -rf dist","prebuild":"npm run clean"},"_id":"@bernierllc/onboarding-feature-plugin@0.2.1","bugs":{"url":"https://github.com/bernierllc/tools/issues"},"homepage":"https://github.com/bernierllc/tools#readme","_integrity":"sha512-a9XOcpU5z2TjnAnK9EgOw7/6By5tFgW36SBIGNhwAs74nVuJSh/OfTwTtvXFpyAkr+I79P1OuVA0Rvx69+WFUw==","_resolved":"/home/runner/work/tools/tools/packages/core/onboarding-feature-plugin/bernierllc-onboarding-feature-plugin-0.2.1.tgz","_from":"file:bernierllc-onboarding-feature-plugin-0.2.1.tgz","_nodeVersion":"24.18.0","_npmVersion":"12.0.1","dist":{"integrity":"sha512-a9XOcpU5z2TjnAnK9EgOw7/6By5tFgW36SBIGNhwAs74nVuJSh/OfTwTtvXFpyAkr+I79P1OuVA0Rvx69+WFUw==","shasum":"d8b579e3aa73d81aa2ce205e46bbf65c8542283f","tarball":"https://registry.npmjs.org/@bernierllc/onboarding-feature-plugin/-/onboarding-feature-plugin-0.2.1.tgz","fileCount":23,"unpackedSize":45649,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB0n4QO51d7JrqEmlXNtq7zD9xh+8nBFTHwDmCcuzT3sAiBrOGsCIHLoMPqNu+1Vw4xpSuWAyhg68TPPG7a/kYAGwQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:99b4dbab-2faa-47e7-bba9-4752f34c1c82"}},"directories":{},"maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/onboarding-feature-plugin_0.2.1_1784015673321_0.5389294403230211"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-16T18:23:52.187Z","modified":"2026-07-14T07:54:33.576Z","0.0.1":"2026-06-16T18:23:52.538Z","0.2.0":"2026-06-18T16:57:38.514Z","0.2.1":"2026-07-14T07:54:33.454Z"},"bugs":{"url":"https://github.com/bernierllc/tools/issues"},"author":{"name":"Bernier LLC"},"license":"Bernier LLC","homepage":"https://github.com/bernierllc/tools#readme","keywords":["onboarding","plugin","registry","ai","agent","runtime","bernierllc"],"repository":{"type":"git","url":"git+https://github.com/bernierllc/tools.git","directory":"packages/core/onboarding-feature-plugin"},"description":"Plugin contract and registry for extending onboarding agents with goals, tools, side-panel slots, and handoffs.","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"readme":"# @bernierllc/onboarding-feature-plugin\n\nPlugin contract and registry for extending onboarding agents with additional goals, questions, tools, side-panel UI slots, and post-completion handoffs.\n\n## Overview\n\nThis package owns the **extension contract and merge logic** for the BernierLLC onboarding system. Concrete plugins (e.g. `@bernierllc/onboarding-plugin-website-builder`) live in their own packages and depend on this contract.\n\nKey capabilities:\n\n- **`OnboardingPlugin` interface** — the complete plugin contract every author must implement\n- **`PluginRegistry`** — in-memory registry with duplicate-id detection\n- **`mergePluginContributions()`** — deterministic merge of goals, phases, tools, panels, and handoffs into a base `OnboardingConfig`\n- **Bulk collision detection** — reports all id/name conflicts at once, not just the first\n- **Ordering rules** — deterministic phase and handoff ordering with warning logging for inter-base-phase slots\n\n## Installation\n\n```bash\nnpm install @bernierllc/onboarding-feature-plugin\n```\n\n## Usage\n\n### Registering and using a plugin\n\n```typescript\nimport {\n  PluginRegistry,\n  mergePluginContributions,\n  OnboardingPluginError,\n  OnboardingPluginConflictError,\n} from '@bernierllc/onboarding-feature-plugin';\nimport type {\n  OnboardingPlugin,\n  PluginContext,\n  OnboardingGoal,\n  PluginToolDefinition,\n  PluginPanelDescriptor,\n  HandoffTarget,\n  MergedConfig,\n} from '@bernierllc/onboarding-feature-plugin';\n\n// 1. Define a plugin (typically imported from a concrete plugin package)\nconst websiteBuilderPlugin: OnboardingPlugin = {\n  id: 'website-builder',\n  name: 'Website Builder Plugin',\n  version: '1.0.0',\n\n  configSchema: {\n    type: 'object',\n    properties: { siteTemplate: { type: 'string' } },\n    required: ['siteTemplate'],\n  },\n\n  validateConfig(config: unknown): asserts config is Record<string, unknown> {\n    if (\n      typeof config !== 'object' ||\n      config === null ||\n      typeof (config as Record<string, unknown>)['siteTemplate'] !== 'string'\n    ) {\n      throw new OnboardingPluginError('siteTemplate (string) is required', {\n        code: 'INVALID_PLUGIN_CONFIG',\n      });\n    }\n  },\n\n  contributeGoals(_ctx: PluginContext): OnboardingGoal[] {\n    return [\n      {\n        id: 'website-builder:domain-chosen',\n        description: 'Domain preference captured',\n        required: false,\n        phase: 'getting-specific',\n        completionCheck: { type: 'field-present', field: 'preferredDomain' },\n      },\n    ];\n  },\n\n  contributePhases: () => [],\n\n  contributeTools(_ctx: PluginContext): PluginToolDefinition[] {\n    return [\n      {\n        name: 'previewWebsite',\n        description: 'Generate a live website preview',\n        parameters: {\n          type: 'object',\n          properties: { template: { type: 'string' } },\n          required: ['template'],\n        },\n        requiredPermissions: ['website:preview'],\n      },\n    ];\n  },\n\n  contributePanel(_ctx: PluginContext): PluginPanelDescriptor | null {\n    return {\n      slotId: 'website-builder-preview',\n      label: 'Website Preview',\n      triggerToolNames: ['previewWebsite'],\n      metadata: { position: 'right' },\n    };\n  },\n\n  contributeHandoffs(\n    _ctx: PluginContext,\n    state: Record<string, unknown>\n  ): HandoffTarget[] {\n    if (!state['sitePublished']) return [];\n    return [\n      {\n        id: 'website-builder:view-site',\n        type: 'external',\n        label: 'View your live site',\n        order: 10,\n      },\n    ];\n  },\n};\n\n// 2. Register the plugin\nconst registry = new PluginRegistry();\nregistry.register(websiteBuilderPlugin);\n\n// 3. Validate per-instance config before merge\nregistry.validatePluginConfig('website-builder', {\n  siteTemplate: 'contractor-default',\n});\n\n// 4. Merge into a base config\nconst merged: MergedConfig = mergePluginContributions(\n  baseConfig,\n  [{ plugin: websiteBuilderPlugin, config: { siteTemplate: 'contractor-default' } }],\n  currentSessionState // optional — defaults to {}\n);\n\n// merged.config   — OnboardingConfig with combined goals, phases, handoffs\n// merged.tools    — PluginToolDefinition[] to register with agent-tool-registry\n// merged.panels   — PluginPanelDescriptor[] to pass to the UI host\n// merged.pluginIds — ['website-builder'] — registration order for auditability\n```\n\n### Handling multiple plugins\n\n```typescript\nimport { PluginRegistry, mergePluginContributions } from '@bernierllc/onboarding-feature-plugin';\n\nconst registry = new PluginRegistry();\nregistry.register(websiteBuilderPlugin);\nregistry.register(calendarPlugin);\n\nconsole.log(registry.listRegistered()); // ['website-builder', 'calendar']\n\nconst merged = mergePluginContributions(\n  baseConfig,\n  [\n    { plugin: websiteBuilderPlugin, config: { siteTemplate: 'default' } },\n    { plugin: calendarPlugin, config: { timezone: 'America/New_York' } },\n  ]\n);\n```\n\n### Error handling\n\n```typescript\nimport {\n  OnboardingPluginError,\n  OnboardingPluginConflictError,\n} from '@bernierllc/onboarding-feature-plugin';\n\n// Duplicate registration\ntry {\n  registry.register(websiteBuilderPlugin); // already registered\n} catch (err) {\n  if (err instanceof OnboardingPluginError) {\n    console.error(err.code);    // 'PLUGIN_ALREADY_REGISTERED'\n    console.error(err.context); // { pluginId: 'website-builder' }\n  }\n}\n\n// Config validation failure\ntry {\n  registry.validatePluginConfig('website-builder', { siteTemplate: 42 });\n} catch (err) {\n  if (err instanceof OnboardingPluginError) {\n    console.error(err.code);  // 'PLUGIN_CONFIG_INVALID'\n    console.error(err.cause); // original error from plugin.validateConfig\n  }\n}\n\n// Id collision during merge (all conflicts reported at once)\ntry {\n  mergePluginContributions(baseConfig, [pluginWithDuplicateGoalId]);\n} catch (err) {\n  if (err instanceof OnboardingPluginConflictError) {\n    console.error(err.conflicts); // ['goal:goal-name', 'tool:sharedTool']\n  }\n}\n```\n\n## API\n\n### `PluginRegistry`\n\n```typescript\nclass PluginRegistry {\n  constructor(logger?: Logger)\n\n  /** Registers a plugin. Throws OnboardingPluginError if id is already registered. */\n  register(plugin: OnboardingPlugin): void\n\n  /** Returns the plugin for the given id, or undefined if not found. */\n  resolve(id: string): OnboardingPlugin | undefined\n\n  /** Validates per-instance config via the plugin's own validateConfig. Wraps errors with plugin context. */\n  validatePluginConfig(id: string, config: unknown): void\n\n  /** Returns registered plugin ids in registration order. */\n  listRegistered(): string[]\n}\n```\n\n### `mergePluginContributions()`\n\n```typescript\nfunction mergePluginContributions(\n  base: OnboardingConfig,\n  plugins: ReadonlyArray<{ plugin: OnboardingPlugin; config: unknown }>,\n  state?: Record<string, unknown>,\n  logger?: Logger\n): MergedConfig\n```\n\nDeterministic merge algorithm:\n\n| Item | Strategy |\n|------|----------|\n| **Goals** | Appended after base goals in plugin registration order |\n| **Phases** | Appended, then sorted by `order`; inter-base-phase slots logged as warnings |\n| **Handoffs** | Merged, sorted by `order`; ties broken by registration order (stable sort) |\n| **Tools** | Concatenated in registration order |\n| **Panels** | Concatenated — each plugin may contribute one or zero panels |\n\nAll id/name collisions (goals, phases, handoffs, tools) are collected before throwing `OnboardingPluginConflictError`, so callers see the complete picture.\n\n### `OnboardingPlugin` interface\n\n```typescript\ninterface OnboardingPlugin {\n  id: string;\n  name: string;\n  version: string;\n  configSchema: Record<string, unknown>;\n\n  validateConfig(config: unknown): asserts config is Record<string, unknown>;\n  contributeGoals(context: PluginContext): OnboardingGoal[];\n  contributePhases(context: PluginContext): OnboardingPhase[];\n  contributeTools(context: PluginContext): PluginToolDefinition[];\n  contributePanel?(context: PluginContext): PluginPanelDescriptor | null;\n  contributeHandoffs(context: PluginContext, state: Record<string, unknown>): HandoffTarget[];\n}\n```\n\n### Error classes\n\n| Class | Code | When thrown |\n|-------|------|-------------|\n| `OnboardingPluginError` | `ONBOARDING_PLUGIN_ERROR` | Base error for all plugin-related failures |\n| `OnboardingPluginError` | `PLUGIN_ALREADY_REGISTERED` | Duplicate id on `register()` |\n| `OnboardingPluginError` | `PLUGIN_NOT_FOUND` | Unknown id on `validatePluginConfig()` |\n| `OnboardingPluginError` | `PLUGIN_CONFIG_INVALID` | Plugin's `validateConfig` throws |\n| `OnboardingPluginConflictError` | `ONBOARDING_PLUGIN_CONFLICT` | Id/name collision during merge |\n\nAll errors follow ES2022 `Error.cause` chaining — underlying errors are never swallowed.\n\n## Integration Documentation\n\n### Logger integration\n\n`PluginRegistry` and `mergePluginContributions` accept an optional `@bernierllc/logger` `Logger` instance. When omitted, a silent no-transport logger is created internally so the package produces no console output by default.\n\n```typescript\nimport { Logger, LogLevel, ConsoleTransport } from '@bernierllc/logger';\nimport { PluginRegistry } from '@bernierllc/onboarding-feature-plugin';\n\nconst logger = new Logger({\n  level: LogLevel.DEBUG,\n  transports: [new ConsoleTransport()],\n});\nconst registry = new PluginRegistry(logger);\n```\n\nLogged events:\n- `DEBUG` — plugin registered, merge summary\n- `WARN` — plugin phase order slots between base phases\n\n### NeverHub integration\n\nThis is a **pure core package** with no I/O, no React, and no NeverHub dependency. NeverHub integration is handled at the service layer (`onboarding-agent-service`) which receives the `MergedConfig` output and can register it with `@bernierllc/neverhub-adapter`. Graceful degradation applies automatically — core functionality works regardless of NeverHub availability.\n\n### Integration flow\n\n```\nonboarding-agent-service\n  ↓ registry.resolve(id) + registry.validatePluginConfig(id, pluginConfig[id])\n  ↓ mergePluginContributions(baseConfig, enabledPlugins, currentState)\n  → MergedConfig\n      .config  → onboarding-config-core.compileSystemPrompt()\n      .tools   → agent-tool-registry (registered by service layer)\n      .panels  → onboarding-chat-ui (host panel mapping)\n```\n\n### Writing a plugin package\n\n1. Depend on `@bernierllc/onboarding-feature-plugin`\n2. Implement `OnboardingPlugin` — give your plugin a stable, namespaced `id` (e.g. `'my-org:feature-name'`)\n3. Export the plugin object or a factory function\n4. The consuming service registers it: `registry.register(myPlugin)`\n\n## License\n\nCopyright (c) 2025 Bernier LLC. See LICENSE for details.\n","readmeFilename":"README.md"}