Docs · Toolchain
`package.vyipac`
package.vyipac is the package manifest: a YAML file at a package's root that
declares its identity, its entry points, its dependencies, and which files
belong to it. This page is the complete schema. For the narrative treatment —
what a package is and how imports resolve — read
guide 14 — Modules and packages;
for the commands that consume the manifest see the CLI.
No manifest is required to get started. A lone .vyi file with only relative
and @pkg imports compiles fine; add package.vyipac when the project needs
an enforced root, named executables, or dependencies.
The file at a glance
name: greeter
version: 0.1.0
lang: 0.0.1
deps:
json: ^1.0.0
entries:
greeter: src/main.vyi
greeter-admin: src/admin.vyi
export: src/lib.vyi
source:
root: src
include:
- "**/*.vyi"
exclude:
- "**/*.tests.vyi"The manifest must be a YAML mapping. These are the seven top-level keys — an
unknown key is flagged in the editor (vyipacUnknownKey, a warning) and
otherwise ignored:
| Key | Type | Required | Purpose |
|---|---|---|---|
name | string | no | the package's identity |
version | string | no | the package's semantic version |
lang | string | no | Vyi language version to build against; pins the stdlib |
deps | map string → string | no | third-party dependencies: name → version constraint |
entries | map string → path | no | executables: bin name → entry file |
export | path | no | the package's library entry (its public API file) |
source | mapping | no | scopes which files belong to the package |
Every key is optional — an empty manifest is valid and still marks a package root.
How the manifest is found
The compiler walks up from the entry file's directory; the first
package.vyipac it meets marks the package root. Everything follows from
that:
- Every
.vyifile under the root belongs to the package (unless a
source scope narrows it).
- Relative imports must stay inside the root. One that escapes —
import { thing } from "../../outside" — is a compile error:
import "../../outside" escapes the package root.
- The compilation's file system is rooted there, so a file deep in the tree
can import a sibling of the root with ../-paths that stay inside it.
When no manifest is found, the build is ad-hoc: the entry file's own directory is the root, no containment is enforced, and the stdlib resolves at its default version.
A manifest that is not valid YAML fails the build immediately with the parser's message, e.g. `parse manifest package.vyipac: yaml: line 1: did not find expected ',' or ']'`.
name, version, lang
name: greeter
version: 0.1.0
lang: 0.0.1name— how consumers refer to the package. Names beginning with@
belong to the standard library (@core, @io, …); use a plain name for
your own packages.
version— the package's own semantic version. Recorded for consumers
and future registry tooling.
lang— the Vyi language version to build against. This pins the
standard library project-wide: every @pkg import in the compilation
resolves at exactly this version. Omitted (or with no manifest at all), the
stdlib resolves at the toolchain's default version. Pinning a version that
is not installed fails every stdlib import — `package "@io" ([email protected]) not
found — and, since @core then cannot load, Result, Ok`, and the other
prelude names go undefined with it.
All three are free-form scalars: the toolchain validates that they are strings
(the editor flags a non-scalar with vyipacWrongType) but does not enforce a
version format today.
deps
deps:
json: ^1.0.0
http: 2.3.0deps maps each third-party dependency to a semantic-version constraint. A
declared dependency is imported by its bare name — no ./, no @:
import { stdout } from "@io" // stdlib: no declaration needed
fn main() !u8 {
stdout.write("only stdlib here\n")
return Ok(0)
}A bare-name import that is not declared in deps is a compile error at the
import line:
import { decode } from "json" // error: unknown dependency "json"
fn main() !u8 { return Ok(0) }Dependency resolution is (planned). The declaration side works today: the
compiler validates bare imports against deps and reports
unknownDependency for undeclared names. The tooling that fetches packages
and populates the local dependency store has not landed yet, so a declared
dependency currently fails one step later with `cannot resolve import
"json"`. Until the resolver lands, the constraint strings are recorded but
not solved.
entries
entries:
greeter: src/main.vyi
greeter-admin: src/admin.vyientries names the package's executables (bins). Each key is a bin name; each
value is the path of that bin's entry file, relative to the manifest's
directory. A program is the import graph rooted at an entry — one
package can ship several bins, each rooting its own program.
Two rules are enforced per entry, live in the editor and when vyi build
resolves a bin name:
| Rule | Diagnostic | Message shape |
|---|---|---|
| the entry file must exist | entryFileMissing | entry "ghost" points at "src/nope.vyi", which does not exist |
it must contain a top-level main | entryFileNoMain | entry "broken" points at a file that has no main: "src/lib.vyi" |
"Contains a main" means a top-level fn main declaration (or a top-level
con main bound to a function value). This check is purely syntactic
presence; the entry contract — main takes nothing and returns !u8 —
is enforced when the program actually compiles:
import { stdout } from "@io"
fn main() !u8 {
stdout.write("hello from the greeter bin\n")
return Ok(0)
}vyi build accepts a bin name in place of a file path — any positional
argument that doesn't end in .vyi is looked up in the current directory's
entries:
vyi build -o greeter greeter # by bin name, via the manifest
vyi build -o greeter src/main.vyi # by path — entries not consultedAn unknown name lists what's available: `no bin "nosuch" in package.vyipac
entries (have: greeter, greeter-admin). A package with no entries` block
gets `package.vyipac declares no entries (pass a .vyi path, or add an entries
block)`. See the CLI.
export
export: src/lib.vyiexport names the package's library entry: the single file other packages
receive when they import this package by name — its public API surface. It is
conventionally a barrel that re-exports the public names. Unlike an entries
file, it needs no main; the only rule is that the path must exist
(entryFileMissing otherwise: `export points at "src/lib.vyi", which does not
exist). A package can declare both entries (its tools) and export` (its
library surface), or either alone.
Today the field is declared and validated, but the import machinery reaches a
name-imported package through the file package.vyi at its root — the
convention the standard packages follow. Routing dependency imports through
export lands together with the dependency resolver (planned).
source
source:
root: src
include:
- "**/*.vyi"
exclude:
- "vendor/**"
- "**/*.tests.vyi"source scopes which files under the manifest's directory belong to the
package. Absent, every .vyi under the directory belongs. Present, a file
belongs when it passes all three tests, in order:
- it sits under
root(a manifest-dir-relative directory; omitted or
. means the manifest's own directory; the editor flags a non-directory
with sourceRootMissing),
- it matches at least one
includeglob (an absent or empty list
includes every .vyi under root),
- it matches no
excludeglob (applied after include).
Globs are matched against paths relative to root, /-separated:
| Pattern | Matches |
|---|---|
* | any run of characters within one path segment |
? | exactly one character within a segment |
** | zero or more whole segments |
So **/*.tests.vyi matches test files at any depth (including directly under
root — ** can match zero segments), while vendor/*.vyi matches only files
immediately inside vendor/.
The scope governs discovery and checking: which files the workspace tooling enumerates, checks, and treats as package members. It does not cut imports — an explicit import resolves wherever it points, even at an excluded file.
Which manifest governs a file
Manifests can nest — a package vendored inside another, a sub-project in a
monorepo. For any .vyi file, exactly one manifest governs it: the
deepest package.vyipac at or above it in the directory tree.
- The governing manifest's
sourcescope — and only that one — decides
whether the file belongs. Outer manifests never re-filter files that a nested manifest governs.
- A manifest without a
sourcescope still claims its whole directory:
everything under it belongs to it, not to an ancestor package.
- A file with no manifest above it always belongs (ad-hoc mode).
So given package.vyipac at the repo root and another at vendor/lib/, the
root manifest's exclude: ["vendor/**"] is not what keeps vendor/lib/x.vyi
out of the root package — the nested manifest already governs it.
Package capabilities (registry)
Authors do not declare capability flags or target support in
package.vyipac. When a package is published, the package registry records
the privileges and platforms it may use — including hasBuiltIns,
hasInlineMarkers, hasFileSystemAccess, hasNetworkAccess, and
supportedTargets. Local application manifests only carry identity, sources,
entries, and dependencies.
That registry-side grant model is (planned); today the bootstrap compiler still recognises a few of these flags internally for the standard library.
Validation summary
Where each manifest-level finding surfaces:
| Diagnostic | Meaning | Surfaces |
|---|---|---|
| YAML parse error | the manifest is not valid YAML | fails every build under the root |
vyipacUnknownKey (warning) | a key outside the schema | editor |
vyipacWrongType | a value of the wrong YAML shape | editor |
entryFileMissing | an entries/export path that does not exist | editor; vyi build by bin name |
entryFileNoMain | an entries file with no top-level main | editor; vyi build by bin name |
sourceRootMissing | source.root is not a directory | editor |
unknownDependency | a bare import not declared in deps | compiling any program that imports it |
importEscapesRoot | a relative import leaving the package root | compiling any program that imports it |
The editor tooling (the language server) also completes the schema's keys
inside package.vyipac documents.