Thank you for your interest in contributing to substack-cli! This project converts local Markdown files into Substack drafts and publishes them via browser automation or the internal Substack API.
- Node.js >= 18.0.0
- npm
- A Git client
git clone https://github.com/edithatogo/substack-cli-ts
cd substack-cli-ts
npm install
npm run buildCopy the environment template:
Copy-Item .env.example .envConfigure at minimum SUBSTACK_PUBLICATION_URL. See .env.example for all available options.
npm run typecheck # TypeScript type checking
npm test # Run unit tests
npm run quality # Full quality gate: format:check + lint + typecheck + build + test:coverage
npm run lint # ESLint
npm run format:check # Prettier formatting check- TypeScript strict mode is enabled — all strict checks apply.
- ESLint enforces code quality rules. Run
npm run lint:fixto auto-fix issues. - Prettier handles formatting. Run
npm run formatto format all files. - Use explicit module boundaries — keep command handlers thin and delegate logic to separate modules.
- Use ESM imports (
import/export), not CommonJS. - No barrel files (
index.tsthat re-export) — import directly from the module. - Shell scripts should quote variable expansions and use small, explicit setup steps.
- Do not commit
.env,config/master.key,.substack-cli/, browser storage-state files, credentials, traces, screenshots, or generated dependency directories.
- Create or reuse a handler function in the relevant module (e.g.,
src/substack-api/,src/publish/). - Add the command definition to
src/cli.tsusing thecommanderAPI. - Document the command and its options in
docs/api/commands.md. - Add tests for the handler logic.
- Run
npm run typecheck && npm testto verify. - If the command produces output that should be fixture-tested, capture a fixture via the
schema captureworkflow.
- Define the node extension in
src/parser/following the existing Tiptap extension pattern. - Register it in the Tiptap editor configuration in
src/parser/. - Add the corresponding HTML-to-ProseMirror conversion logic.
- Add test cases that exercise the new node through the
inspectcommand. - Capture updated fixtures with
schema capture.
Fixtures are used to lock down parser output and browser workflow traces:
node dist/cli.js schema capture examples/basic.md --out fixtures/prosemirror/basic.json
node dist/cli.js trace fixture trace-artifact.json --out fixtures/workflow/publish.jsonAfter updating parser or browser logic, regenerate relevant fixtures and verify with schema compare.
- Create a feature branch from
main. - Make your changes following the code style above.
- Run
npm run qualityto ensure all checks pass. - If your change affects parser output, regenerate fixtures.
- Open a pull request with a concise summary of the change, what commands you ran to validate, and any notes about environment variables, browser automation, generated payloads, or secret handling.
- The PR title should be a short imperative statement (e.g., "Add API transport for draft scheduling").
Open an issue on GitHub. Include the version, your environment, reproduction steps, and any relevant trace or error output.
Major design decisions are documented as ADRs in docs/decisions/:
0001-quality-toolchain.md— CI and quality tooling0002-transport-strategy.md— Dual-transport design (browser + API)0003-reusable-module-boundaries.md— Module structure0004-e2e-testing.md— End-to-end testing strategy
See Track 14 (Quality, CI, and Automation) for details on the CI pipeline, including linting, type checking, testing, coverage, mutation testing, and secret scanning.
This project follows the Contributor Covenant. By participating, you agree to uphold its standards.