Imported from everlastingcrown/paperclip-slack-plugin (
AGENTS.md). Install upstream withnpx skills add everlastingcrown/paperclip-slack-plugin. Copyright stays with the author.
AGENTS.md — Paperclip Slack Plugin
Commands
pnpm install # first time (requires Node >= 24, pnpm >= 10)
pnpm run build # esbuild + tsc → dist/
pnpm run dev # watch mode (esbuild only, no typecheck)
pnpm run test # vitest (19 tests, ~500ms)
pnpm run build:types # tsc typecheck only (strict failures here block CI)
Always build:types after changing signatures — esbuild won't catch type errors.
Paperclip SDK quirks
- Calendar versioning:
@paperclipai/plugin-sdkuses calver (2026.618.0). Peer dep is*, dev dep pins the exact version for type resolution. Checkpnpm view @paperclipai/plugin-sdk versionbefore bumping. PluginConfigValidationResult: uses{ ok: boolean; errors?: string[]; warnings?: string[] }. Errors are flat strings, NOT per-field objects. There is noPluginConfigValidationErrortype in the SDK.- All getters return
T | null:ctx.issues.get(),ctx.agents.get(),ctx.projects.get()returnT | null. Every call site needs a null guard. The type system enforces this strictly. ctx.state.get()returnsPromise<unknown>: Cast/convert after checking non-null before using as a specific type.runWorker(plugin, import.meta.url): last line ofsrc/worker.ts. Required for the worker to start when loaded as the main module by the Paperclip host. No-op in test environments, but the plugin silently fails without it.@paperclipai/plugin-sdkis externalized in esbuild:--external:@paperclipai/plugin-sdk— the Paperclip host provides it at runtime. Only@slack/web-apiis bundled intodist/worker.js.
Architecture notes
- Config is module-scoped cache (
src/config.ts). Event handlers callgetConfig()synchronously — noctx.config.get()on every event. Config is populated insetup()and refreshed inonConfigChanged(). - No native
issue.status_changedevent.src/events/issue-status.tssubscribes toissue.updated, stores previous status viactx.state.get/set(scopeKind"issue"), and compares. First-seen issues are silently skipped (no baseline notification). - Event registration is centralized:
src/events/index.ts→registerAllHandlers(ctx). Adding a new event means touching 4 files: a handler inevents/,types.ts(EventKey+EventsConfig),manifest.ts(JSON schema), andevents/index.ts(registration). - Two entrypoints must align:
package.jsonpaperclipPlugin.manifest/.workerfields andmanifest.tsentrypointsboth point todist/. Mismatches break plugin loading. .jsextension required on local imports:module: "nodenext"enforces explicit extensions. Omitting.json local imports fails at typecheck.
Release notes
- Conventional Commits enforced by semantic-release:
fix:→ patch,feat:→ minor,BREAKING CHANGE:/feat!:→ major. Non-conventional messages won't trigger releases. - CI merges to
maintriggersemantic-release: version bump → npm publish (needsNPM_TOKENsecret) → GitHub Release. - The
.releaserc.jsongit plugin only commitspackage.json; no other files are auto-committed.
Testing
tests/slack-client.test.tsmocks@slack/web-apiat module scope usingvi.mock. Mock factories reference module-scopedvi.fn()variables (hoisted by vitest). Don't nest mock setup insidedescribeblocks.tests/formatter.test.tsvalidates Block Kit structure. Expect exact block counts per event variant. Theactionsblock with "View in Paperclip" button is always the last block.