Development
npm install
npm run build # shared → core → cli → desktop
npm test # unit and integration tests (vitest)
npm run typecheck
npm run e2e -w @testpion/desktop # the UI regression suite in the real Electron app (apps/desktop/e2e/plans)
npm run dev # Electron
npm run dev:web # browser + local bridge
node examples/servers/demo-servers.mjs # local REST/GraphQL/LLM/WebSocket demo serversEvery feature ships with an implementation, unit tests, integration tests, documentation and an example. A UI feature also gets a plan in apps/desktop/e2e/plans/; a CLI command prints its --json output through the envelope (packages/cli/src/output.ts); a new MCP tool follows the tool contract (packages/core/src/mcp-server/tool-contract.ts: paging, response_format, confirm: true for deleting or overwriting) and is added to the pinned tool list in tests/integration/workspace-run.test.ts.
AI features
A new AI task is an entry in packages/core/src/ai/tasks/registry.ts (its prompt in prompts.ts; changing a prompt bumps the task's version) with golden cases in tests/ai/, which run with the mock provider in npm test: no key and no network. To see how the prompts do with a real model, run the same cases live (never in CI by default):
AI_EVAL_LIVE=1 TESTPION_AI_PROVIDER=ollama TESTPION_AI_MODEL=llama3.2 npx vitest run tests/ai # prints a scorecard per taskThe UI plans and the documentation screenshots use the offline mock with scripted answers (x-mock-rules on a mock provider: a regular expression on the last user message and the answer); see apps/desktop/e2e/plans/ai-flow-from-collection.cjs, ai-triage.cjs and apps/desktop/scripts/capture-steps.cjs (which also seeds a failed collection run and a flaky test's history for the triage and flaky shots). Runs for the triage and flaky golden cases are written with tests/ai/run-fixtures.ts. See AI features.
Hardening of the packaged app
electron-builder.yml sets Electron fuses in the installed binary: it can't be started as a plain Node.js runtime (ELECTRON_RUN_AS_NODE), doesn't read NODE_OPTIONS, ignores --inspect and only loads the app from its archive. Check a packaged build with npm run package:dir -w @testpion/desktop, then start release/win-unpacked/TestPion.exe.
The npm package
npm run pack:cli (scripts/pack-cli.mjs) builds the testpion npm package: the CLI with packages/core and packages/shared bundled by esbuild into one file, and the third-party modules the bundle imports as ordinary dependencies, with the version ranges the workspace packages ask for. It writes dist/npm/testpion/ and dist/npm/testpion-<version>.tgz; --json prints a summary.
The release workflow's Publish the CLI to npm job publishes the tarball with provenance when the repository has an NPM_TOKEN secret (an npm automation or granular token allowed to publish testpion). Without the secret it skips with a notice; a version already on npm is left as it is.
Code signing (not set up yet)
The installers are unsigned: Windows SmartScreen and macOS Gatekeeper warn on first launch, and updates are checked by their hash only. Signing needs certificates the project doesn't have yet. When it does:
- Windows: an OV/EV code-signing certificate, or Azure Trusted Signing. With a
.pfx, add the repository secretsWIN_CSC_LINK(the file, base64) andWIN_CSC_KEY_PASSWORDand pass them as environment variables to the Package step of.github/workflows/release.yml; electron-builder then signs the app and the installer. With Azure Trusted Signing, setwin.azureSignOptionsinelectron-builder.ymlinstead. - macOS: an Apple Developer ID Application certificate. Add
CSC_LINK(the.p12, base64),CSC_KEY_PASSWORD,APPLE_ID,APPLE_APP_SPECIFIC_PASSWORDandAPPLE_TEAM_IDas secrets and pass them to the Package step; inelectron-builder.ymlremoveidentity: "-", sethardenedRuntime: trueandnotarize: true. - In both cases remove
CSC_IDENTITY_AUTO_DISCOVERY: "false"from the workflow, and try it on a pre-release tag first. - Afterwards, drop the "isn't code-signed yet" notes from the Windows and macOS installation pages.
