agentleFS
ivanestarivanestar/eitr/.agents/skills/stack-scaler/SKILL.md

stack-scaler

Skill4 starsChanged 2 months agoApache-2.0View on GitHub

Step-by-step guide for extending the Eitr codebase to support a new programming language and/or E2E test automation tool. Covers the full pipeline from CLI questionnaire through plan.ts generator registry to template generation and tests.

The file

Show its rules and text

We split this skill into the rules it gives an agent

13 rules. About 0 are common, 5 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every step is isolated: you must modify 0 existing generators.

    line 14
  2. Very specific:

    Missing File Rule: If a required file (e.g. schema.ts or generate.ts) is missing, you must immediately abort and output EXACTLY: ERROR: …

    line 20
  3. Very specific:

    Test Failure Rule: If npm test fails after your changes, you must revert the modifications and output EXACTLY: ERROR: Tests failed, changes …

    line 21
  4. Distinctive:

    Before touching any file, understand the full data flow:

    line 28
  5. Very specific:

    Write template render functions under packages/engine/src/plan/templates/<lang>/.

    line 49
  6. Very specific:

    Add a new class below PlaywrightTsGenerator.

    line 139
  7. Distinctive:

    Create a dedicated subdirectory for the new stack templates.

    line 198
  8. Very specific:

    If you add >0 new AI rule templates, implement the corresponding native render functions so that planSharedScaffold in …

    line 231
  9. Very specific:

    Add PROJECT_FILES_<LANG> and CREATE_IF_ABSENT_<LANG> constants and a new describe block that mirrors the existing TypeScript block:

    line 242
  10. Very specific:

    Add packages/engine/test/python-templates.test.ts (or equivalent) with:

    line 262
  11. Very specific:

    Verify base_page.py contains async def, and 0 assert keywords.

    line 265
  12. Distinctive:

    Add a test that:

    line 271
  13. Distinctive:

    Before opening a PR, verify 100% of items:

    line 290

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Eitr Stack Scaler

Purpose

This skill guides you through adding support for a new language + automation tool combination to the Eitr generator. After following it, running eitr init and selecting the new combination will produce a project that successfully executes >0 tests.

Every step is isolated: you must modify 0 existing generators.


Boundary Constraints & Edge Cases

  • Missing File Rule: If a required file (e.g. schema.ts or generate.ts) is missing, you must immediately abort and output EXACTLY: ERROR: Required file not found.
  • Test Failure Rule: If npm test fails after your changes, you must revert the modifications and output EXACTLY: ERROR: Tests failed, changes reverted.
  • Never Modify Existing: You must make 0 changes to existing TargetGenerator implementations.

Architecture Overview

Before touching any file, understand the full data flow:

eitr init
  └─ packages/cli/src/questionnaire/schema.ts   ← declares question choices
  └─ packages/cli/src/questionnaire/driver.ts   ← filters tools by language
  └─ .eitr/init.json                            ← persisted answers

eitr generate
  └─ packages/cli/src/commands/generate.ts      ← reads init.json, guards unsupported combos,
  │                                                runs recon, builds StackProfile, calls plan()
  └─ packages/engine/src/plan/plan.ts           ← dispatches to the correct TargetGenerator
  └─ packages/engine/src/plan/templates/<lang>/ ← pure render functions (no I/O)
  └─ packages/engine/src/apply/apply.ts         ← writes files to disk (already works for all)

The changes needed for a new combination are:

  1. Register the tool in the questionnaire schema.ts.
  2. Lift the guard in generate.ts.
  3. Implement a TargetGenerator class and register it in plan.ts.
  4. Write template render functions under packages/engine/src/plan/templates/<lang>/.
  5. Add >2 unit tests.

Step 1 — Questionnaire: Register the new language and tool

File: packages/cli/src/questionnaire/schema.ts

1a. Add the language (if new)

// LANGUAGE_CHOICES array — add the new entry
const LANGUAGE_CHOICES: readonly Choice[] = [
  { label: 'TypeScript', value: 'typescript' },
  { label: 'Python', value: 'python' }, // ← example
  // ...
];

1b. Add the automation tool (if new)

// AUTOMATION_TOOL_CHOICES array
const AUTOMATION_TOOL_CHOICES: readonly Choice[] = [
  { label: 'Playwright', value: 'playwright' },
  { label: 'pytest', value: 'pytest' }, // ← example
  // ...
];

1c. Add the default output directory

export function defaultOutputDirForAutomationTool(tool?: string): string {
  switch ((tool ?? '').toLowerCase()) {
    case 'pytest':
      return 'PytestTests'; // ← example
    // ...
  }
}

1d. Wire tool–language compatibility

export function isToolSupportedByLanguage(tool: string, language: string): boolean {
  // Playwright and Selenium work with any language — they stay as-is.
  switch (language) {
    case 'python':
      return tool === 'pytest' || tool === 'playwright' || tool === 'selenium';
    // ...
  }
}

Step 2 — CLI Generate: Lift the unsupported-combo guard

File: packages/cli/src/commands/generate.ts

The current guard (lines ~91–96) rejects any combination that is not typescript + playwright. Extend it to allow the new combo:

const SUPPORTED: Array<{ language: string; automationTool: string }> = [
  { language: 'typescript', automationTool: 'playwright' },
  { language: 'python', automationTool: 'pytest' }, // ← add new entry
];

const supported = SUPPORTED.some((s) => s.language === language && s.automationTool === tool);
if (!supported) {
  process.stderr.write(
    `eitr generate: generation for "${language}" + "${tool}" is not implemented yet.\n`,
  );
  return 1;
}

Important: Do NOT remove the existing guard logic. Simply extend the supported list.


Step 3 — Engine: Implement a TargetGenerator

File: packages/engine/src/plan/plan.ts

3a. Create the class

Add a new class below PlaywrightTsGenerator. You must edit 0 existing classes.

/**
 * Generator for <Language> + <Tool> (e.g. Python + pytest).
 * Isolated from the TypeScript generator: shares 0 code paths.
 */
export class PytestPythonGenerator implements TargetGenerator {
  readonly language = 'python';
  readonly automationTool = 'pytest';

  plan(profile: StackProfile, opts: PlanOptions): FileDescriptor[] {
    const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
    const projectName = opts.projectName ?? 'pytest-tests';
    const ciCd = opts.ciCd ?? 'none';

    const files: FileDescriptor[] = [
      // ── config ──────────────────────────────────────────────────────────
      {
        path: 'pytest.ini',
        writePolicy: 'create-if-absent',
        provenance: { origin: 'project' },
        source: { kind: 'inline', text: renderPytestIni({ baseUrl, projectName }) },
      },
      // ── conftest ────────────────────────────────────────────────────────
      {
        path: 'conftest.py',
        writePolicy: 'create-if-absent',
        provenance: { origin: 'project' },
        source: { kind: 'inline', text: renderPytestConftest() },
      },
      // ── base components ─────────────────────────────────────────────────
      {
        path: 'components/base/base_page.py',
        writePolicy: 'create-if-absent',
        provenance: { origin: 'base' },
        source: { kind: 'inline', text: renderPythonBasePage() },
      },
      // ... add all other files
    ];

    return files;
  }
}

3b. Register it in the TARGET_GENERATORS registry

export const TARGET_GENERATORS: TargetGenerator[] = [
  new PlaywrightTsGenerator(),
  new PytestPythonGenerator(), // ← append; never reorder existing entries
];

Step 4 — Templates: Write render functions

Create a dedicated subdirectory for the new stack templates.

Path: packages/engine/src/plan/templates/<lang>/

Example layout for Python + pytest:

packages/engine/src/plan/templates/python/
  pytest-ini.ts         → renderPytestIni()
  conftest.ts           → renderPytestConftest()
  base-page.ts          → renderPythonBasePage()
  button.ts             → renderPythonButton()
  text-input.ts         → renderPythonTextInput()
  page-readme.ts        → renderPythonPageReadme()

Template authoring rules

  1. Pure functions only — 0 fs calls, 0 fetch calls, 0 side effects.
  2. Language conventions — use snake_case for Python, camelCase for JS/TS, etc.
  3. Method Safety Contract — enforce in every base class template:
    • Actions → return None (Python) / Promise<void> (TS).
    • Producers → @property (Python) / getter (TS) returning component instance.
    • Snapshot reads → _now() suffix (Python) / Now() suffix (TS).
    • 0 assertions inside component or page classes.
  4. AI rules — Generate native .claude/, .cursor/, .devin/, .codex/, or .agents/skills/ files. The stack-scaler rule must NOT be included.

Step 5 — Native AI Rules Templates

File: packages/engine/src/plan/templates/ai-rules.ts

If you add >0 new AI rule templates, implement the corresponding native render functions so that planSharedScaffold in packages/engine/src/plan/shared.ts generates them for each supported AI assistant.


Step 6 — Unit Tests

6a. Snapshot test

File: packages/engine/test/plan.snapshot.test.ts

Add PROJECT_FILES_<LANG> and CREATE_IF_ABSENT_<LANG> constants and a new describe block that mirrors the existing TypeScript block:

describe('plan() snapshot — Python + pytest', () => {
  it('emits a complete project for python + pytest', () => {
    const paths = plan(baselineStackProfile('/fake'), {
      ...planOptions(),
      language: 'python',
      automationTool: 'pytest',
    })
      .files.map((f) => f.path)
      .sort();
    expect(paths).toEqual(PYTHON_PROJECT_FILES.sort());
  });
});

6b. Template unit tests

Add packages/engine/test/python-templates.test.ts (or equivalent) with:

  • Snapshot or string-contains checks for each render function.
  • Verify base_page.py contains async def, and 0 assert keywords.

6c. CLI guard test

File: packages/cli/test/generate.test.ts

Add a test that:

  1. Writes init.json with { language: 'python', automationTool: 'pytest' }.
  2. Asserts runGenerate(...) returns 0.
  3. Asserts conftest.py exists in the output directory.

Step 7 — Build & Verify

npm run build       # must compile with 0 TypeScript errors
npm test            # 100% of tests must pass

Checklist

Before opening a PR, verify 100% of items:

  • schema.ts: new language and/or tool choice added to all relevant arrays
  • schema.ts: isToolSupportedByLanguage() updated
  • schema.ts: defaultOutputDirForAutomationTool() updated
  • generate.ts: supported-combo guard extended (0 existing entries removed)
  • plan.ts: new TargetGenerator class added below existing ones
  • plan.ts: class appended to TARGET_GENERATORS (0 reordering)
  • Templates directory created under packages/engine/src/plan/templates/<lang>/
  • 100% of render functions contain 0 I/O calls
  • Method Safety Contract enforced in base page template
  • Snapshot test extended with new language block
  • CLI generate test covers the new combo (exit 0, files written)
  • npm run build — 0 errors
  • npm test — 100% of tests pass
  • 100% of existing tests pass with 0 modifications

Examples

Good Example

// Adding a new combo (0 existing entries modified or removed)
const SUPPORTED: Array<{ language: string; automationTool: string }> = [
  { language: 'typescript', automationTool: 'playwright' },
  { language: 'python', automationTool: 'pytest' },
];

Bad Example

// Modifying existing combo (Violation: changed existing entry instead of appending)
const SUPPORTED: Array<{ language: string; automationTool: string }> = [
  { language: 'python', automationTool: 'pytest' },
];

Language-specific conventions reference

LanguageNamingConfig fileFixture patternTest file prefix
TypeScriptcamelCaseplaywright.config.tsfixtures.ts*.spec.ts
Pythonsnake_casepytest.iniconftest.pytest_*.py
JavacamelCasepom.xmlJUnit @BeforeEach*Test.java
C#PascalCase*.csprojNUnit [SetUp]*Tests.cs

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.