> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Project structure

> The files lua init creates, which ones you edit, which the CLI owns, and what lua compile adds

A Lua project is a Node.js package whose entry file, `src/index.ts`, holds one `LuaAgent`; the CLI compiles everything the agent references and records server-side ids and versions beside your code. This page lists every file `lua init` writes, who edits it, and what `lua compile` adds.

*Verified against lua-cli 3.33.0.*

## Files after lua init

```text theme={null}
my-agent/
├── .npmignore
├── env.example
├── lua.skill.yaml
├── node_modules/
├── package-lock.json
├── package.json
├── QUICKSTART.md
├── README.md
├── src/
│   └── index.ts
└── tsconfig.json
```

`lua init --with-examples` adds `examples/` with `README.md`, `skills/` (three skills, their tools under `skills/tools/`), `webhooks/`, `jobs/`, `preprocessors/`, `postprocessors/`, `services/`, and `workflows/`. Nothing under `examples/` is compiled until you import it from the agent.

## Who edits what

| File                         | Edited by | Purpose                                                                                                                                |
| ---------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `src/index.ts`               | You       | The [agent](/concepts/agents): `name`, `persona`, and every registered primitive. `lua init` fills in the name and a persona template. |
| `src/**`                     | You       | Tools, skills, webhooks, jobs, and the rest, in any layout. The compiler follows imports from the entry file.                          |
| `lua.skill.yaml`             | CLI       | State only: agent and organization ids, each primitive's server id and version, backup state.                                          |
| `package.json`               | You       | `lua-cli ^3.33.0`, `zod`, and the libraries the examples use. Remove what you don't need.                                              |
| `tsconfig.json`              | You       | TypeScript settings for your editor and `tsc`: `strict`, ES2020, bundler resolution.                                                   |
| `env.example`                | You       | Names of the variables your code reads with `env()`. Copy it to `.env` for local runs; deployed agents read `lua env`, not `.env`.     |
| `.npmignore`                 | You       | The scaffold's ignore list. See [Ignore files](#ignore-files).                                                                         |
| `README.md`, `QUICKSTART.md` | You       | Scaffold notes. Delete or replace them.                                                                                                |

## What lua compile adds

```text theme={null}
my-agent/
├── .gitignore          # created with `dist-v2/` if missing
└── dist-v2/
    ├── manifest.json   # every compiled primitive: kind, name, version
    ├── artifacts/      # one folder per kind: agent/, skill/, tool/, webhook/, job/, …
    └── sources/        # content-addressed copies of your files, uploaded as the backup
```

`lua test`, `lua chat -e sandbox`, and `lua push` compile first, so `dist-v2/` appears after any of them. `dist/` exists only if you run `npm run build` (plain `tsc`); the CLI never reads it.

## Ignore files

The scaffold's ignore list is published as `.npmignore` (npm renames a package's `.gitignore` when it publishes), and `lua init` copies it under that name. It ignores `dist/`, `dist-v2/`, `*.js`, `*.js.map`, `node_modules/`, `.env`, `.env.local`, `.vscode/`, `.idea/`, `.lua/`, `.DS_Store`, and `Thumbs.db`. `lua compile` writes a `.gitignore` containing only `dist-v2/`. Before your first commit, copy the full list: `cp .npmignore .gitignore`.

## lua.skill.yaml

`lua.skill.yaml` maps the project to its agent and records what has been pushed; it holds no configuration, so the persona, tools, and skills live only in code.

```yaml lua.skill.yaml theme={null}
agent:
  agentId: agent_abc123
  orgId: org_abc123
skills:
  - name: weather
    skillId: skill_abc123
    version: 1.0.1
webhooks: []
jobs: []
preprocessors: []
postprocessors: []
mcpServers: []
backup:
  activeVersion: 1
  lastHash: 9ece6ecf2f3b19fdeb10437bb2c100e7572945fb8982e239b7db14bdfd522e8b
  lastPushedAt: '2026-09-12T17:25:43.688Z'
workflows: []
```

The CLI writes ids the first time a primitive reaches the server and bumps `version` on each push. Don't edit `version` by hand: push and sync overwrite it with the server's active version before the push reads it. To choose the next version, pass `lua push <type> --set-version <ver>`; `--force` bumps the patch number. Every field is listed in the [`lua.skill.yaml` reference](/reference/cli/lua-skill-yaml).

## Naming

* Tool `name`: lowercase letters, digits, hyphens, and underscores, starting with a letter; the compiler warns otherwise. The scaffold uses snake\_case (`get_weather`). It is what the model calls and what `lua test --name` selects.
* Skill, webhook, and job `name`: kebab-case (`weather`, `order-status`, `daily-summary`). Each is the primitive's identifier on the server, so renaming one creates a new primitive.
* Files: a convention, not a rule. The scaffold uses `src/skills/tools/<Name>Tool.ts` with a default-exported class, `src/skills/<name>.skill.ts`, `src/webhooks/<Name>Webhook.ts`, and `src/jobs/<Name>Job.ts`.
* Entry file: the compiler looks for `index.ts`, `src/index.ts`, `agent.ts`, `src/agent.ts`, `main.ts`, then `src/main.ts`, and uses the first one that exists.

## See also

* [`lua.skill.yaml` reference](/reference/cli/lua-skill-yaml)
* [`lua init`](/reference/cli/init)
* [`lua compile`](/reference/cli/compile)
* [About agents](/concepts/agents)
* [Quickstart](/get-started/quickstart)
