npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tagalingo/grammar-engine

v0.1.4

Published

Tagalog grammar engine for conjugation, decomposition, and drill generation

Readme

@tagolingo/grammar-engine

A rule-based Tagalog verb engine for conjugation, decomposition, case markers, and drill generation. Pure TypeScript with bundled JSON data -- no network, no database, fully offline.

What It Does

Given a verb root like "luto" (cook), the engine can:

  • Conjugate it across 5 focus types and 4 aspects
  • Decompose a conjugated form like "nagluluto" back to its root, focus, and aspect
  • Assign case markers (ang/ng/sa/si/ni/kay) to nouns based on focus type
  • Generate drills with a correct answer and plausible distractors

All functions are synchronous, pure, and return a Result<T, E> type -- check .ok before accessing .value.

Installation

npm install @tagolingo/grammar-engine

Usage

Conjugate a verb

import { conjugate } from '@tagolingo/grammar-engine'

conjugate('luto', 'AF', 'completed')
// { ok: true, value: 'nagluto' }

conjugate('kain', 'AF', 'infinitive')
// { ok: true, value: 'kumain' }

conjugate('luto', 'IF', 'completed')
// { ok: false, error: 'FOCUS_NOT_SUPPORTED', message: 'Root "luto" does not support IF' }

Get a full conjugation grid

import { conjugationGrid } from '@tagolingo/grammar-engine'

const result = conjugationGrid('luto')
// result.value.grid['AF']['completed']    -> 'nagluto'
// result.value.grid['OF']['infinitive']   -> 'lutuin'
// result.value.blocked                    -> forms that don't exist for this verb

Decompose a conjugated form

import { decompose, decomposeKnown } from '@tagolingo/grammar-engine'

// "What verb is this?" -- returns candidates sorted by frequency
decompose('nagluluto')
// [{ root: 'luto', focus: 'AF', aspect: 'incompleted', affix_class: 'mag', steps: [...] }]

// When you already know the root
decomposeKnown('nagluluto', 'luto')
// { root: 'luto', focus: 'AF', aspect: 'incompleted', ... }

Assign case markers

import { assignMarkers } from '@tagolingo/grammar-engine'

assignMarkers('AF', [
  { role: 'actor', noun: 'Maria', type: 'name' },
  { role: 'patient', noun: 'pagkain', type: 'common' },
])
// [
//   { noun: 'Maria', role: 'actor', marker: 'si', focused: true },
//   { noun: 'pagkain', role: 'patient', marker: 'ng', focused: false }
// ]

// Switch to Object Focus and the markers shift
assignMarkers('OF', [
  { role: 'actor', noun: 'Maria', type: 'name' },
  { role: 'patient', noun: 'pagkain', type: 'common' },
])
// Maria gets 'ni', pagkain gets 'ang'

Generate a drill

import { generateDrill } from '@tagolingo/grammar-engine'

generateDrill('luto', 'AF', 'completed')
// {
//   prompt: 'What is the completed Actor Focus form of luto?',
//   correct_answer: 'nagluto',
//   distractors: ['magluto', 'nagluluto', 'magluluto']
// }

Distractors are chosen to be plausible: same-focus-different-aspect first, then different-focus-same-aspect, then cross-combinations.

How Irregular Verbs Work

The engine handles irregularity through four mechanisms in verbs.json, applied in priority order during conjugation:

1. blocked_forms -- "This form doesn't exist"

Prevents generation of forms that aren't used in modern Tagalog. Returns a FORM_BLOCKED error.

{ "root": "dukot", "blocked_forms": ["AF_contemplated"] }

2. stem_overrides -- "Just use this exact form"

Completely bypasses all rules for a specific affix-class + aspect combination. The key format is {affix_class}_{aspect}.

{
  "root": "some_verb",
  "stem_overrides": {
    "mag_completed": "the_correct_irregular_form"
  }
}

3. irregular_flags -- "Apply this special rule"

Activates specific spelling transformations. Currently recognized:

  • nasal_substitution -- for mang-/nang- class verbs where the prefix fuses with the root's initial consonant (p/b to m, t/d/s to n, k to ng)
{ "root": "bili", "irregular_flags": ["nasal_substitution"] }

4. root_phonology -- "This root has a special sound pattern"

Tells the engine about phonological features that affect how standard rules apply:

  • vowel_initial -- triggers ni- to in- conversion for -in/-an/i- classes (e.g., "niaral" becomes "inaral")
  • consonant_cluster_initial -- uses cluster-aware reduplication (e.g., "trabaho" becomes "tatrabaho" instead of "tratrabaho")
{ "root": "aral", "root_phonology": ["vowel_initial"] }
{ "root": "trabaho", "root_phonology": ["consonant_cluster_initial"] }

Fixing Incorrect Conjugations

All fixes are edits to src/data/verbs.json. No code changes needed. The decompose() reverse index rebuilds automatically from conjugate(), so fixes propagate everywhere.

The form is totally wrong and no rule can produce the right answer: Add a stem_overrides entry to hardcode the correct form.

The form shouldn't exist at all: Add the {focus}_{aspect} key to blocked_forms.

A needed spelling rule isn't firing (or the wrong one is): Fix irregular_flags or root_phonology on the verb entry.

The verb is using the wrong affix class for a given focus: Fix the focus_map entry to point to the correct affix class.

After making a fix, run npm test to verify nothing else broke.

Types

type FocusType = 'AF' | 'OF' | 'LF' | 'BF' | 'IF'
type Aspect = 'infinitive' | 'completed' | 'incompleted' | 'contemplated'
type SemanticRole = 'actor' | 'patient' | 'location' | 'beneficiary' | 'instrument'
type NounType = 'common' | 'name' | 'name_plural'
type MarkerSet = 'ang' | 'ng' | 'sa' | 'si' | 'ni' | 'kay' | 'sina' | 'nina' | 'kina'
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E; message: string }

Project Structure

src/
  engine/
    conjugate.ts              # Core conjugation (affix lookup + spelling rules)
    decompose.ts              # Reverse: conjugated form -> root + focus + aspect
    conjugation-grid.ts       # Full focus x aspect table for a root
    generate-drill.ts         # Quiz generation with smart distractors
    markers.ts                # Focus-driven case marker assignment
  rules/
    spelling-rules.ts         # Reduplication, nasal substitution, um-infixing, etc.
    linker.ts                 # -ng / na linker selection
  data/
    affix-table.json          # 8 affix classes x 4 aspects -> patterns
    verbs.json                # Verb database with roots, focus maps, overrides, phonology
  types.ts                    # All shared types
  index.ts                    # Public API barrel export
scripts/
  scrape-verbs.ts             # Fetch conjugation tables from reference sites
  classify-verbs.ts           # LLM fallback for unscrapeable roots
  validate-verbs.ts           # Cross-check forms against engine rules
__tests__/
  conjugate.test.ts
  decompose.test.ts
  generate-drill.test.ts
  markers.test.ts
  data-validation.test.ts

Development

npm install
npm run build       # tsup -> dist/ (CJS + ESM + DTS)
npm test            # vitest
npm run typecheck   # tsc --noEmit

Dictionary Enhancement Pipeline

A one-off batch pipeline (scripts/dictionary-enhance/) that processes the Tagalog dictionary CSV through Claude to verify definitions, replace unsuitable example sentences, and add missing English translations.

Setup

  1. Add your Anthropic API key to a .env file in the project root:

    ANTHROPIC_API_KEY=sk-ant-...
  2. The pipeline processes the CSV at:

    ~/Downloads/tagalog_vocab_entries_with_examples - tagalog_vocab_entries_with_examples.csv

Scripts

| Script | Command | What it does | |---|---|---| | main.ts | npm run dict:enhance | Reads the first 2,000 rows of the source CSV, runs each entry through three sequential agent passes (definition check, sentence evaluation, translation), and writes the enhanced CSV to scripts/dictionary-enhance/output/tagalog_dictionary_enhanced.csv | | verify.ts | npm run dict:verify | Reads the enhanced CSV, checks every entry for missing fields, and runs a quality review across all entries in batches -- flagging incorrect definitions, inappropriate sentences, and inaccurate translations. Writes all issues to output/issues.json | | fix.ts | npm run dict:fix | Reads output/issues.json and sends each flagged entry back to Claude with the specific issue and suggestion. Applies fixes to the CSV in place. Re-translates any entries whose sentences were replaced |

Running the full pipeline

# 1. Enhance all 2,000 entries (20-30 min, ~$1-3 in API costs)
npm run dict:enhance -- --batch-size=100 --concurrency=2

# 2. Verify quality across all entries
npm run dict:verify -- --batch-size=100 --concurrency=2

# 3. Auto-fix flagged issues
npm run dict:fix

# 4. Re-verify to confirm issues are resolved
npm run dict:verify -- --batch-size=100 --concurrency=2

All three scripts accept --batch-size=N and --concurrency=N flags. dict:enhance also accepts --limit=N to process fewer rows (useful for testing).

Output

scripts/dictionary-enhance/output/
  tagalog_dictionary_enhanced.csv   # Enhanced CSV (Definition, Example_Sentence,
                                    # Example_Translation filled in for all rows)
  issues.json                       # Issues found by verify.ts
  run-log.json                      # Per-batch stats from the enhance run

Three provenance columns are added to the CSV:

| Column | What it contains | |---|---| | Enhanced_By | Comma-separated list of passes that modified this entry: definition, sentence, translation, fix | | Original_Definition | The original definition, preserved when it was changed | | Original_Example | The original example sentence, preserved when it was replaced |

Engine Pipeline

The conjugation engine applies three layers in order:

  1. Affix Rule Lookup -- maps (affix_class x aspect) to an affix pattern from the table
  2. Spelling Adjustments -- reduplication, nasal substitution, um-infixing, suffix vowel harmony, vowel-initial prefix fixes
  3. Sentence Framing (future) -- template-based sentence generation with slot fillers and linker rules

Affix Classes

| Class | Typical Focus | Example (luto) | |---|---|---| | mag | Actor | magluto, nagluto, nagluluto, magluluto | | um | Actor | kumain, kumain, kumakain, kakain | | ma (stative) | Actor | matulog, natulog, natutulog, matutulog | | maka (potentive) | Actor | makakita, nakakita, nakakikita, makakikita | | mang (distributive) | Actor | mamili, namili, namamili, mamamili | | in | Object | lutuin, niluto, niluluto, lutuin | | i | Benefactive/Instrumental | iluto, iniluto, iniluluto, iluluto | | an | Locative | lutuan, nilutuan, nilulutuan, lulutuan |

Data Pipeline

Verb data is populated at build time:

  1. scrape-verbs.ts fetches conjugation tables from reference sites
  2. classify-verbs.ts uses Claude API as a fallback for roots not found online
  3. validate-verbs.ts cross-checks scraped forms against engine rules

Output is committed to verbs.json.