Skip to content

Contributing to DivineSWE

Building Platform Packages

DivineSWE supports four platforms. The canonical source targets claude-code; packages for other platforms are generated from it.

Generate a Single Platform

Terminal window
npm run generate -- --platform copilot-cli

Output lands in the mapping-selected directory: committed copilot/ or codex/ for marketplace packages, and dist/<platform>/ for other targets.

Generate All Platforms

Terminal window
npm run generate:all

See docs/platform-portability.md for the full generator architecture, platform mapping schema, and instructions for adding new platform targets.

Installing Platform Bundles into a Target Project

The install-platform.mjs script automates the generate → verify → stage → promote pipeline for any installable platform. By default, platforms install globally (e.g. ~/.config/opencode/); use --per-project to opt out.

Terminal window
npm run install:copilot-cli # Install copilot-cli bundle globally
npm run install:all # Install all installable platforms globally
npm run install:interactive # Interactive platform selection and target confirmation

To install into the current project directory instead of the global default:

Terminal window
node scripts/install-platform.mjs install --platform copilot-cli --per-project --yes

To overwrite existing files:

Terminal window
node scripts/install-platform.mjs install --platform copilot-cli --force --yes

To install to a specific directory:

Terminal window
node scripts/install-platform.mjs install --platform copilot-cli --target /path/to/project --yes

To install all platforms at once:

Terminal window
node scripts/install-platform.mjs install --all --yes

opencode is not a file-copy install target. It is delivered as an npm plugin (the divineswe npm package). The installer rejects --platform opencode.

See docs/platform-portability.md for the full flag reference, three-state file model, atomicity guarantees, and marker-based target resolution.

Publishing the npm Package (Maintainers)

The unscoped divineswe package is published manually:

Terminal window
npm run generate:all # regenerate dist/opencode/ (also runs automatically via prepack)
npm pack --dry-run # inspect the tarball file list before shipping
npm publish # publish the divineswe package to npm

prepack runs npm run generate:all on both npm pack and npm publish, so the shipped dist/opencode/ payload is always freshly generated from the canonical source.

Adding a New Platform

  1. Create platforms/<new-platform>.json following the schema.
  2. Optionally create platform-specific hooks in hooks/<new-platform>/.
  3. Optionally create skill variants as skills/<skill>/SKILL.<new-platform>.md.
  4. Run node scripts/generate-platform.mjs --platform <new-platform> to test.
  5. Add integration tests in tests/integration/platform-<new-platform>.test.ts.

Version Bumps

When updating the version number, all three files must be updated together:

  1. package.json — canonical version source
  2. .claude-plugin/plugin.json — plugin manifest version
  3. package-lock.json — run npm install after updating package.json to sync

Tests in tests/plugin/plugin-manifest-adversarial.test.ts enforce that these three files agree. A stale package-lock.json is the most common drift — always regenerate it after a version bump.

Version bump release checklist

Beyond the three-file sync above, a version bump touches two more places that do not derive the version automatically:

  1. EXPECTED-literal gotcha: a version bump must ALSO update the hardcoded EXPECTED literal and the should_report_<ver> test names in tests/structural/book71-pipeline-adversarial.test.ts — that guard does NOT derive the version from package.json, so a bump that skips it leaves a stale-version assertion silently passing against the wrong string.
  2. copilot-content-commit gotcha (Book 79): a prophecy that runs npm run generate:all must COMMIT the regenerated copilot/ CONTENT (agents/skills/scripts, not just the version fields) plus both marketplace.json versions, because tests/integration/four-platform-determinism.test.ts compares the committed copilot/ against a fresh regen — a partial (version-only) commit leaves stale content and FAILS determinism on a clean checkout. Mason’s selective-add for a version-bump prophecy must include any regenerated copilot/ content plus any Loki-Break sibling adversarial test files — both have been left behind by an over-narrow add.

Git Practices

  • Use git mv for file renames to preserve history
  • Do not include Co-Authored-By lines in commits
  • Commit message format: Book N: <summary>

Test Conventions

  • Runner: npx vitest run (vitest, NOT pytest)
  • Test naming: should_<behavior>_when_<condition> with underscores
  • Tests read AGENT.md/SKILL.md content and assert regex patterns against it
  • Generated-artifact tests must assert REAL loadability — TOML parseability, shell-command validity, hook command-path existence — NOT just token/regex presence

See docs/testing-guide.md for the full idioms: real-loadability corollaries, doc-as-contract guards, the boot-smoke contract, and the doc-relocation test idiom.

Website (site/)

The site/ subtree is a self-contained Astro + Starlight static site (cinematic landing + /docs) served from the root of its own subdomain (https://divineswe.aidragon.io, Astro base: '/', root-relative links). It has its own vitest runner — run site tests via cd site && npm run test, never the repo-root npx vitest run (the root config excludes site/). site/ is excluded from the npm tarball and invisible to plugin loading; whole-repo structural test walkers must keep 'site' in their SKIP sets so recursive copies never traverse site/node_modules.

  • Docs sync: site/scripts/sync-docs.mjs publishes an allow-list of repo docs/ stems plus root CHANGELOG.md (H1 → title frontmatter; rewrites relative .md cross-links so nothing 404s or leaks internal-doc names; the allow-list gates files, NOT link contents). The sync console count derives from ALLOW_LIST.length + 1 — never a literal.
  • Sidebar: two groups — Initiation (Overview / Quick Start / Installation / Workflows) and The Scriptures (Architecture / Agents / Skills / Configuration / State Model / Testing Guide / Vows — Consumer Contract / Contributing / Changelog). Every sidebar slug must resolve to an allow-list-derived synced route (Overview points at docs/overview, not docs).
  • Drift guards: install commands in synced quickstart.md/installation.md stay byte-exact to README; site/src/content/docs/index.md is a frozen landing-adversarial source-scan placeholder — never edit it; the site/tests/build-smoke.test.ts ALLOW_LIST_SLUGS canary is a separate hardcoded list — when the sync allow-list grows, update it too.
  • Reference docs are de-booked: architecture, configuration, state-model, and testing-guide are evergreen prose with no numbered Book N dev-log tags (CHANGELOG exempt; guard tests/structural/book78-debook.test.ts).
  • Build & deploy: the build-smoke is env-gated on DIVINESWE_SITE_E2E. Deployment is via .gitlab-ci.yml (build → SFTP-mirror site/dist via lftp using masked SFTP_HOST/SFTP_USER/SFTP_PASSWORD/SFTP_REMOTE_DIR CI variables). The gitlab.com remote is the source, decoupled from the deploy origin; gitlab.com source links are preserved across the site.
  • Gotcha: astro dev skips the sync-docs prebuild — run the sync manually when previewing docs changes.

The Offering Acceptance Policy (Book 88)

An Offering is a screened, instance-discovered finding that a downstream DivineSWE instance hands back to the maintainers — either a proposed law (in laws-register format) or a script-opportunity sighting. Instances stage and screen Offerings locally via /divineswe:offering (scripts/offering.mjs) and deliver them by an explicit human action — a prefilled GitLab new-issue URL, or Service Desk email for account-less contributors. There is no telemetry, no phone-home, and no instance-side auto-submission: the human always sends. This section governs what happens after an Offering arrives — how a maintainer triages it and canonizes the accepted ones.

Dedup authority is maintainer-side

The instance-side lint runs an advisory self-dedup against its own local audit log only — it cannot see the upstream backlog (the docs are not shipped, and the tool has no network reach). Maintainer-side PR/issue review is the authoritative dedup. Only a maintainer, reading the incoming Offering against docs/honor-system-rules.md (the laws register) and docs/script-opportunities.md (its stable SO-NNN namespace), decides whether an Offering is a genuinely new finding, a refinement of an existing entry, or a duplicate to close. No automated check — upstream or downstream — is ever that decision.

Label taxonomy

Triage every incoming Offering with exactly one disposition label and one kind label.

Kind — where the Offering wants to land:

LabelMeaningCanonical home when accepted
offering:lawA proposed law / honor-system rule.docs/honor-system-rules.md (the laws register)
offering:opportunityA script-opportunity sighting (agent hand-executing an exact procedure).docs/script-opportunities.md, a new SO-NNN entry

Disposition — the triage verdict:

LabelMeaning
offering:acceptedNew, in-scope, and worth canonizing — proceed to the canonization step.
offering:refinedOverlaps an existing entry; fold the new signal into that entry rather than adding a duplicate.
offering:duplicateAlready covered by an existing law or SO-NNN; close with a pointer to the canonical entry.
offering:declinedOut of scope or contrary to a Denied decision (e.g. telemetry, auto-submission); close with the rationale.
offering:deferredValid but parked behind a volume/again trigger (e.g. a shipped upstream snapshot); record the trigger.

Canonization

An offering:accepted finding is canonized into its canonical home:

  • offering:law → add the rule to docs/honor-system-rules.md in laws-register format, naming the enforcement mechanism that gives it teeth (the register does not trust prose to guard itself), and wire the guarding test/hook the rule claims.
  • offering:opportunity → append a new, allocation-ordered SO-NNN entry to docs/script-opportunities.md, classified against the three-gate decision rule (MECHANICAL / HYBRID / JUDGMENT) whose canonical home is skills/avatar-forge/axioms.md. IDs are opaque, never reused, never renumbered — the SO-NNN namespace is the dedup anchor for future Offerings.

An offering:refined finding is merged into the existing law or SO-NNN entry; offering:duplicate / offering:declined / offering:deferred Offerings are closed with a rationale (and, for deferrals, the recorded re-trigger).