Skip to content

Latest commit

 

History

History
202 lines (147 loc) · 18.6 KB

File metadata and controls

202 lines (147 loc) · 18.6 KB

Repository-level Agent Guide

Reply in English unless the user explicitly asks otherwise. Always talk in ASD-STE100 Simplified Technical English.

TypeScript monorepo for pythinker-code, a provider-agnostic AI coding agent. This file covers product identity, project map, hard constraints, and workflow rules.

Product Identity

pythinker-code plans, writes, tests, and iterates on code autonomously. The same runtime talks to any LLM through the packages/kosong abstraction layer.

Wire Types

Wire type SDK / transport Providers
anthropic @anthropic-ai/sdk Anthropic (Claude family)
openai OpenAI Chat Completions OpenAI (GPT-4o/4.1/4.5/5.x, GPT-3.5-turbo)
openai_responses OpenAI Responses API OpenAI (GPT-4.1/5.x, o-series)
google-genai @google/genai Google (Gemini 2.0–3.x)
vertexai Google Vertex AI Google Cloud–hosted Gemini
pythinker Pythinker-compatible API PyModel, Kimi, and custom compatible endpoints

Any OpenAI-compatible endpoint (DeepSeek, Qwen, GLM, Grok, Together AI, Fireworks, etc.) works via the openai/openai_responses wire with a custom baseURL.

Model Selection

Flows through the catalog (catalog.ts):

  1. JSON catalog maps providerId → models[] with context window, capabilities, cost, and modality metadata.
  2. inferWireType() resolves provider → wire type (explicit type field, then heuristic on npm/id).
  3. createProvider() instantiates the correct ChatProvider.
  4. getModelCapability() returns per-model flags (vision, tool-use, thinking, fast-mode).

Adding an OpenAI-compatible provider requires zero code changes — just add a catalog entry.

Working Principles

  • Start from requirements and code facts; discuss unclear goals first.
  • Code is the source of truth — don't read Markdown to understand implementation.
  • Validate version claims against authoritative docs (Context7 MCP, Tavily).
  • Read relevant source and follow the nearest AGENTS.md before changing code.
  • Keep changes focused — no drive-by refactors.
  • Implement current requirements directly; no backward-compatibility shims.
  • Simplest implementation first: stdlib → established libraries → custom code.
  • No co-author attribution or agent identity in commits/PRs.
  • Git identity: elkaix <melkholy@techmatrix.com> — apply per command; never modify git config.

Project Map

Package Description Notes
apps/pythinker-code CLI / TUI app Consumes @pymodel/pythinker-code-sdk; no agent-core dep. Use write-tui skill.
apps/pythinker-web Browser UI (Vue 3 + Vite + vue-i18n) REST + WS /api/v1; no agent-core dep. See its AGENTS.md.
apps/pythinker-inspect Web inspector for the agent-gateway /api/v1/debug RPC surface Workspace/session browser, per-session transcript chat, per-scope Service panels, DI unit inspection. See its AGENTS.md.
apps/vis Session replay & debugging visualizer server/ + web/ subdirs.
packages/agent-core Agent engine Agent, Session, profile, skills, tools, plan, permission, DI.
packages/agent-core-v2 DI × Scope agent engine (the v2 port behind agent-gateway) Three LifecycleScope tiers — App / Session / Agent (app/scopes.ts). Workspace resources use App-owned WorkspaceInstance / Program lifetimes, not a DI scope. Also includes the L3 unit layer (Service/Fiber units, collection contribution points, the Feature seam in src/features/). See its AGENTS.md and use the agent-core-dev skill.
packages/node-sdk Public TS SDK & harness
packages/kosong LLM provider abstraction Wire types, catalog, capability registry.
packages/pyaos Execution environment File/process abstractions.
packages/agent-gateway Pythinker Code server Fastify server backed by @pymodel/agent-core-v2; sessions over REST + WebSocket (/api/v1 + /api/v1/ws), plus /api/v1/debug/* reflection RPC (--debug-endpoints, loopback bind + bearer auth). See its AGENTS.md.
packages/klient Client SDK Contract-driven facade over agent-core-v2 (global.* / session(id).* / agent(id).*, zod-validated); transport via subpath entry (`@pymodel/klient/ipc
packages/transcript Isomorphic transcript rendering data layer L1 agent-granular store, L2 idempotent operations, L3 off/turn/block/delta subscription granularity, L4 framework-free view registry, turn-cursor pagination. Pure TypeScript (browser-safe, no engine imports); sole owner of the transcript contract types (src/contract/). See its AGENTS.md.
packages/oauth Auth utilities
packages/telemetry Client-side telemetry
packages/tree-sitter-bash Pure-TypeScript bash parser No runtime deps, no wasm; parse(source, { timeoutMs, maxNodes }) under a deterministic budget returns a discriminated ParseResult — treat aborted/hasError trees as "cannot analyze" and degrade. Parser only, no safety judgments.
packages/minidb Embedded JSON document store MiniDb behind agent-gateway's search index — snapshot + WAL persistence with an exclusive write lock, larger-than-RAM full-text layer, persistent index generations. See its AGENTS.md.
packages/acp-server Agent Client Protocol host over engine v2 Drives the engine through a klient memory-transport facade.
packages/pi-tui Vendored TUI library Upstream fork with local divergences; tests run with node --test, not vitest. See its AGENTS.md.
packages/protocol Shared REST + WS protocol schemas Envelope, error codes, pagination, WS-control types.
packages/remote-control Remote Control tunnel client Registers this machine with a relay and forwards HTTP/WebSocket traffic to the local server, behind a machine-wide single-instance lock. Consumed by agent-gateway (the /api/v1/remote-control toggle) and the CLI (pythinker web --remote-control).

The web bundle: apps/pythinker-code/dist-web is the committed, prebuilt bundle of apps/pythinker-web (built with pnpm --filter @pymodel/pythinker-web run build and copied via scripts/copy-web-assets.mjs). apps/pythinker-code/scripts/check-web-assets.mjs fails when the bundle is missing or stale (it compares a fingerprint of every apps/pythinker-web build input against the one recorded at copy time); it runs in pre-push, in the CLI build, and on prepack. Whenever you touch the web UI, run pnpm run build:web and commit the restaged bundle in the same change. packages/server and packages/server-e2e are empty leftover directories excluded from the workspace — not packages.

Environment

  • Node.js 24.x, minimum 24.15.0 (.nvmrc pins 24.15.0). pnpm 10.34.3 (root packageManager). engine-strict=true; pnpm install fails outside the supported Node 24 range.

Monorepo Maintenance

  • pnpm-workspace.yaml is source of truth, but flake.nix hardcodes workspacePaths/workspaceNames.
  • Update both when adding/removing any workspace package — for every package, including leaf / test / e2e packages that nothing depends on. Missing a path silently drops files from Nix's src fileset; missing a name breaks pnpmConfigHook (dependencies for that workspace are not fetched).
  • CI (scripts/check-nix-workspace.mjs) only validates the transitive dependency closure of @pymodel/pythinker-code — a leaf package outside that closure slips through even when missing from flake.nix. A green check is NOT proof of full sync — keep flake.nix updated by hand.

Coding Rules

  • English-only codebase. Use ASCII/Latin fixtures (e.g. café) for unicode tests.
  • packages/agent-core-v2, packages/agent-gateway, and packages/transcript are comment-free zones: no line/block comments; no JSDoc either, not even on exported symbols; the only exception is a load-bearing lint-suppression directive (oxlint-disable / eslint-disable), while other tooling directives (@ts-expect-error, …) stay banned. Enforced by scripts/check-no-comments.mjs, which runs as part of pnpm lint.
  • tsgo (@typescript/native-preview) available via npx tsgo -p <tsconfig> --noEmit; committed scripts use tsc — run both for type fixes.
  • Pass undefined directly for optional props — no conditional spread.
  • user?: User, not user?: User | undefined.
  • Single-param internal methods stay single-param — no options-object wrapping.
  • Non-root index.ts: prefer export * from './module'.
  • Agent class must be standalone — no mandatory Session/agentId. Optional sessionId as provider hint only.
  • Prefer adding tests to existing files. Fix failing tests first (unless there's a real impl bug); when a test fails because of a user modification, default to fixing the test first, not the implementation.
  • Do not sacrifice code quality for external compatibility unless the user explicitly asks for it.
  • Breaking changes require changesets with major bump (user confirmation required).
  • Identity freeze: never rewind published package.json versions, never change the VS Code publisher or extension id (pymodel.pythinker), never replace CHANGELOG.md with another history. scripts/check-identity-freeze.mjs (via pnpm lint) and Release --npm enforce this.

Experimental Features

Gate behind flags. Env: PYTHINKER_CODE_EXPERIMENTAL_<NAME> toggles one; PYTHINKER_CODE_EXPERIMENTAL_FLAG enables all. Precedence is per-flag env > [experimental] config > master env > the flag's default. Release: flip the entry's default to true.

  • packages/agent-core (v1): add the flag to the central registry at packages/agent-core/src/flags/registry.ts, then check it with flags.enabled('my-feature').
  • packages/agent-core-v2 and agent-gateway modules: no central catalog — declare the flag in the owning domain via registerFlagDefinition at import time (see packages/agent-core-v2/docs/flag.md), then check it with IFlagService.enabled(id).

Workflow

  • Never commit to main directly. Every change lands through a pull request: branch, push the branch, open a PR, get the checks green, then merge. main enforces this for everyone including admins, so a direct push is rejected outright (GH006) — do not try to work around it with --admin, --no-verify, or a force push.
  • A PR is mergeable only when all six required checks pass (build, test, lint, typecheck, nix build .#pythinker-code, Check flake.nix workspace sync), every review conversation is resolved, and the branch is up to date with main.
  • Prefer rg / rg --files for code reading.
  • Follow existing boundaries and local patterns.
  • Replace internal identifiers with neutral placeholders in public text/test data (e.g. example.com, example.test, YOUR_API_KEY). Before opening a PR, ask a read-only agent to audit the diff for context-specific internal identifiers.
  • When creating a PR, use the write-pr skill (.agents/skills/write-pr/SKILL.md) to write the PR description. PR titles: Conventional Commit style (e.g. chore: remove legacy format commands).
  • Fill in .github/pull_request_template.md — link the issue, describe changes. No placeholder text or vague AI-generated PR summaries; the human author must understand the change well enough to explain the code, edge cases, and why the approach fits.
  • Run gen-changesets skill before submitting PRs. Changesets must strictly follow its rules: one short user-facing sentence stating only what changed; skip any change users cannot perceive. Never decide major on your own — stop, explain, and get explicit user confirmation first; default to minor, fall back to patch.
  • Changeset text is shipped text: the desktop release body is generated from apps/desktop/CHANGELOG.md, and the in-app updater shows it to users verbatim. A release body must state what changed for users — never a build stamp, a commit hash, or placeholder text. desktop-release.yml fails a stable release whose version has no changelog entry.
  • Prefer import ... from '#/...' (equivalent to @/...).
  • Do not commit throwaway scratch or exploratory files. Never stage agent working notes or handoff documents (e.g. HANDOVER-*.md, HANDOFF-*.md, handoff.md), or throwaway UI/UX prototypes or design mockups (e.g. *-designs.html, *-mockup.html, *-demo(s).html). The only tracked .html files should be Vite index.html entrypoints. Put scratch work under .tmp/ (gitignored).

Where to Update Instructions

  • Hard rules that affect almost every task: update the root AGENTS.md.
  • Rules that only affect a specific directory: update the nearest sub-directory AGENTS.md.
  • Project-map entries stay at 1–2 sentences; deep package docs live in the package's own AGENTS.md.

Code Review Rules

These rules apply to every pull request review, automated or human. The user populations and contract files they refer to are listed in .agents/skills/review-pr/surfaces.md.

Enumerate changed behavior, not just bugs

Any input that worked before the change — a config key, env var, CLI flag, provider response shape, session written by an older version, client request, or hook payload — must behave the same after it unless the PR declares the change. For every deleted or narrowed branch, condition, default, or prompt sentence, ask who reached it before and where they go now; for every new condition, ask which existing inputs now match it first. Flag a PR that calls a path "unchanged" when its branch condition moved.

A flipped default or removed behavior needs a named loss and an escape hatch

Everyone on the old default is affected. Require the changeset to name the behavior users lose, not only the new default; a config, env, or flag escape hatch or a maintainer's explicit sign-off in the PR; and a test that pins the old behavior for the population that keeps it.

Prompt text is behavior

Editing or deleting sentences under packages/agent-core-v2/src/**/*.md (system prompt, tool descriptions, reminders, overlays, built-in skills) changes agent behavior for every user who receives that prompt. "No test references the sentence" is not evidence of no impact. Require the PR to name the population that receives the text (every session, plan mode, a flag-gated feature such as Tower), what the sentence enforced, who relied on it, and what enforces it now.

Contract files are tripwires

The manifests under packages/agent-core-v2/docs/ (config-manifest.toml, wire-manifest.d.ts, state-manifest.d.ts), packages/agent-gateway/test/__snapshots__/apiSurface.snapshot.test.ts.snap, packages/node-sdk/src/index.ts, packages/agent-core-v2/src/features/externalHooks/, packages/acp-server/, and apps/pythinker-code/src/cli/ are consumed outside the packages that define them: the desktop and web apps in this repository, the inspector app, the VS Code extension, ACP clients such as Zed, SDK users, hook scripts, and headless-output parsers. When they change, require the PR to name the consumers and how data and clients from the previous release keep working.

Ports and refactors carry the old path's feature inventory

When a change replaces or bypasses an existing path, require a list of what the old path did — env vars honored, fallbacks, accepted inputs — and where each item lives in the new path. A silently dropped item is a regression, not a cleanup.

Silent failure outranks a crash

A change that makes the product silently ignore configuration, silently approve or skip an action, or silently drop data is the most severe finding: users get no signal to report.

GitNexus — Code Intelligence

This project is indexed by GitNexus as pythinker-code (91837 symbols, 264777 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.

Index stale? Run node .gitnexus/run.cjs analyze from the project root — it auto-selects an available runner. No .gitnexus/run.cjs yet? npx gitnexus analyze (npm 11 crash → npm i -g gitnexus; #1939).

Always Do

  • MUST run impact analysis before editing any symbol. Before modifying a function, class, or method, run impact({target: "symbolName", direction: "upstream"}) and report the blast radius (direct callers, affected processes, risk level) to the user.
  • MUST run detect_changes() before committing to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: detect_changes({scope: "compare", base_ref: "main"}).
  • MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
  • When exploring unfamiliar code, use query({search_query: "concept"}) to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
  • When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use context({name: "symbolName"}).
  • For security review, explain({target: "fileOrSymbol"}) lists taint findings (source→sink flows; needs analyze --pdg).

Never Do

  • NEVER edit a function, class, or method without first running impact on it.
  • NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
  • NEVER rename symbols with find-and-replace — use rename which understands the call graph.
  • NEVER commit changes without running detect_changes() to check affected scope.

Resources

Resource Use for
gitnexus://repo/pythinker-code/context Codebase overview, check index freshness
gitnexus://repo/pythinker-code/clusters All functional areas
gitnexus://repo/pythinker-code/processes All execution flows
gitnexus://repo/pythinker-code/process/{name} Step-by-step execution trace

CLI

Task Read this skill file
Understand architecture / "How does X work?" .claude/skills/gitnexus/gitnexus-exploring/SKILL.md
Blast radius / "What breaks if I change X?" .claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md
Trace bugs / "Why is X failing?" .claude/skills/gitnexus/gitnexus-debugging/SKILL.md
Rename / extract / split / refactor .claude/skills/gitnexus/gitnexus-refactoring/SKILL.md
Tools, resources, schema reference .claude/skills/gitnexus/gitnexus-guide/SKILL.md
Index, status, clean, wiki CLI commands .claude/skills/gitnexus/gitnexus-cli/SKILL.md