Appearance
Notes for coding agents
Rules and a map for an agent that writes programs with the Simulator for a developer. Each rule links to the page that explains it.
Where to read
/llms.txtis the index of this site: every page with its address and a one-line summary./llms-full.txtis every page, in reading order, in one file.- The API reference is at
/api/: every exported name, grouped by what it is for. - The pages, in order: Get access and install, Your first program, How the Simulator works, then the screen pages.
The canonical program shape
A fresh install logs in and saves the session; every later run launches the stored session on the screen it expects. One otherwise constant goes to every entry.
ts
import {
AndroidIgpapi,
ChallengeSelectVerifyMethodScreen,
ChallengeVerifyCodeScreen,
defineOtherwise,
FeedTimelineScreen,
} from '@igpapi/android';
import { Effect } from 'effect';
import { readFile, writeFile } from 'node:fs/promises';
import { createInterface } from 'node:readline/promises';
const username = process.env['IG_USERNAME'] ?? '';
const password = process.env['IG_PASSWORD'] ?? '';
// A code is read at the moment a step asks for it (see "Codes that arrive during the run").
const readCode = (kind: 'email') =>
Effect.promise(async () => {
const terminal = createInterface({ input: process.stdin, output: process.stdout });
const code = await terminal.question(`The ${kind} code: `);
terminal.close();
return code.trim();
});
// One constant for every entry.
const otherwise = defineOtherwise({
[ChallengeSelectVerifyMethodScreen.id]: method => method.chooseVerificationMethod('email'),
[ChallengeVerifyCodeScreen.id]: screen => Effect.flatMap(readCode('email'), code => screen.submitCode(code)),
});
// The first run: a fresh install logs in and saves the session.
const checkpoint = await AndroidIgpapi.install({
seed: username,
otherwise,
main: home =>
Effect.gen(function* () {
const next = yield* home.logIn({ username, password });
return FeedTimelineScreen.is(next) ? yield* next.checkpoint() : undefined;
}),
});
await writeFile('session.json', JSON.stringify(checkpoint));
// Every later run: launch the stored session on the screen you expect, and return data.
const saved = JSON.parse(await readFile('session.json', 'utf8'));
const itemCount = await AndroidIgpapi.launch({
checkpoint: saved,
screen: FeedTimelineScreen,
otherwise,
main: feed => Effect.succeed(feed.items.length),
});
console.log(itemCount);Rules
- Screens first. Use an
AndroidIgpapientry and the screen handles. Call an effect directly only where no screen exists (Effects insidemain). - Keep
seedstable for an account (How the Simulator works). - Stored values are objects. Parse what your storage returns (
JSON.parse(row.checkpoint)) and pass the object. An entry never takes JSON text (Keep a session). - Narrow with the guards:
XxxScreen.is(screen)for a screen,XxxItem.is(item)for a list item. - One
yield*per line, insideEffect.gen(function* () { … }). - Answer errors with
catchTag/catchTags, nevercatchAll, and end a long program with the exhaustiveness check (A complete session, Errors). - One
otherwiseconstant for every entry (Challenges). - Codes come from a
readCodeeffect at the moment a step asks, never from an environment variable at start-up (Codes that arrive during the run). - A handle never leaves
main. Return data frommain; do not keep a handle. - Continue from the handle an action returns. An action that opens a screen returns that screen's handle (Work with screens).
- Persist a session, do not log in on every run. Log in once with
install, storecheckpoint(), andlaunchit.
Which page for which task
| Task | Page | Method |
|---|---|---|
| Log in | Log in | home.logIn(…) |
| Keep a session | Keep a session | checkpoint(), launch |
| Get a code during a run | Codes that arrive during the run | readCode |
| Like, comment, repost | Likes, reposts and comments | like(), comment(), repost() |
| Publish | Publishing | openCreation(mode), share() |
| Send a direct message | Direct | sendMessage() |
The same list, by the goal, is All recipes.
What not to do
- Do not write a manual pre-login before the login: the login owns it.
- Do not act on a screen another screen covers (Work with screens).
- Do not use
Effect.diefor an outcome the program expects. Return a value that names the outcome. - Do not call a name the API reference does not list.