- TypeScript 90.7%
- Shell 5.7%
- Dockerfile 3.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Prompt/skill forbid csproj and bootstrap version edits, and PR bodies strip leftover version-bump lines so agents cannot claim a release number that is not this commit. |
||
| config | ||
| prompts | ||
| scripts | ||
| src | ||
| vendor | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Sentinel
Continuous multi-project BugSink → Forgejo hotfix gateway powered by the Cursor SDK.
- Persona / prompt: Sentinel (
sentinel.hotfix.v1) - Loop:
sentinel-watch— always-on poll across enrolled projects - Ship mode: opens Forgejo PRs only (no auto-merge, no release publish, no BugSink resolve)
- Config: hot-reloadable
/data/config/projects.json— add/remove projects without restart
Repo: https://git.aexoradao.com/epistemophiliac/Sentinel
Logical flow
- Watch
projects.json(andPUT /v1/projects) → live in-memory registry swap - For each enabled project → BugSink triage (deterministic filters)
- Dedupe fingerprints / open PRs
- Clone Forgejo repo → Cursor SDK local agent with Sentinel prompt
- Commit + push
sentinel/hotfix-*→ open PR
Why local Cursor SDK (not managed cloud agents)
Forgejo is self-hosted. Cursor managed cloud agents are GitHub-oriented. Sentinel runs local agents (local: { cwd }) against persistent clones on the Coolify volume.
Enroll a project (no restart)
Edit /data/config/projects.json on the volume, or:
curl -sS -X PUT https://<sentinel-host>/v1/projects \
-H "Authorization: Bearer $SENTINEL_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d @- <<'EOF'
{
"pollIntervalSec": 300,
"maxGlobalConcurrentAgents": 2,
"projects": [
{
"id": "my-app",
"enabled": true,
"bugsink": "AexoLoader",
"forgejo": "epistemophiliac/my-app",
"defaultBranch": "main",
"noiseDeny": [],
"minEvents": 1,
"promptProfile": "sentinel.hotfix.v1"
}
]
}
EOF
Disable without deleting:
curl -sS -X PATCH https://<sentinel-host>/v1/projects/my-app \
-H "Authorization: Bearer $SENTINEL_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
API
| Method | Path | Notes |
|---|---|---|
| GET | /health |
liveness, paused, skills/tools ok |
| GET | /v1/status |
full surveillance snapshot (live transcript + counters) |
| GET | /v1/ready |
toolchain + skills inventory (auth) |
| GET | /v1/live |
SSE stream of AI transcript lines (line / begin / end / heartbeat) |
| POST | /v1/pause |
master kill — stop cycles + abort active agent |
| POST | /v1/resume |
clear pause |
| POST | /v1/abort |
abort active agent without pausing forever |
| POST | /v1/fingerprints/clear |
unlock all dedupe fingerprints |
| GET | /v1/projects |
live registry |
| PUT | /v1/projects |
replace registry (writes file → hot reload) |
| PATCH | /v1/projects/:id |
patch one project |
| POST | /v1/projects/reload |
force reload from disk |
| GET | /v1/runs |
recent runs (includes truncated transcript) |
| GET | /v1/runs/:id |
one run |
| POST | /v1/run |
kick one watch cycle now (409 if paused) |
If SENTINEL_ADMIN_TOKEN is set, all /v1/* routes require Authorization: Bearer ….
Safety defaults
SENTINEL_PAUSEDdefaults totruein compose — deploy stays idle untilPOST /v1/resume.SENTINEL_AGENT_TIMEOUT_SECdefaults to 900 — hung agents are cancelled.- Boot reclaims leftover
queued/runningrows and unlocks fingerprints. - Full Cursor
run.stream()transcript is logged ([ai:…]) and mirrored to/v1/status+ SSE. - Prompt forces surgical hotfixes only (no open-ended exploration).
- Empty-diff gate: if the agent finishes with a clean git tree, Sentinel forces one write follow-up. If still clean → status
empty_diff(no commit, fingerprint stays locked). Narrative-only finishes cannot open a PR.
Skills + agent toolchain (Docker)
Sentinel vendors https://git.aexoradao.com/epistemophiliac/skills into vendor/aexora-skills and bakes them into the image at /opt/aexora-skills.
On every container start and before each agent run:
- Skills/rules are applied to
HOME/.cursor(user) and the Forgejo clone’s.cursor(project). - Cursor agents load
settingSources: ["project", "user"]. - Hotfix commits exclude
.cursor/so skills are never pushed into customer PRs.
Refresh the vendor snapshot: ./scripts/sync-skills.sh
Image toolchain (verified by entrypoint): git, curl, rsync, rg, python3, jq, dotnet (SDK 8), node, plus build-essential / binutils for inspection builds.
Check after deploy: GET /v1/ready (Bearer admin token).
Note: The LaTeX skill is present as documentation; a full TeX Live stack is not installed (image size). Hotfix work uses the C#/git/python toolchain above.
Coolify deploy
- Create a Docker Compose resource from this repo.
- Set secrets in Coolify UI:
CURSOR_API_KEY,BUGSINK_API_TOKEN,FORGEJO_TOKEN(and optionalSENTINEL_ADMIN_TOKEN). - Assign a domain to service
sentinelon port 8080 (compose declaresSERVICE_URL_SENTINEL_8080). - After first boot, enroll projects via
PUT /v1/projectsor by editing thesentinel_datavolume file/data/config/projects.json. - Confirm readiness:
curl -sS -H "Authorization: Bearer $SENTINEL_ADMIN_TOKEN" https://<host>/v1/ready— expecttoolsOk: trueand a non-zeroskillsCount.
Note: Changing compose env defaults does not update values Coolify already stored — edit them in the Coolify UI.
Local dev
cp config/projects.example.json /tmp/sentinel-projects.json
cp .env.example .env # fill keys
export SENTINEL_DATA_DIR=./data
export SENTINEL_CONFIG_PATH=./data/config/projects.json
mkdir -p data/config data/repos
cp config/projects.example.json data/config/projects.json
npm install
npm run dev
Dry-run (no real BugSink/Cursor/Forgejo calls for empty registry):
SENTINEL_DRY_RUN=true CURSOR_API_KEY=x BUGSINK_API_TOKEN=x FORGEJO_TOKEN=x npm run dev
License
Private Aexora tooling.