Desktop-first personal command center built with Electron.
srgnt/
├── packages/
│ ├── tsconfig/ # Shared TypeScript configs
│ ├── contracts/ # Zod schemas and entity definitions
│ ├── runtime/ # Local-first runtime, storage, approvals
│ ├── desktop/ # Electron main/preload/renderer
│ ├── connectors/ # Jira, Outlook Calendar, Teams SDK
│ ├── executors/ # Skill executor contracts
│ ├── sync/ # Sync engine for workspace continuity
│ ├── entitlements/ # Premium/Fred entitlement separation
│ └── fred/ # Fred premium orchestration layer
├── examples/
│ ├── skills/
│ │ └── daily-briefing/ # Example skill package validated against shared contracts
│ └── connectors/
│ └── jira/ # Example connector package validated against shared contracts
├── .agent-vault/ # Durable roadmap, phase, and architecture notes
└── TESTING.md # Manual verification guide and known limitations
This project uses pnpm as the package manager (see DEC-0005).
# Install dependencies
pnpm install
# Type check all packages
pnpm typecheck
# Run tests
pnpm test
# Build all workspaces
pnpm build
# Lint all packages
pnpm lint
# Run the desktop Electron E2E suite
pnpm test:e2e
# Smoke test the packaged Linux desktop build
pnpm test:e2e:packaged:linuxThe desktop app now has a dedicated Desktop E2E GitHub Actions workflow that runs on Linux and validates both:
- the Electron end-to-end suite against the built app
- a packaged Linux smoke test against the unpacked desktop bundle
For local details and the full desktop testing workflow, see TESTING.md.
Third-party connectors are distributed as isolated packages that the desktop app installs via CLI, verifies with sha256, and loads in a worker/subprocess runtime (see DEC-0016). The public surface lives in @srgnt/connectors (SDK) and @srgnt/contracts (schemas). A working reference is examples/connectors/jira/.
Every connector package must export one ConnectorPackage object from its entrypoint:
import type { ConnectorPackage } from '@srgnt/connectors';
export const connectorPackage: ConnectorPackage = {
manifest, // ConnectorManifest — identity, auth, capabilities, entity types
runtime, // ConnectorPackageRuntime — sdkVersion, host range, capabilities, executionModel
factory, // (host: HostContext) => Promise<Connector>
};manifest: validated bySConnectorManifest. Declaresid,version,provider,authType, capability matrix, andentityTypes.runtime: validated bySConnectorPackageRuntime. Must declaresdkVersion,minHostVersion,entrypoint,capabilities(subset ofhttp.fetch,logger,crypto.randomUUID,workspace.root), andexecutionModel: 'worker' | 'subprocess'. Third-party packages may not run in the Electron main process.factory: receives aHostContext(boundedHostCapabilities+connectorId+sdkVersion) and returns aConnector. Typically extendsBaseConnectororSyncableConnectorfrom the SDK.
-
Scaffold the package. Use
examples/connectors/jira/as a template: a standalone npm/pnpm package that depends on@srgnt/connectorsand@srgnt/contracts. Build to./dist/(TS → ESM) and setmainto the compiled entrypoint. -
Define the manifest. Shape and validate against
SConnectorManifestinpackages/contracts/src/connectors/manifest.ts. Pick a stableid; it is the activation key and must match the one the host expects. -
Implement the connector. Extend
BaseConnector(no sync) orSyncableConnector(withsync(): Promise<SyncResult>) from@srgnt/connectors. Only use capabilities yourruntime.capabilitiesblock declared — the host mediates all I/O throughHostContext. -
Write the factory. A minimal factory:
import { BaseConnector, type HostContext } from '@srgnt/connectors'; import type { ConnectorPackage } from '@srgnt/connectors'; class MyConnector extends BaseConnector { async connect() { this.updateHealth('connected'); } async disconnect() { this.updateHealth('disconnected'); } async refresh() { this.updateHealth('connected'); } } export const connectorPackage: ConnectorPackage = { manifest: { id: 'my-connector', /* ... */ }, runtime: { sdkVersion: '0.1.0', minHostVersion: '0.1.0', entrypoint: 'connectorPackage', capabilities: ['http.fetch', 'logger'], executionModel: 'worker', }, factory: async (host: HostContext) => new MyConnector(/* manifest */), };
-
Publish the package. Host it at an
https://URL the desktop app can fetch (plainhttpsonly;localhostis allowed for dev registries).
Installation is CLI-only in v1. The binary is srgnt-connectors, shipped from @srgnt/desktop.
# Install (HTTPS required; sha256 integrity check is pinned when --checksum is passed)
pnpm --filter @srgnt/desktop cli:connectors -- install https://example.com/my-connector.json \
--connector-id my-connector \
--checksum <sha256-hex>
# List installed packages with lifecycle state
pnpm --filter @srgnt/desktop cli:connectors -- list
# Inspect one package (redacted — no tokens, no secrets)
pnpm --filter @srgnt/desktop cli:connectors -- inspect my-connector
# Remove a package
pnpm --filter @srgnt/desktop cli:connectors -- remove my-connector
# JSON output for any command
pnpm --filter @srgnt/desktop cli:connectors -- list --jsonFlags:
--workspace <path>: override the target workspace (default$SRGNT_WORKSPACEor~/srgnt-workspace).--connector-id <id>: assert the installed package's manifest id; mismatches fail closed.--checksum <sha256>: pin the expected sha256 of the fetched package payload.--json/--text: output format.
Installing only writes a durable package record to the workspace. Activation (handshake, isolated runtime spawn, load) happens when the Electron app boots and calls ConnectorPackageHost.applyRestartRecovery(). Integrity, compatibility, or handshake failures all leave the package in a clearly-marked non-active state and the record remains inspectable and removable.
If you are using pi with this repo, the team templates are maintained globally only. See docs/pi-teams.md for the reusable team templates and the recommended QA → bugfix → QA loop.
Quick start:
create_predefined_team({ team_name: "qa", predefined_team: "qa", cwd: "/path/to/srgnt" })
create_predefined_team({ team_name: "bugfix", predefined_team: "bugfix", cwd: "/path/to/srgnt" })
create_predefined_team({ team_name: "review", predefined_team: "review-team", cwd: "/path/to/srgnt" })