Skip to content

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 ​

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 AndroidIgpapi entry and the screen handles. Call an effect directly only where no screen exists (Effects inside main).
  • Keep seed stable 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, inside Effect.gen(function* () { … }).
  • Answer errors with catchTag / catchTags, never catchAll, and end a long program with the exhaustiveness check (A complete session, Errors).
  • One otherwise constant for every entry (Challenges).
  • Codes come from a readCode effect 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 from main; 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, store checkpoint(), and launch it.

Which page for which task ​

TaskPageMethod
Log inLog inhome.logIn(…)
Keep a sessionKeep a sessioncheckpoint(), launch
Get a code during a runCodes that arrive during the runreadCode
Like, comment, repostLikes, reposts and commentslike(), comment(), repost()
PublishPublishingopenCreation(mode), share()
Send a direct messageDirectsendMessage()

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.die for an outcome the program expects. Return a value that names the outcome.
  • Do not call a name the API reference does not list.

The pages of this site are the README files of the repository. The API reference is generated from the source.