Contributing to SiftCoder¶
Development setup¶
Requirements: - Node ≥ 20 - Optional: Ollama for local-LLM development
Repo layout¶
See ARCHITECTURE.md §4. Quick map:
src/ TypeScript core (memory engine, LLM clients, utils)
hooks/ .mjs hook scripts
skills/ SKILL.md per skill
agents/ .md per agent
commands/ .md per slash command
monitors/ .mjs monitor scripts
bin/ CLI entry
scripts/ install / setup helpers
docs/ hand-curated documentation
tests/ cross-module integration tests
Testing¶
Vitest is the runner. Coverage gates are enforced in CI.
Targets: - 90% lines / 90% statements / 90% functions - 85% branches
Coverage exclusions (see vitest.config.ts): I/O entry points (daemon/index.ts, mcp/server.ts), CLI binaries, web static assets.
Coding standards¶
- TypeScript strict mode (
tsconfig.jsonis the contract) - ESLint + Prettier —
npm run lintandnpm run formatbefore pushing - ES modules everywhere (
"type": "module") - No default exports — named exports only
- No global state — pass dependencies explicitly
- Async-first — no sync IO in async paths
- Errors are typed (
src/core/errors.ts); throw classes, not strings - Public functions need explicit return types
How to add a skill¶
- Create
skills/<name>/SKILL.mdwith frontmatter:
- Body is markdown — rules, patterns, anti-patterns. No code other than examples.
- Skills are auto-discovered via the plugin manifest. No registration needed.
- Test the skill manually:
/skill <name>in Claude Code.
How to add an agent¶
- Create
agents/<name>.mdwith frontmatter:
---
name: <name>
description: Use when... [single trigger sentence]
tools: Read, Edit, Bash, Grep, Glob
model: sonnet
---
- Body is the agent's system prompt. Be specific:
- Method — numbered steps
- Output — exact shape expected
-
Rules — hard constraints (read-only? scope limits?)
-
Single-purpose. If the agent has two jobs, split it.
- Read-only by default; only grant Edit/Write if the job requires it.
How to add a hook¶
Hooks are heavyweight — they fire on every matching event. Default to NO. Only add a hook when:
- It captures something you can't get any other way (memory, incident)
- Its budget is < 2s
- Failure is silent and never blocks the user
Steps:
- Create
hooks/<event>/<name>.mjs. Pure ES module. - Read the event payload from stdin (JSON), exit code controls behaviour:
0— pass2— block (only PreToolUse / PreCompact)- Wrap in try/catch — never crash on bad input.
- Document the hook's contract at the top of the file (event, matcher, side effects, failure mode).
- Register in
hooks/hooks.jsonwith an explicit timeout. - Add a test in
tests/hooks/<name>.test.ts.
Commit style¶
Conventional commits:
feat(memory): add provenance pruning
fix(hooks): handle missing scope.json gracefully
docs(readme): clarify Ollama setup
chore(deps): bump vitest 2.1
Pull requests¶
- One concern per PR
- All CI green (lint, typecheck, test, coverage)
- Update
CHANGELOG.mdunder## Unreleased - Reference issue if applicable
Releases¶
Maintainers only. Steps:
- Update
CHANGELOG.md— moveUnreleasedto a new version section - Bump
package.jsonversion - Tag:
git tag v1.x.y && git push --tags - GitHub Action publishes to npm and creates GitHub release
Code of conduct¶
Be kind, be specific, be evidence-based. No bikeshedding.