create-nola-lang 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +8 -3
  2. package/dist/{chunk-LUSVIOAN.js → chunk-CB26Z47Y.js} +53 -87
  3. package/dist/examples.d.ts +4 -2
  4. package/dist/examples.js +4 -2
  5. package/dist/flow.d.ts +8 -2
  6. package/dist/flow.js +15 -12
  7. package/dist/github.d.ts +1 -1
  8. package/dist/github.js +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/launch.d.ts +2 -2
  11. package/dist/launch.js +1 -1
  12. package/dist/main.js +1 -1
  13. package/dist/node-version.d.ts +3 -2
  14. package/dist/node-version.js +4 -3
  15. package/dist/providers.d.ts +1 -1
  16. package/dist/providers.js +1 -1
  17. package/dist/registry.d.ts +19 -10
  18. package/dist/registry.js +20 -5
  19. package/dist/scaffold.d.ts +19 -15
  20. package/dist/scaffold.js +63 -113
  21. package/package.json +1 -1
  22. package/skills/nola/SKILL.md +48 -16
  23. package/skills/nola/references/config.md +5 -5
  24. package/skills/nola/references/patterns.md +67 -31
  25. package/skills/nola/references/pitfalls.md +200 -52
  26. package/skills/nola/references/syntax.md +221 -122
  27. package/templates/empty/package.json +5 -5
  28. package/templates/empty/src/main.tsi +8 -0
  29. package/templates/empty/src/main.ts +0 -5
  30. package/templates/feature-extraction/README.md +0 -20
  31. package/templates/feature-extraction/nola.config.ts +0 -14
  32. package/templates/feature-extraction/nola.replay.jsonl +0 -2
  33. package/templates/feature-extraction/package.json +0 -22
  34. package/templates/feature-extraction/src/main.tsi +0 -22
  35. package/templates/feature-extraction/tsconfig.json +0 -12
  36. package/templates/function-calling/README.md +0 -21
  37. package/templates/function-calling/nola.config.ts +0 -14
  38. package/templates/function-calling/nola.replay.jsonl +0 -1
  39. package/templates/function-calling/package.json +0 -22
  40. package/templates/function-calling/src/main.tsi +0 -12
  41. package/templates/function-calling/src/tickets.ts +0 -17
  42. package/templates/function-calling/tsconfig.json +0 -12
  43. package/templates/typescript-interop/README.md +0 -15
  44. package/templates/typescript-interop/nola.config.ts +0 -14
  45. package/templates/typescript-interop/nola.replay.jsonl +0 -1
  46. package/templates/typescript-interop/package.json +0 -22
  47. package/templates/typescript-interop/src/main.ts +0 -8
  48. package/templates/typescript-interop/src/person.tsi +0 -11
  49. package/templates/typescript-interop/tsconfig.json +0 -12
@@ -13,16 +13,14 @@ export interface ScaffoldOptions {
13
13
  /**
14
14
  * The inference provider chosen in the wizard; default "none" (the
15
15
  * template's own config). Any live provider replaces `nola.config.ts` with
16
- * that provider's config, skips the offline templates' replay ledger, and renders
17
- * the matching README notes.
16
+ * that provider's config and drops the offline templates' replay ledger.
18
17
  */
19
18
  provider?: ProviderId;
20
19
  /**
21
20
  * The editor chosen in the wizard; default "none". Decides the next-steps
22
- * comment rendered at the top of the template's src/main.ts: with VS Code
23
- * it names F5, breakpoints and the recommended extension (all of which the
24
- * .vscode files the flow writes make true); without one it stays
25
- * editor-neutral.
21
+ * comment that opens the template's entry file: with VS Code it names F5,
22
+ * breakpoints and the recommended extension (all of which the .vscode
23
+ * files the flow writes make true); without one it stays editor-neutral.
26
24
  */
27
25
  ide?: "vscode" | "none";
28
26
  }
@@ -42,23 +40,29 @@ export declare function vendorEnvVar(template: string, provider: ProviderId): st
42
40
  */
43
41
  export declare function envExample(envVar: string): string;
44
42
  /**
45
- * Add the recommended `.gitignore` to an example's files. Examples are copied
46
- * verbatim from `examples/`, where the monorepo's root ignore file covers them,
47
- * so they carry none — one an example does carry wins.
43
+ * Add the recommended `.gitignore` to a template's files. Templates are
44
+ * copied as committed — examples from `examples/`, where the monorepo's root
45
+ * ignore file covers them — so they carry none; one a template does carry wins.
48
46
  */
49
47
  export declare function withRecommendedGitignore(files: Map<string, string>): Promise<Map<string, string>>;
50
48
  /**
51
- * The comment that opens the entry file (src/main.ts, or the `.tsi` entry
52
- * of a one-file template) — the file the scaffold lands the user on (VS Code opens it
53
- * as the active editor), so it carries the first three things to do. The VS
54
- * Code variant only ships with the editor step's .vscode files, which are
55
- * what make F5 and the extension prompt real.
49
+ * The comment that opens the entry file (the `.tsi` entry of a one-file
50
+ * template and of `empty`, else the plain-TS src/main.ts) — the file the
51
+ * scaffold lands the user on (VS Code opens it as the active editor), so it
52
+ * carries the first three things to do. The VS Code variant only ships with
53
+ * the editor step's .vscode files, which are what make F5 and the extension
54
+ * prompt real.
56
55
  */
57
56
  export declare function nextStepsComment(ide: "vscode" | "none", template: string): string;
58
57
  export declare function ownVersion(): Promise<string>;
59
58
  /**
60
59
  * Lay a template down into `targetDir`. One implementation serves both entry
61
- * points: the `create-nola-lang` bin (`npm create nola-lang`) and `nola init`.
60
+ * points — the `create-nola-lang` bin (`npm create nola-lang`) and `nola init`
61
+ * — and both sources: the files are collected as committed, then the scaffold
62
+ * adds what a project needs on top — the chosen provider's config (dropping
63
+ * the replay ledger), the project name and the lockstep ranges in
64
+ * package.json, the recommended .gitignore, the vendor key's .env.example,
65
+ * and the next-steps comment at the top of the entry file.
62
66
  */
63
67
  export declare function scaffold(targetDir: string, opts?: ScaffoldOptions): Promise<ScaffoldResult>;
64
68
  //# sourceMappingURL=scaffold.d.ts.map
package/dist/scaffold.js CHANGED
@@ -5,7 +5,8 @@ import { fileURLToPath } from "node:url";
5
5
  import { collectExampleFromDisk, devExamplesDir, rewriteExamplePackageJson } from "./examples.js";
6
6
  import { fetchExampleFromGitHub } from "./github.js";
7
7
  import { providerById } from "./providers.js";
8
- import { templateByName, templateNames } from "./registry.js";
8
+ import { entryFile, templateByName, templateNames } from "./registry.js";
9
+ /** This package's own templates: `empty` (the one builtin), `_providers/` and `_gitignore`. */
9
10
  const TEMPLATES_DIR = fileURLToPath(new URL("../templates/", import.meta.url));
10
11
  /** The config a live provider choice writes over the template's own (`templates/_providers/<id>.config.ts`). */
11
12
  export function providerConfigUrl(provider) {
@@ -28,36 +29,36 @@ export function vendorEnvVar(template, provider) {
28
29
  export function envExample(envVar) {
29
30
  return `# Copy to .env and fill in — \`nola run\` applies .env before evaluating nola.config.ts.\n${envVar}=\n`;
30
31
  }
31
- /** _gitignore ships underscored (npm pack strips nested .gitignore files). */
32
- const RENAMES = { _gitignore: ".gitignore" };
33
- /** The ONE recommended .gitignore — every scaffold gets it; no template keeps a copy of its own. */
32
+ /** The ONE recommended .gitignore — every scaffold gets it; no template keeps a copy of its own (_gitignore ships underscored: npm pack strips nested .gitignore files). */
34
33
  const GITIGNORE_URL = new URL("../templates/_gitignore", import.meta.url);
35
34
  /**
36
- * Add the recommended `.gitignore` to an example's files. Examples are copied
37
- * verbatim from `examples/`, where the monorepo's root ignore file covers them,
38
- * so they carry none — one an example does carry wins.
35
+ * Add the recommended `.gitignore` to a template's files. Templates are
36
+ * copied as committed — examples from `examples/`, where the monorepo's root
37
+ * ignore file covers them — so they carry none; one a template does carry wins.
39
38
  */
40
39
  export async function withRecommendedGitignore(files) {
41
40
  if (!files.has(".gitignore"))
42
41
  files.set(".gitignore", await readFile(GITIGNORE_URL, "utf8"));
43
42
  return files;
44
43
  }
45
- /** Files whose __NAME__/__VERSION__, README-note and __NEXT_STEPS__ placeholders are substituted. */
46
- const SUBSTITUTED = new Set(["package.json", "README.md", "main.ts", "main.tsi"]);
47
44
  /**
48
- * The comment that opens the entry file (src/main.ts, or the `.tsi` entry
49
- * of a one-file template) — the file the scaffold lands the user on (VS Code opens it
50
- * as the active editor), so it carries the first three things to do. The VS
51
- * Code variant only ships with the editor step's .vscode files, which are
52
- * what make F5 and the extension prompt real.
45
+ * The comment that opens the entry file (the `.tsi` entry of a one-file
46
+ * template and of `empty`, else the plain-TS src/main.ts) — the file the
47
+ * scaffold lands the user on (VS Code opens it as the active editor), so it
48
+ * carries the first three things to do. The VS Code variant only ships with
49
+ * the editor step's .vscode files, which are what make F5 and the extension
50
+ * prompt real.
53
51
  */
54
52
  export function nextStepsComment(ide, template) {
55
53
  if (ide === "vscode") {
56
- const breakpointIn = templateByName(template)?.entry
57
- ? "on the `ask` line below"
58
- : template === "typescript-interop"
59
- ? "in src/person.tsi"
60
- : "in your .tsi file";
54
+ // `empty`'s stub has no ask yet — the breakpoint goes on the one the user writes.
55
+ const breakpointIn = template === "empty"
56
+ ? "on your first `ask` in this file"
57
+ : templateByName(template)?.entry
58
+ ? "on the `ask` line below"
59
+ : template === "typescript-interop"
60
+ ? "in src/person.tsi"
61
+ : "in your .tsi file";
61
62
  return [
62
63
  "// Next steps in VS Code:",
63
64
  "// 1. Press F5 to run this file (.vscode/launch.json is already set up).",
@@ -73,40 +74,7 @@ export function nextStepsComment(ide, template) {
73
74
  ].join("\n");
74
75
  }
75
76
  /** Template files that only make sense for the offline (replay) configuration. */
76
- const OFFLINE_ONLY = new Set(["nola.replay.jsonl"]);
77
- const README_NOTES = {
78
- offline: {
79
- START_NOTE: "works offline, no API key needed",
80
- PROVIDER_NOTE: "This project runs offline: `nola.config.ts` replays answers from the committed\n" +
81
- "`nola.replay.jsonl` ledger. The ledger is keyed by the exact prompt, so once\n" +
82
- "you edit the `.tsi` file or add your own asks, switch the config to a real\n" +
83
- "model (see the comment in `nola.config.ts`): `model: \"nola\"` with a key from\n" +
84
- "`npx nola-lang key` (25 free runs), or your own provider and its key.",
85
- },
86
- nola: {
87
- START_NOTE: "uses your 25 free Nola runs (key in .env)",
88
- PROVIDER_NOTE: "`nola.config.ts` sets `model: \"nola\"` — platform-served inference with the trial key the\n" +
89
- "scaffold wrote to `.env` (git-ignored). Out of runs? `npx nola-lang account` opens\n" +
90
- "your Nola account, where prepaid balance is added, or bring your\n" +
91
- "own model (see the comment in `nola.config.ts`).",
92
- },
93
- };
94
- /** The README notes for a chosen provider: offline, the Nola trial, or a vendor and the env var its factory reads. */
95
- function readmeNotes(provider) {
96
- if (provider === "none")
97
- return README_NOTES.offline;
98
- if (provider === "nola")
99
- return README_NOTES.nola;
100
- const def = providerById(provider);
101
- if (!def?.envVar || !def.model)
102
- throw new Error(`provider "${provider}" has no vendor config`);
103
- return {
104
- START_NOTE: `set ${def.envVar} in .env first`,
105
- PROVIDER_NOTE: `\`nola.config.ts\` sets \`model: ${def.model}\` — ${def.label} serves inference with the key it reads\n` +
106
- `from \`${def.envVar}\`. Copy \`.env.example\` to \`.env\` (git-ignored; \`nola run\` applies it) and fill in\n` +
107
- `\`${def.envVar}\` before the first \`npm start\`, or switch models in \`nola.config.ts\`.`,
108
- };
109
- }
77
+ const OFFLINE_ONLY = ["nola.replay.jsonl"];
110
78
  export async function ownVersion() {
111
79
  const pkg = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
112
80
  return pkg.version;
@@ -123,9 +91,26 @@ async function prepareTarget(absRoot, force) {
123
91
  for (const entry of entries)
124
92
  await rm(join(absRoot, entry), { recursive: true, force: true });
125
93
  }
94
+ /**
95
+ * A template's files as committed: `empty` from this package's templates/
96
+ * dir, every other template from examples/ — the dev checkout's on disk,
97
+ * else GitHub at the lockstep tag. Everything is buffered before the caller
98
+ * touches the target, so a failed fetch scaffolds nothing.
99
+ */
100
+ async function collectTemplate(def, version) {
101
+ if (def.source === "builtin")
102
+ return collectExampleFromDisk(TEMPLATES_DIR, def.name);
103
+ const dev = await devExamplesDir();
104
+ return dev ? collectExampleFromDisk(dev, def.name) : fetchExampleFromGitHub(def.name, version);
105
+ }
126
106
  /**
127
107
  * Lay a template down into `targetDir`. One implementation serves both entry
128
- * points: the `create-nola-lang` bin (`npm create nola-lang`) and `nola init`.
108
+ * points — the `create-nola-lang` bin (`npm create nola-lang`) and `nola init`
109
+ * — and both sources: the files are collected as committed, then the scaffold
110
+ * adds what a project needs on top — the chosen provider's config (dropping
111
+ * the replay ledger), the project name and the lockstep ranges in
112
+ * package.json, the recommended .gitignore, the vendor key's .env.example,
113
+ * and the next-steps comment at the top of the entry file.
129
114
  */
130
115
  export async function scaffold(targetDir, opts = {}) {
131
116
  const root = targetDir;
@@ -134,69 +119,34 @@ export async function scaffold(targetDir, opts = {}) {
134
119
  const def = templateByName(template);
135
120
  if (!def)
136
121
  throw new Error(`unknown template "${template}" (valid: ${templateNames().join(", ")})`);
122
+ const version = await ownVersion();
123
+ const files = await collectTemplate(def, version);
137
124
  await prepareTarget(absRoot, opts.force ?? false);
138
125
  const name = opts.name ?? basename(absRoot);
139
- const version = await ownVersion();
140
- const envVar = vendorEnvVar(template, opts.provider ?? "none");
141
- if (def.source === "example") {
142
- const dev = await devExamplesDir();
143
- const exampleFiles = await withRecommendedGitignore(dev ? await collectExampleFromDisk(dev, template) : await fetchExampleFromGitHub(template, version));
144
- const manifest = exampleFiles.get("package.json");
145
- if (manifest)
146
- exampleFiles.set("package.json", rewriteExamplePackageJson(manifest, { name, version }));
147
- if (envVar !== undefined && !exampleFiles.has(".env.example"))
148
- exampleFiles.set(".env.example", envExample(envVar));
149
- for (const [relPath, content] of exampleFiles) {
150
- const target = join(absRoot, relPath);
151
- await mkdir(join(target, ".."), { recursive: true });
152
- await writeFile(target, content);
153
- }
154
- return { root, files: [...exampleFiles.keys()].sort() };
155
- }
156
- const templateRoot = join(TEMPLATES_DIR, template);
157
- const versionRange = `^${version}`;
158
126
  const provider = opts.provider ?? "none";
159
- const notes = readmeNotes(provider);
160
- const nextSteps = nextStepsComment(opts.ide ?? "none", template);
161
- const files = [];
162
- const copyDir = async (fromDir, relDir) => {
163
- await mkdir(join(absRoot, relDir), { recursive: true });
164
- for (const entry of await readdir(join(templateRoot, fromDir), { withFileTypes: true })) {
165
- const fromRel = join(fromDir, entry.name);
166
- if (entry.isDirectory()) {
167
- await copyDir(fromRel, join(relDir, entry.name));
168
- continue;
169
- }
170
- const toName = RENAMES[entry.name] ?? entry.name;
171
- if (provider !== "none" && OFFLINE_ONLY.has(entry.name))
172
- continue;
173
- const toRel = join(relDir, toName);
174
- // A live provider's config replaces the template's own (both builtin templates keep it at the root).
175
- const source = provider !== "none" && relDir === "." && entry.name === "nola.config.ts"
176
- ? providerConfigUrl(provider)
177
- : join(templateRoot, fromRel);
178
- let content = await readFile(source, "utf8");
179
- if (SUBSTITUTED.has(entry.name)) {
180
- content = content
181
- .replaceAll("__NAME__", name)
182
- .replaceAll("__VERSION__", versionRange)
183
- .replaceAll("__START_NOTE__", notes.START_NOTE)
184
- .replaceAll("__PROVIDER_NOTE__", notes.PROVIDER_NOTE)
185
- .replaceAll("__NEXT_STEPS__", nextSteps);
186
- }
187
- await writeFile(join(absRoot, toRel), content);
188
- files.push(toRel.replaceAll("\\", "/"));
189
- }
190
- };
191
- await copyDir(".", ".");
192
- if (!files.includes(".gitignore")) {
193
- await writeFile(join(absRoot, ".gitignore"), await readFile(GITIGNORE_URL, "utf8"));
194
- files.push(".gitignore");
127
+ // A live provider's config replaces the template's own; the ledger it replayed goes with it.
128
+ if (provider !== "none") {
129
+ for (const offline of OFFLINE_ONLY)
130
+ files.delete(offline);
131
+ files.set("nola.config.ts", await readFile(providerConfigUrl(provider), "utf8"));
195
132
  }
196
- if (envVar !== undefined) {
197
- await writeFile(join(absRoot, ".env.example"), envExample(envVar));
198
- files.push(".env.example");
133
+ const manifest = files.get("package.json");
134
+ if (manifest)
135
+ files.set("package.json", rewriteExamplePackageJson(manifest, { name, version }));
136
+ await withRecommendedGitignore(files);
137
+ const envVar = vendorEnvVar(template, provider);
138
+ if (envVar !== undefined && !files.has(".env.example"))
139
+ files.set(".env.example", envExample(envVar));
140
+ // The entry file greets the user with the next steps — it is what VS Code opens.
141
+ const entry = entryFile(template);
142
+ const program = files.get(entry);
143
+ if (program !== undefined)
144
+ files.set(entry, `${nextStepsComment(opts.ide ?? "none", template)}\n\n${program}`);
145
+ for (const [relPath, content] of files) {
146
+ const target = join(absRoot, relPath);
147
+ await mkdir(join(target, ".."), { recursive: true });
148
+ await writeFile(target, content);
199
149
  }
200
- return { root, files: files.sort() };
150
+ return { root, files: [...files.keys()].sort() };
201
151
  }
202
152
  //# sourceMappingURL=scaffold.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nola-lang",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Scaffold a new Nola project (npm create nola, or npm create nola-lang)",
5
5
  "keywords": [
6
6
  "nola",
@@ -22,24 +22,39 @@ import type { Person } from "./types.js";
22
22
  // thenable Intent<T>. `await`ing it (or `ask`) runs the inference.
23
23
  infer function extractPerson(.text: string) {
24
24
  // `ask` resolves an intent the way `await` resolves a promise.
25
- const person = ask `Extract the person described in the text`<Person>;
25
+ const person = ask `Extract the person described in the text`: Person;
26
26
  return person;
27
27
  }
28
28
  ```
29
29
 
30
- - `infer function name(...)` — declares a nola function. An optional
31
- backtick instruction goes between name and params:
32
- `` infer function name`instruction`(...) ``. `await` is legal in the body.
30
+ - `infer function name(...)` — declares a nola function. Any bare template
31
+ literal on its own line in the body is a CONTEXT STATEMENT: text every
32
+ later `ask` in the invocation sees, read at each ask. It may continue with
33
+ values: `` `Page` oncall `when the ticket is an outage.` `` (a name, a call
34
+ or a bracketed literal; anything else in parentheses — but `await`, `ask`
35
+ and `this` cannot be values: assign them to a local first). Several are
36
+ legal, anywhere in the body; one before a loop shows the current state on
37
+ every pass. Visibility is lexical, like a `const`: a statement inside a
38
+ block reaches only the asks inside those braces, and statements never
39
+ accumulate across loop passes — each ask sees every statement visible at
40
+ its position exactly once, rendered at that ask. `await` is legal in the
41
+ body.
33
42
  - `.name: T` parameters are CONTEXT parameters: their values are shown to
34
43
  the LLM. Plain (no dot) parameters are ordinary values the LLM never
35
44
  sees. `.` is only legal on infer-function parameters. Rule of thumb:
36
45
  one dot marks a value flowing INTO the model (`.name`); a template after
37
46
  `ask` is a value coming OUT.
38
- - `` ask `prompt`<T> `` — an extractor: asks the LLM for a `T`.
39
- `${...}` interpolation works inside the backticks. Anywhere that is NOT
40
- directly after `ask` — a stored intent, a call-intent argument, an
41
- object/array literal — spell it `` ..`prompt`<T> `` (a typed template
42
- there without the dots is NOLA2014); `` ask ..`prompt` `` is still legal.
47
+ - `` ask `prompt`: T `` — an extractor: asks the LLM for a `T`. The colon
48
+ is glued to the closing backtick; `` ask `prompt`<T> `` is the same
49
+ construct (write `: T` unless a page shows otherwise).
50
+ `${...}` interpolation works inside the backticks. The `..` is implied
51
+ directly after `ask` and in a call's argument list — a typed template
52
+ that starts an argument, or a value nested in a plain object/array
53
+ literal there, is an extractor: `` createTicket(`a short title`: string, 2) ``.
54
+ Anywhere else — a stored intent, a ternary, a spread, `new` — spell it
55
+ `` ..`prompt`: T `` (a typed `` `x`<T> `` elsewhere is NOLA2014, a
56
+ `` `x`: T `` a syntax error); `` ask ..`prompt` `` and `` fn(..`x`: T) ``
57
+ are still legal.
43
58
  - `` ask fn`hint`(...) `` — or a plain call whose arguments contain an
44
59
  extractor — is a call intent (the LLM fills the extractor-shaped
45
60
  arguments, then the function runs). Only `` fn`hint`(...) `` carries
@@ -48,12 +63,29 @@ infer function extractPerson(.text: string) {
48
63
  from `nola.config.ts`. The name must be a static identifier. When the
49
64
  platform serves inference (`model: "nola"`) any unconfigured
50
65
  name is legal — it is sent to the platform as a free-form inference profile.
51
- - Prompt templates: inside ANY instruction backticks (marker, extractor,
52
- call hint) a hole that starts with a single dot — `${.member}` — reads
53
- the intent's prompt scope; a literal containing one REPLACES that
54
- intent's prompt block (`${.default}` is the built-in block, `${.next}`
55
- the rest of the prompt). Every other `${expr}` is a lexical value.
56
- See `references/syntax.md` → "Prompt templates".
66
+ - Values in an instruction — two spellings, one rule: `.name` (a contextual
67
+ parameter or `const .name` binding) is DATA, rendered as an `<input
68
+ name>` block the system turn marks as data; a value after text in a
69
+ context statement (`` `Page` oncall ``) or a `${expr}` hole in ANY
70
+ instruction backticks is part of the developer's words, spliced where it
71
+ stands (a string verbatim, a function its name, a call intent its callee
72
+ and arguments, anything else JSON) — the model FOLLOWS it, so interpolate
73
+ only text the developer controls (a team name, a count, a date) and put
74
+ anything a user or a document supplied in `.name`. A context statement is
75
+ read at EACH ask that sees it (module or body alike); an extractor's or
76
+ hint's text when the ask runs. A Nola construct (`ask`, `..`) inside a
77
+ context statement's value or hole, or in a call hint's hole, is NOLA2010
78
+ (written bare right after the text, NOLA1020); a call intent is a legal
79
+ value (a HINTED `` fn`hint`(…) `` one only in parentheses or a `${}` hole —
80
+ bare it is no call intent, and its extractor argument is NOLA2010). There
81
+ is no `${.member}` prompt scope (a dot cannot start a hole's expression).
82
+ See `references/syntax.md` → "Context statements" and "Values in an
83
+ instruction".
84
+ - The prompt itself is the developer's words in four tags — `<context
85
+ function|module>` with `<input name>` blocks, then `<task>` (`<task
86
+ call="fn">` for a call intent); the schema rides structured output, never
87
+ the prompt; `system.message` REPLACES the default system text. See
88
+ `references/syntax.md` → "The prompt".
57
89
 
58
90
  ## Where Nola diverges from TypeScript — hard rules
59
91
 
@@ -72,7 +104,7 @@ infer function extractPerson(.text: string) {
72
104
  with an exported type's name (NOLA2011). `./x.tsi` with no `x.tsi` on disk
73
105
  is the VIEW of `x.ts`: the same module plus those values — import plain-TS
74
106
  types that way when you need their schema. Keep one basename per module.
75
- - An extractor's `<T>` should be a named, JSON-shaped type. The schema comes
107
+ - An extractor's type should be a named, JSON-shaped type. The schema comes
76
108
  from the RESOLVED type, so unions (a `kind` key makes them discriminated),
77
109
  `Partial<T>`, `extends`, `Record<string, T>` and types from packages all
78
110
  work; `Date` is revived to a real `Date`. `Map`/`Set`/`Promise`, functions
@@ -124,8 +124,8 @@ export default defineConfig({
124
124
 
125
125
  ```tsi
126
126
  export infer function summarize(.text: string) {
127
- const draft = ask with fast `a rough summary`<string>;
128
- return ask with careful `a polished summary of: ${draft}`<string>;
127
+ const draft = ask with fast `a rough summary`: string;
128
+ return ask with careful `a polished summary of: ${draft}`: string;
129
129
  }
130
130
  ```
131
131
 
@@ -232,11 +232,11 @@ the providers package:
232
232
  "check": "nola check"
233
233
  },
234
234
  "dependencies": {
235
- "@nola-lang/providers": "^0.1.13",
236
- "@nola-lang/runtime": "^0.1.13"
235
+ "@nola-lang/providers": "^0.1.14",
236
+ "@nola-lang/runtime": "^0.1.14"
237
237
  },
238
238
  "devDependencies": {
239
- "nola-lang": "^0.1.13",
239
+ "nola-lang": "^0.1.14",
240
240
  "typescript": "^5.6.0"
241
241
  },
242
242
  "engines": { "node": ">=22.18" }