Repository avatar
Developer Tools
v0.2.4
active

skeptopus

io.github.systasys/skeptopus

Your AI says it's done. SkeptOpus goes and looks: commits, pushes, tests, files, deploys.

Documentation

SkeptOpus by SYSTASYS. Your AI says it's done. SkeptOpus goes and looks.

Your AI says it's done. SkeptOpus goes and looks.
A small checker that runs on your machine and verifies what AI coding agents claim:
commits, pushes, merges, tests, files, comments, deploys.
Made for everyone who builds with AI, whether you read the code or not.

Code under MIT Node.js 18 or later Works with Claude Code, Codex, Cursor, Gemini CLI and any MCP client No account, no server, no telemetry


In thirty seconds

An agent ends its turn with a confident summary. SkeptOpus reads it, finds each claim, and goes to look for itself:

$ node core/cli.mjs check --message "Done. Committed ee89a26, pushed to origin, all tests pass, and the shop is live at http://localhost:4173/shop."
SkeptOpus · [ CONTRADICTED ]
  [CONTRADICTED] deployed http://localhost:4173/shop: the page doesn't exist (not found) (HTTP 404)
  [SEEN]         committed ee89a26: the commit exists
  [NOT FOUND]    pushed: the branch isn't on the remote
  [CONTRADICTED] tests pass: run again just now, twice: they fail (the total counts each quantity)

That is a real run, on a small repository made for the demonstration. The page said to be live does not exist. The commit exists, so it is seen. The push never happened. The tests fail when they are run again, twice, and the failing one is named.

The command above is the one that produced that output, from a clone of this repository. You do not need a clone: after npx skeptopus install, the same check reads npx skeptopus check --message "...".

Why it exists

On 12 November 2026, the AI building SkeptOpus told its author "all tests pass". Twice. SkeptOpus ran them: they failed. A test depended on the folder it was started from. SkeptOpus was right, and the AI was not.

AI agents are good at work and generous with the word "done". Nobody can reread everything. SkeptOpus does the rereading: it never takes the agent's word for it, and it never blocks the agent either. It tells you.

Four answers


SEEN
It looked. It's true.

NOT FOUND
It looked and found nothing.

CONTRADICTED
It found the opposite.

COULDN'T LOOK
No way to look, so no verdict.

"Seen" means done, not done well: SkeptOpus checks that a thing exists and holds, not that it is any good. A message is only as good as its weakest claim.

Install

You need Node.js 18 or later.

npx skeptopus install --dry-run   # says what it would do, writes nothing
npx skeptopus install             # hooks every AI coding tool it finds
npx skeptopus status
npx skeptopus uninstall           # takes everything back out; your journal stays

From a clone of this repository, node core/cli.mjs does the same as npx skeptopus.

One command copies the engine to ~/.skeptopus/app, hooks every tool it finds, and lets each tool's AI call SkeptOpus itself:

ToolAfter each turnBefore it says "done"
Claude CodeA line at the end of the turn.MCP server, user-wide.
CodexA warning in Codex. Approve the new hook once in /hooks.MCP server in ~/.codex/config.toml.
CursorThe journal, a notification and a sound (Cursor shows nothing from hooks).MCP server in ~/.cursor/mcp.json.
Gemini CLIA message at the end of the turn. Not tried yet.MCP server in ~/.gemini/settings.json.
Any AI, even in a browserCopy its answer, run node ~/.skeptopus/app/cli.mjs clip.

Every settings file it touches is kept as it was, beside it, as <file>.avant-skeptopus. Installing twice never adds a second line. --skip mcp leaves the servers out.

Let your AI ask first

The hook checks what the AI said, after it said it. The MCP server lets the AI check before it speaks. npx skeptopus mcp speaks the Model Context Protocol on stdin and stdout, in both of its current forms (the 2026-07-28 revision and the earlier handshake ones), with three tools:

ToolWhat it answers
checkEvery claim in the message the AI is about to send, one verdict each, and what to do about it.
todayThe state of the day: what was checked, what was not seen to be true.
what_can_you_checkWhat can be verified in a folder before relying on it.

A check the AI makes on itself is kept out of your journal: an agent that verifies before it speaks has misled no one.

In continuous integration

SkeptOpus reads what a pull request claims, its description and its commits, and checks it against the code:

on: pull_request
jobs:
  skeptopus:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: systasys/skeptopus@v0.2.2
        with:
          fail-on: contradicted       # or: problems, anything, never
          settings: .github/skeptopus.json

The answer lands in the run's summary as a table. From the command line, check also writes --format md or --format junit (one test case per claim, read by every CI dashboard), --output to a file, and --fail-on to decide what fails the run. Use it on pull_request, never on pull_request_target.

Settings and profiles

Your settings are ~/.skeptopus/config.json, created once and never overwritten. npx skeptopus config shows the settings in effect for a folder and where each one comes from.

ProfileWhat changes
calmIt speaks up only when something is false.
normalThe default.
strictA "done" that names nothing is refused; tests that pass only on a second run, and a file whose age cannot be compared, no longer count as seen.

npx skeptopus init finds how a folder runs its tests, from its own files, and runs nothing. --apply keeps it for that folder only, in your file:

{
  "lang": "en",
  "profile": "normal",
  "projects": {
    "/home/me/app": { "test": ["npm", "test"], "profile": "strict" }
  }
}
KeyWhat it does
langThe language shown first: en, fr, es, zh or ar.
profilecalm, normal or strict, here or per project.
notify, soundWhen the small window and the sound come: always, problems, contradicted or never. What you set wins over the profile.
test, testCwdThe command that runs your tests, as a list. Never a shell string.
checksMeasurements it may redo when an AI announces one: name, match, run, cwd, slow, timeoutMs.
repos, rootsGitHub repositories to ask, and more folders to look in.
projectsAny of the above for one folder and what it holds. The most specific folder wins.
webfalse to never open a page an AI says is live.
mcp{ "timeoutMs": 50000, "journal": false } for checks the AI makes on itself.
trustedFolders whose own .skeptopus.json may declare commands too.

A project may carry a .skeptopus.json saying where to look. It can never say what to run, unless you listed its folder in trusted: the AI being checked works in that project and could write the file.

What it reads, and how it checks

It reads claims in English, French, Spanish, Chinese and Arabic. A plan, a question, a condition ("once #12 is merged") or a quotation is not a claim. Each part of a sentence keeps its own polarity: "committed, not pushed" holds two opposite claims.

ClaimHow it is checked
A file written or pointed toThe file is there, not empty, and not older than the session.
A commitIn the repositories at hand, or on GitHub.
A pushThe remote itself is asked. Each GitHub repository the sentence names (owner/name with its branch or its commit) is a push of its own, read on GitHub's public side, with no login. A push that names no repository is judged on the folder the AI worked in, and on no other.
A pull request merged or openedGitHub is asked, through the gh command you already use.
A comment posted, a repository createdGitHub is asked; "private" is checked when it was said. A comment said to be posted on owner/name issue #N, without its own link, is looked for on GitHub's public side.
The tests passYour test command is run again; a failure is run twice before anything is called contradicted.
A measurement announcedRedone only the way you declared it.
Deployed, live at an addressThe page is opened once, like anyone would: no cookie, no login.
A bare "done"Named as vague: there is nothing to check.

The journal page reads in the five languages, shows one character per answer, and puts the octopus on top in the pose of the day's worst answer. A file seen at night and changed or deleted since is marked so the morning after.

Privacy

No server, no account, no telemetry. Everything it keeps stays in ~/.skeptopus. It uses the network for four things only: your own git remote, GitHub through gh, GitHub's public side with no login (for a repository or an issue a sentence names), and a page an AI says is live (turn that off with "web": false).

The extension for VS Code and Cursor

node scripts/skeptopus-pack-extension.mjs        # writes dist/skeptopus-0.2.2.vsix
code --install-extension dist/skeptopus-0.2.2.vsix

The journal in a side panel, the worst answer of the day in the status bar, and "check what I copied". It runs the engine with the editor's own runtime. See extension/README.md.

Develop

node --test

A test must not depend on the machine it runs on, nor leave anything behind. Tried on Windows 11 with Node.js 24, Linux (Ubuntu 24.04) with Node.js 18, VS Code 1.142 and Cursor 3.29; the MCP server with the official MCP Inspector. Not tried yet: macOS.

The Spanish, Chinese and Arabic words were written by a non-native author. Corrections from readers whose language it is are welcome.

License

The code is under the MIT license (see LICENSE). The SkeptOpus name, the octopus, the other characters, the artwork and the sounds are not: see NOTICE and TRADEMARKS.md. The typefaces in core/fonts keep their own license (SIL Open Font License).

SkeptOpus, by SYSTASYS · made by Hichem Benali

NPM
skeptopus
Install Command
npm install skeptopus