Clueless

Clueport

Your school isn't in Clueless yet? Build its connector.

Clueport links Clueless to your school's course site. One student builds it, and every student there gets it.

Overview

How it works, step by step

You capture your own pages, an AI writes the parser from them, and the verifier proves it works without anyone logging in. Nobody needs a login they don't have.

  1. 01

    Open Clueport

    In Clueless, open Settings, then Clueport. Fill in four fields: your school's short id (lowercase letters, digits and hyphens; it can't change once it ships), its name, a short id for the site you're connecting, and the site's https address. Add your name and contact too. Only the reviewer sees them.

  2. 02

    Log in on your school's site

    Press Open & sign in. A window opens on your school's own site. Log in the way you always do. Clueless reads the address of the page you're on, so you never paste a link.

  3. 03

    Capture the pages that matter

    Go to your course list and press Capture on the Courses row. Do the same for a syllabus, then any of assignments, grades, materials or schedule. Each capture is cleaned before it's saved: ids, tokens and cookies come out, and the panel says what changed.

  4. 04

    Copy the prompt, make the kit

    The panel shows the prompt your AI will work from, with a Copy prompt button. Press Generate kit. A folder named clueport-kit-<school>-<site> lands on your Desktop.

  5. 05

    Install once, then run the checker

    Open the folder in a terminal. The first run fails on purpose: it lists every read that still returns nothing.

    cd ~/Desktop/clueport-kit-<school>-<site>
    npm install --ignore-scripts   # once
    node verify.mjs                # after every edit
  6. 06

    Hand the prompt to your AI

    Paste PROMPT.md into a coding agent or a chat window (the recipes are below). It edits connector.mjs, you run node verify.mjs, and you repeat until it prints VERIFIED. Missing a page? Capture it in Clueless and press Generate kit again. Your connector.mjs is kept.

  7. 07

    Clean up and send

    Delete node_modules/ and package-lock.json, zip the folder, and press Email the kit in the panel. Your mail app opens with the subject and message filled in; attach the zip. If the site really can't be connected (hard 2FA, blocked addresses), say so in the panel's box and send it anyway.

  8. 08

    Review and release

    The reviewer runs clueportkit verify, which reads your kit and runs nothing, then reads connector.mjs, then runs clueportkit run. An approved connector ships in the next Clueless release, and your school shows up in the app.

Reference

Working with an AI agent

Any agent works. PROMPT.md is the brief, connector.mjs is the only file to edit, and node verify.mjs is the loop. Here is the exact prompt Clueless writes for the example kit. Yours names your school, your pages and the reads you captured.

PROMPT.md · example-u / example-lms
I'm writing a connector for **Example University** so the Clueless study app can read
my university's learning platform. This folder is a starter kit: the pages are
already captured and scrubbed. I need you to write the parser.

## The files

| file | what it is |
|---|---|
| `connector.mjs` | **the only file you edit** |
| `contract.d.ts` | the types. Read this first. |
| `fixtures/` | real pages from my portal |
| `manifest.json` | what this connector claims to be |
| `verify.mjs` | the checker. Run it constantly. |

## How to work

1. Read `contract.d.ts`, then `manifest.json`.
2. **Open each file in `fixtures/` and read the actual markup.** Find the real
   tags, classes and attributes that hold each piece of data. Do not guess and do
   not write selectors for a page you have not read — a selector that matches
   nothing returns an empty list, which looks exactly like a working connector
   for a course with no data. That is the one mistake this kit exists to catch.
3. Fill in one method at a time.
4. Run `node verify.mjs` after **every** change. Keep going until it prints
   **VERIFIED**. Do not stop before that, and do not change `verify.mjs` or
   `manifest.json` to make it pass.

```sh
npm install --ignore-scripts   # once
node verify.mjs                # after every edit
```

## Rules the checker will fail you on

1. **Only `fetchPage(url)`.** It is handed to you and it is the only way to
   reach the network. Never import `node:https`, never call `fetch()`, never
   touch `node:fs`, `child_process`, `eval` or `process.env`. The kit is
   reviewed by someone who cannot log in to my university, so it has to run
   offline against the captured pages — code that goes around the fetcher cannot
   be checked by anyone but me, and the checker refuses it.
2. **Ids must be deterministic** — built only from the page's own content. Use
   the `detId` helper at the top of `connector.mjs`. Never `Date.now()`,
   never `Math.random()`, never a counter. An id that changes between runs makes
   the app write duplicates and lose the student's own edits.
3. **`fetchCurrentTerm()` must never throw.** Return `null` if the platform has
   no notion of a current term.
4. **Every course's `semester` must be exactly the `'YYYY/S'` string
   `fetchCourses` was given** — not the platform's own wording.
5. **`fetchSyllabus` `bands` must include a letter literally named `"A"`**, or
   the whole grade scale is thrown away and the course silently falls back to a
   default that is wrong for my university.
6. **Do not leave a declared read returning `[]`.** If a fixture genuinely does
   not contain what a method needs, say so and stop — I will capture more pages.
   Do not invent them.

## What to implement

- `fetchCourses(yearsem)` — backed by `fixtures/courses.html` (called with `2026/1`)
- `fetchSyllabus(courseId)` — backed by `fixtures/syllabus_c1.html` (called with `C1`)

## When you're done

Tell me when `node verify.mjs` says **VERIFIED** and stop there. If it warns
about grade letters or anything else, quote the warning — some warnings need a
change in Clueless itself and are the maintainer's job, not yours.

---
Kit: `example-u` / `example-lms` · portal https://lms.example.edu

In the app, Settings › Clueport › Copy prompt gives you the same text for your kit. Start the agent inside the kit folder so it can read the pages.

Claude Code

cd clueport-kit-<school>-<site>
claude "Read PROMPT.md and do exactly what it says. Edit only connector.mjs.
Run node verify.mjs after every change and stop when it prints VERIFIED."

It reads PROMPT.md and the pages, then runs the checker until it prints VERIFIED.

Codex CLI

codex --approval-mode auto-edit "Read PROMPT.md and follow it. Edit only connector.mjs.
Run node verify.mjs after every change until it prints VERIFIED."

Auto-edit lets it write connector.mjs and run the checker without asking each time.

Gemini CLI

gemini -p "Read PROMPT.md and follow it. Edit only connector.mjs.
Run node verify.mjs after every change until it prints VERIFIED."

It picks up contract.d.ts and the pages from the folder.

Cursor

# Cursor › Agent mode
@PROMPT.md Do exactly this. Edit only connector.mjs.
Run node verify.mjs after every change until it prints VERIFIED.

Open the kit as a project, point Agent mode at PROMPT.md, and ask it to run the checker after each edit.

GitHub Copilot CLI

copilot "Read PROMPT.md and follow it. Edit only connector.mjs.
Run node verify.mjs after every change until it prints VERIFIED."

Agent mode in the terminal: it edits and runs commands in the folder.

A chat window

Clueless › Settings › Clueport › Copy prompt
node verify.mjs   # after each change the model gives you

Paste the prompt. When the model asks, paste the page it needs. Save its connector.mjs and run the checker yourself.

RuleWhy agents get it wrong
Read the pages before writing a selectorA model writes selectors for the page it imagines. Each one returns an empty list, which looks just like a working connector for a course with no data. Make it open fixtures/ first.
Only connector.mjs changesAgents fix a failing check by editing the check. The reviewer refuses a kit whose verify.mjs, manifest.json or package.json differ from the generated ones.
No fetch, fs, https or process.envAgents reach for fetch() when a page is missing. fetchPage(url) is the only way out. Capture the page in Clueless instead.
Delete node_modules before zippingThe agent installed packages to run the checker. A sent dependency tree is unreviewed code, and the reviewer's checker refuses it.

Shipping

What the reviewer runs

Two commands, in order. The first reads your kit as text and runs nothing. The second runs it, and won't start unless the first passed.

clueportkit verify <kit-dir>   # read a submission. Executes nothing.
clueportkit run    <kit-dir>   # run it, after reading it.
clueportkit demo   <out-dir>   # write a working example kit

An approved connector.mjs joins the Clueless code, reviewed and built like everything else, and your school shows up in the next release. Only the reviewer sees your name and contact.

Questions

Before you start

Can my kit ship if the site can't be connected?

Yes. Set portal.unreachable to the reason, such as required 2FA or blocked addresses. The checker checks the reason instead, and students at that school see it as the reason nothing syncs.

Do I need a toolchain?

No. A kit is plain JavaScript and node verify.mjs. The checker ships inside the kit.

What about my personal data in the captured pages?

Capture strips ids and secrets and says what it changed. It can't recognise a person's name on an unknown page, so read your pages before you send them.

Reference

The contract

A connector returns plain rows. It reaches the network through one function it's handed, and nothing else.

export interface Connector {
  source: string;                       // stamped on every row you write
  capabilities?: readonly Capability[]; // spell them out (see manifest)

  fetchCurrentTerm(): Promise<string | null>;
  fetchTermOptions?(): Promise<string[]>;
  fetchCourses(term: string): Promise<CourseInput[]>;      // 'YYYY/S'
  fetchAssignments(courseId: string): Promise<AssignmentInput[]>;
  fetchGrades(courseId: string): Promise<GradeInput[]>;
  fetchMaterials(courseId: string): Promise<MaterialInput[]>;
  fetchSyllabus(courseId: string): Promise<SyllabusData>;
  fetchSchedule(courseId: string): Promise<EventInput[]>;
  resolveStudentId(hintCourseId: string): Promise<string>;
  fetchPortfolio(courseId: string, studentId: string): Promise<ComponentScoreInput[]>;
}

export type PageFetcher = (url: string) => Promise<string>;

export interface KitModule {
  createConnector(fetchPage: PageFetcher): Connector;
}

In the app the fetcher is real. In the checker it serves your captured pages, so the same parser runs offline and gives the same answer on a computer that has never seen your school's site. A connector that opens its own connection can't be reviewed, and the checker refuses it.

fetchCurrentTerm never throws. If the site doesn't say which term it is, return null and Clueless uses its calendar.

Reference

Capabilities

Seven reads. Declare the ones you serve and Clueless skips the rest, out loud. Two are required.

CapabilityReturnsRequired
coursesThe course list for one termYes
syllabusWeights, grade bands and the syllabus text for one courseYes
assignmentsAssignments and due dates for one courseNo
gradesPosted grades for one courseNo
materialsFiles and links for one courseNo
scheduleClass sessions for one courseNo
portfolioComponent scores for one student in one courseNo

Every screen hangs off the courses. The syllabus gives each course its grade bands and weights, so Clueport needs no grading rules per school. Grades and portfolio are optional because most gradebooks are empty; students add their own.

Reference

The manifest

manifest.json is the half of a kit that can be checked with no code running. Clueless drafts it for you. This is the example kit's.

{
  "kit": 1,
  "university": { "id": "example-u", "label": "Example University", "blurb": "" },
  "connector":  { "source": "example-lms", "label": "Example LMS",
                  "capabilities": ["courses", "syllabus"] },
  "author":     { "name": "Your Name", "contact": "you@example.invalid" },
  "portal":     { "baseUrl": "https://lms.example.edu", "unreachable": null },
  "fixtures": [
    { "file": "courses.html",     "capability": "courses",  "arg": "2026/1",
      "url": "https://lms.example.edu/courses?term=2026-1" },
    { "file": "syllabus_c1.html", "capability": "syllabus", "arg": "C1",
      "url": "https://lms.example.edu/course/C1/syllabus" }
  ],
  "notes": ""
}
FieldRule
kitMust be 1. A kit in a format this checker doesn't read is refused whole.
university.idLowercase letters, digits and hyphens, 1 to 63 characters, not starting with a hyphen. It can't change once it ships.
connector.sourceSame shape as the id. A few names are taken by Clueless's own connectors.
connector.capabilitiesRequired, and must include courses and syllabus. Leaving it out would mean all seven.
portal.baseUrlAn https address. Plain http leaks the session.
portal.unreachablenull when the connector works. Otherwise a sentence of 20 or more characters saying why it can't, shown to that school's students.
fixtures[].fileA plain file name inside fixtures/. No folders.
author.contactHow the reviewer replies. Never shown to students.

Reference

The verifier

No files, no network, no clock. Two phases, and the words in its report mean different things.

PhaseWhat it checksPrints
staticOnly the files the generator wrote, and Clueless's own files unchanged. A scan for code that reaches past the fetcher: node:https, fetch(, child_process, eval, process.env, and hiding tricks such as built-up import() names and unicode escapes.SAFE TO RUN
fullBuilds your connector on your captured pages, calls every declared read, and checks the rows: required text there, numbers numeric, terms as YYYY/S, known grade letters, and no cleaning placeholders left in the output.VERIFIED

The static scan helps a reviewer; it isn't a sandbox. It puts the few lines a person must read in front of them. Every report names the checker's version and phase.

node verify.mjs

Start in the app. Settings, then Clueport. The capture tool and the kit maker are built in.

Get Clueless