Skip to content

Commit 166f48f

Browse files
Add AGENTS.md and CLAUDE.md for AI agent guidance (#55)
* Add AGENTS.md and CLAUDE.md for AI agent guidance Documents architecture, commands, testing conventions, and mock internals so AI agents can work effectively in this codebase without re-deriving project-specific conventions. --------- Co-authored-by: D. Ror. <imnasnainaec@gmail.com>
1 parent 156b823 commit 166f48f

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# AGENTS.md
2+
3+
This file provides guidance to AI agents when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
# Build
9+
npm run build # Build both main and web-view bundles
10+
npm run build:main # Build main extension only
11+
npm run build:web-view # Build React WebView only
12+
npm run watch # Continuous rebuild on changes
13+
14+
# Lint & Format
15+
npm run lint # Run ESLint + stylelint + tsc --noEmit
16+
npm run lint-fix # Auto-fix linting issues
17+
npm run format # Format with Prettier
18+
19+
# Test
20+
npm test # Run full Jest suite
21+
npm run test:coverage # Run with coverage (100% threshold enforced)
22+
npm test -- path/to/file.test.ts # Run a single test file
23+
npm test -- --testNamePattern="pattern" # Run tests matching name
24+
```
25+
26+
## Architecture
27+
28+
This is a **Platform.Bible extension** for interlinear Bible text alignment. Platform.Bible (PAPI) is an Electron-based application; extensions run in a sandboxed context and communicate with the host via `papi.*` APIs.
29+
30+
### Extension entry point
31+
32+
`src/main.ts` — called by Platform.Bible on activation. Exports two lifecycle functions:
33+
34+
- `activate(context)` — registers the `interlinearizer.mainWebView` WebView provider, the `interlinearizer.openForWebView` command, and `onDidOpenWebView` / `onDidCloseWebView` subscriptions. All registrations are added to `context.registrations` so the platform disposes them on deactivation.
35+
- `deactivate()` — clears `openWebViewsByProject` and returns `true`.
36+
37+
`openWebViewsByProject` (`Map<string, string>`) tracks one open WebView ID per project to prevent duplicates; reopening an already-open project brings that tab to front via the `existingId` option.
38+
39+
### WebView UI
40+
41+
`src/interlinearizer.web-view.tsx` — React component rendered inside Platform.Bible's WebView iframe. `useWebViewScrollGroupScrRef` is a **prop injected by the PAPI host** (not a hook import). Uses PAPI frontend hooks (`useProjectData`, `useProjectSetting`, `useLocalizedStrings`, `useRecentScriptureRefs`) to fetch live data. Renders verse segments as token chips with Tailwind utility classes (all prefixed `tw-`).
42+
43+
The WebView is injected into the main bundle via Webpack's `?inline` query:
44+
45+
```ts
46+
import interlinearizerReact from './interlinearizer.web-view?inline';
47+
import interlinearizerStyles from './interlinearizer.web-view.scss?inline';
48+
```
49+
50+
`src/webpack-env.d.ts` declares the `*?inline`, `*?raw`, and `*.scss` module types that make these imports type-safe.
51+
52+
Two separate Webpack configs handle this: `webpack.config.web-view.ts` builds the React component into `temp-build/`, then `webpack.config.main.ts` copies it into `dist/` alongside contributions, public assets, and type declarations.
53+
54+
The WebView root component is assigned to `globalThis.webViewComponent` (not exported) — this is the PAPI WebView contract. Tests must `require()` the module and read `globalThis.webViewComponent` to get the component.
55+
56+
### Styling
57+
58+
All UI uses Tailwind CSS (via `src/tailwind.css`). Every Tailwind class is prefixed `tw-` to avoid collisions with Platform.Bible's own styles (configured in `tailwind.config.ts`).
59+
60+
### Parser pipeline
61+
62+
Data flows from Platform.Bible's USJ (Unified Scripture JSON) format through two stages:
63+
64+
1. `src/parsers/papi/usjBookExtractor.ts` — converts USJ to the internal `Book` type
65+
2. `src/parsers/papi/bookTokenizer.ts` — segments and tokenizes the book into `Segment`/`Token` structures with character offsets
66+
67+
`src/parsers/pt9/interlinearXmlParser.ts` — separately parses Paratext 9 interlinear XML into the alignment model. The XML schema is documented in `src/parsers/pt9/pt9-xml.md`.
68+
69+
### Data model (`src/types/interlinearizer.d.ts`)
70+
71+
The core types are:
72+
73+
- `InterlinearAlignment` — top-level bilingual container (source + target `InterlinearText`)
74+
- `Book → Segment → Token` — the text hierarchy
75+
- `TextAnalysis` — flat analysis layer keyed by id (does **not** mirror text hierarchy)
76+
- `TokenAnalysis / Morpheme` — parse and 1:1 glosses; multiple analyses per token are allowed, distinguished by `status`
77+
- `AlignmentLink` — links between source and target tokens/morphemes
78+
- `AlignmentEndpoint` — has either token-level or morpheme-level specificity, never both
79+
80+
Key invariants: `Segment.baselineText.slice(charStart, charEnd) === Token.surfaceText`; at most one `TokenAnalysis` per token may have `status: 'approved'`. Multi-string content is tagged by BCP47 writing-system codes. `tokenSnapshot` fields detect drift when baseline text changes.
81+
82+
### TypeScript path aliases
83+
84+
- `@main``src/main`
85+
- `parsers/*``src/parsers/*`
86+
87+
## Testing
88+
89+
Jest with ts-jest, jsdom environment. PAPI is fully mocked in `__mocks__/`. Coverage is enforced at 100% on all `src/**` files (branches, functions, lines, statements).
90+
91+
`resetMocks: true` is set globally — mock implementations are cleared before every test, so each test must set up its own mocks (typically in `beforeEach`). Never rely on implementation state leaking from a prior test.
92+
93+
`@papi/backend` and `@papi/frontend` mocks are mutually exclusive: backend tests use `papi-backend.ts`, WebView tests use `papi-frontend.ts` + `papi-frontend-react.ts`. Each mock file ends with `export {}` so TypeScript treats it as a module.
94+
95+
### Mock internals
96+
97+
Key semantic properties of the mock setup:
98+
99+
- **`resetMocks: true`** — Mock implementations are cleared before every test. Each test must set up its own mocks (typically in `beforeEach`); never rely on state leaking from a prior test.
100+
- **Backend vs. frontend exclusivity** — Backend tests use `papi-backend.ts`, WebView tests use `papi-frontend.ts` + `papi-frontend-react.ts`. Each mock file ends with `export {}` to be treated as a module.
101+
- **`globalThis.webViewComponent` contract** — The WebView root component is assigned to the global (not exported). Tests must `require()` the module and read `globalThis.webViewComponent` to get the component.
102+
103+
Mock files:
104+
105+
- **`__mocks__/fileMock.ts`** — Stub static asset imports.
106+
- **`__mocks__/papi-backend.ts`** — Mocks with jest fns. Re-exports internal jest fns on the default export as `__mock*` properties (e.g., `papi.__mockRegisterCommand`) so tests can assert on them without re-importing. See file for full list.
107+
- **`__mocks__/papi-core.ts`** — Empty module; exists only for module resolution since `@papi/core` is types-only at runtime.
108+
- **`__mocks__/papi-frontend.ts`** — Stubs `logger` (debug/error/info/warn as jest fns).
109+
- **`__mocks__/papi-frontend-react.ts`** — Stubs PAPI React hooks.
110+
- **`__mocks__/platform-bible-react.tsx`** — Stubs components with appropriate `data-testid` attributes. See file for test IDs.
111+
- **`__mocks__/platform-bible-utils.ts`** — Stubs util functions.
112+
- **`__mocks__/styleInlineMock.ts`** and **`__mocks__/styleMock.ts`** — Stub `.scss?inline` and `.scss`.
113+
- **`__mocks__/web-view-inline.ts`** — Stubs `*.web-view?inline` imports as a null-returning React component.
114+
- **`src/__tests__/test-helpers.ts`** — Exports `createTestActivationContext()` for testing `activate()` without type assertions.
115+
116+
### No type assertions
117+
118+
The ESLint rule `no-type-assertion/no-type-assertion` is enforced. **Never use `as` casts in tests.** Workarounds:
119+
120+
- Inject typed WebView state via function overloads in `useWebViewState` stubs (see `makeProps` in `interlinearizer.web-view.test.tsx`).
121+
- Narrow mock call args with `typeof x === 'string'` instead of `as string`.
122+
123+
## Documentation
124+
125+
Every function and method — exported or internal — must have a JSDoc block with:
126+
127+
- A summary sentence describing what the function does and why it exists (non-obvious behavior only; don't restate the name).
128+
- `@param` for every parameter.
129+
- `@returns` describing the return value (omit only for `void`/`Promise<void>`).
130+
- `@throws` for every error condition the caller must handle; omit if the function never throws.
131+
132+
Type declarations (interfaces, type aliases, enums) must have a JSDoc summary on the type itself and on each field or member whose purpose is not self-evident from its name and type.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"recalc",
5353
"reinitializing",
5454
"reserialized",
55+
"sandboxed",
5556
"scriptio",
5657
"sillsdev",
5758
"steenwyk",

0 commit comments

Comments
 (0)