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
@@ -16,44 +16,96 @@ plain TS.
16
16
  ```tsi
17
17
  // plain
18
18
  infer function summarize(.text: string) {
19
- return ask `a one-sentence summary`<string>;
19
+ return ask `a one-sentence summary`: string;
20
20
  }
21
21
 
22
22
  // exported
23
23
  export infer function classify(.message: string) {
24
- return ask `the category of the message`<string>;
24
+ return ask `the category of the message`: string;
25
25
  }
26
26
 
27
- // with an instruction marker between the name and the parameter list
28
- export infer function triage`triage the ticket like a support lead`(.ticket: string) {
29
- return ask `the severity: low, medium or high`<"low" | "medium" | "high">;
27
+ // a context statement: a bare template literal on its own line
28
+ export infer function triage(.ticket: string) {
29
+ `triage the ticket like a support lead`
30
+ return ask `the severity: low, medium or high`: "low" | "medium" | "high";
30
31
  }
31
32
 
32
- // the same instruction as the body's FIRST statement (a bare template literal)
33
- export infer function triage2(.ticket: string) {
34
- `${.default}
35
- Triage like a support lead. Escalate anything mentioning a refund.`
36
- return ask `the severity: low, medium or high`<"low" | "medium" | "high">;
33
+ // text and values — the value is spliced into your words, like a ${} hole
34
+ export infer function escalate(.ticket: string, oncall: string) {
35
+ `Page` oncall `when the ticket is an outage.`
36
+ const steps: string[] = [];
37
+ `Steps taken so far:` steps; // read at EACH ask: the loop sees the current list
38
+ while (steps.length < 3) steps.push(ask `the next step`: string);
39
+ return steps;
37
40
  }
38
41
  ```
39
42
 
40
43
  Rules:
41
44
 
42
- - The body instruction is the marker's second spelling — same meaning (prose =
43
- instruction, `${.member}` = the function's prompt template), lexical holes
44
- see the parameters. Marker + body instruction together is NOLA2013. A
45
- template literal that is NOT the first statement is ordinary code.
46
- - The MODULE body takes the same first-statement literal (the `<module>`
47
- scope's instruction / template) and `const .x` bindings — see "The `ask`
48
- operator".
45
+ - Context statements: any bare template literal statement in a scope body
46
+ (an infer body or the module body, at any depth of braces). Several are
47
+ legal. An ask sees the statements written BEFORE it, in its block or an
48
+ enclosing one — the `const .x` rule; each is read at each ask. Visibility
49
+ is lexical, like a `const`: a statement inside a block reaches only the
50
+ asks inside those braces (an ask after the block never sees it), and
51
+ statements never accumulate across loop passes — each ask sees every
52
+ statement visible at its position exactly once, rendered at that ask; only
53
+ the values change between passes. A value is a
54
+ name, a call or a bracketed literal (`[a, b]`, `{ a }`, `(a + b)`; a plain
55
+ number, string or `true` works too); a `[` or `(` group takes its whole
56
+ tail (`[a, b].filter(ok)` is one value). Parenthesize anything else: after
57
+ a value an operator or arrow is NOLA1020, and so is a value that ENDS with
58
+ a backtick (`` foo<string> `more` ``, `` new Foo `more` `` — write
59
+ `(foo<string>)`, `(new Foo)` or `new Foo()`). `await`, `ask` and `this`
60
+ cannot be values at all — assign the result to a local first
61
+ (`const user = this.user;`, then `` `Page` user ``): an item is a hoisted
62
+ function of its own, so a bare `this` is NOLA1020 and `this` in
63
+ parentheses or in a `${}` hole is a TypeScript error (TS2683); see "Values
64
+ in an instruction" for `await` and `ask`. A call intent is a value: a
65
+ sigil-less one (`` `Page` fn(`x`: T) ``) as it stands, a HINTED one
66
+ (`` fn`hint`(…) ``, an empty marker too) only in parentheses or a `${}`
67
+ hole — bare, its backtick reads as the next text part, so it is no call
68
+ intent (a typed template argument is then a syntax error at its colon, a
69
+ `` ..`x`: T `` one NOLA2010; plain arguments raise no error and render
70
+ the wrong text). An operator directly after the TEXT
71
+ makes no context statement at all: `` `Page ` + oncall `` is ordinary
72
+ string concatenation the model never sees — write `` `Page` oncall ``.
73
+ Values never sit side by side; whitespace between parts is kept as
74
+ written. LINES follow
75
+ JavaScript: a line that starts with a backtick, `[` or `(` continues the
76
+ statement; a line that starts with a NAME begins a new statement
77
+ (`` `analyze` `` ⏎ `foo(x)` is a statement and a call — put `foo(x)` at
78
+ the end of the text's line or in parentheses on its own line). Two
79
+ backtick-led lines in a row are ONE statement (the second line's
80
+ indentation stays in the text): end the first with `;` for two. The mirror
81
+ hazard: a CODE line starting with `[` or `(` right after a context
82
+ statement is taken as its value (its result lands in the prompt, and the
83
+ code runs at every ask) — end the statement with `;`. A backtick literal
84
+ between the name and the parameters (`` infer function f`…`(…) ``) is
85
+ reserved — NOLA1019.
86
+ - The MODULE body takes the same statements and `const .x` bindings — see
87
+ "The `ask` operator". A top-level module statement reaches the asks below
88
+ it and the infer functions DECLARED below it: an ask inside one renders the
89
+ module's `<context module="<file>">` block before the function's, whoever
90
+ calls it, once. A function's OWN VIEW of the module is the top-level
91
+ statements written above its declaration. A function declared ABOVE the
92
+ statement does not see it on its own (a detached `await fn()` sees only the
93
+ function's own view), but `ask fn()` from code that saw it — module code
94
+ below the statement, an infer function declared below it — renders the
95
+ block once with the caller's statements added. A statement inside a
96
+ module-level block reaches a function only through an `ask fn()` written
97
+ inside that block. Bindings are not carried.
98
+ - A context statement in a plain function, a callback, a static block or a
99
+ namespace body — or as the UNBRACED body of an `if`/`else`/loop/label — is
100
+ NOLA2017 (put braces around the body). In a file with no `ask` and no
101
+ infer function a lone text statement is left as written (plain JavaScript,
102
+ a no-op), but a statement of two or more parts makes the file use the
103
+ runtime and every context statement in it an item.
49
104
 
50
105
  - Top-level function declarations only. `infer` on a method, arrow function,
51
- or function expression is a "reserved for a future Nola version" error.
106
+ or function expression is a plain parse error (NOLA1001).
52
107
  - `async infer function` is a parse error — an infer function is never `async`
53
108
  in source; it is implicitly awaitable through `Intent`.
54
- - The instruction marker is a template literal. `${expr}` holes interpolate
55
- lexical values into the instruction; a `${.member}` hole makes the marker a
56
- prompt TEMPLATE for the function's CONTEXT block (see "Prompt templates").
57
109
  - `export default infer function` does NOT parse. Export by name
58
110
  (`export infer function f(...)`) and let consumers import the name.
59
111
  - `ask` is legal only DIRECTLY inside an infer function body or DIRECTLY in
@@ -68,7 +120,7 @@ Rules:
68
120
  ```tsi
69
121
  export infer function enrich(.handle: string, fetchProfile: (h: string) => Promise<string>) {
70
122
  const profile = await fetchProfile(handle); // ordinary promise
71
- return ask `the person's job title from: ${profile}`<string>;
123
+ return ask `the person's job title from: ${profile}`: string;
72
124
  }
73
125
  ```
74
126
 
@@ -86,7 +138,7 @@ interface User {
86
138
  }
87
139
 
88
140
  export infer function getUser(.message: string): Intent<User> {
89
- const user = ask `the user described in the message`<User>;
141
+ const user = ask `the user described in the message`: User;
90
142
  return user;
91
143
  }
92
144
  ```
@@ -101,15 +153,16 @@ A parameter prefixed with ONE dot is a CONTEXT parameter: its name, type and
101
153
  runtime VALUE are composed into the prompt of every `ask` in that invocation.
102
154
  A plain parameter is an ordinary JS argument — its name and type reach the
103
155
  LLM, its value does not. Mnemonic: one dot IN (`.name`); a template after
104
- `ask` OUT — `` ..`prompt` `` spells the same request where `ask` is not
105
- directly in front of it. Writing `..name` on a parameter is NOLA1013.
156
+ `ask` OUT — `` ..`prompt` `` spells the same request where neither `ask`
157
+ nor a call's argument list is in front of it. Writing `..name` on a
158
+ parameter is NOLA1013.
106
159
 
107
160
  ```tsi
108
161
  export type Issue = { id: string; description: string };
109
162
 
110
163
  // `issue` is visible to the LLM; `fallback` is a normal JS value only.
111
164
  export infer function classifyIssue(.issue: Issue, fallback: string) {
112
- const kind = ask `the kind of this issue`<string>;
165
+ const kind = ask `the kind of this issue`: string;
113
166
  return kind || fallback;
114
167
  }
115
168
  ```
@@ -127,7 +180,7 @@ export infer function classifyIssue(.issue: Issue, fallback: string) {
127
180
 
128
181
  ```tsi
129
182
  export infer function nextQuery(.question: string, .notes: string[]) {
130
- return ask `the single best search query to advance the research`<string>;
183
+ return ask `the single best search query to advance the research`: string;
131
184
  }
132
185
  ```
133
186
  - `const .x = …` / `let .x = …` is a contextual BINDING: context for every
@@ -137,27 +190,29 @@ export infer function nextQuery(.question: string, .notes: string[]) {
137
190
  parameter (same `underivableContextType` policy), and travels to a callee
138
191
  through `ask fn()`. At module level it gives the `<module>` scope its
139
192
  CONTEXT block; it does NOT reach infer functions merely declared in the
140
- file. `var .x` is NOLA1014; a pattern is NOLA1011.
193
+ file (a module context statement does, in the functions declared below
194
+ it — see "infer function"). `var .x` is NOLA1014; a pattern is NOLA1011.
141
195
 
142
196
  ```tsi
143
197
  export infer function reply(.mail: string) {
144
198
  const .tone = "brief, friendly";
145
199
  const .customer: Customer = await loadCustomer(mail);
146
- return ask `a reply to the mail`<string>;
200
+ return ask `a reply to the mail`: string;
147
201
  }
148
202
  ```
149
203
 
150
- ## Extractors — `` ask `instruction`<T> `` and `` ..`instruction`<T> ``
204
+ ## Extractors — `` ask `instruction`: T `` and `` ..`instruction`: T ``
151
205
 
152
206
  An extractor is the request itself: instruction text in backticks plus an
153
- optional type argument. Directly after `ask` (and after `ask with <name>`)
207
+ optional type argument. Directly after `ask` (and after `ask with <name>`),
208
+ and as a typed slot in a call's arguments (`` createTicket(`title`: string, 2) ``),
154
209
  the backticks alone are the extractor; everywhere else it is written with two
155
210
  leading dots so it cannot be mistaken for a string.
156
211
 
157
212
  ```tsi
158
213
  export infer function parse(.doc: string) {
159
- const id = ask `the ticket id`<string>; // typed
160
- const count = ask `how many line items`<number>;
214
+ const id = ask `the ticket id`: string; // typed
215
+ const count = ask `how many line items`: number;
161
216
  const free = ask `think step by step about the document`; // untyped
162
217
  return { id, count, free };
163
218
  }
@@ -165,9 +220,7 @@ export infer function parse(.doc: string) {
165
220
 
166
221
  - `${expr}` interpolation is legal inside the backticks and is evaluated at
167
222
  intent-construction time. Strings splice as-is; anything else is
168
- JSON-stringified. A hole starting with a single dot (`${.type}`) is NOT a
169
- lexical value — it reads the extractor's prompt scope and turns the
170
- backticks into a prompt template (see "Prompt templates"):
223
+ JSON-stringified. A hole cannot contain a Nola construct (NOLA2010):
171
224
 
172
225
  ```tsi
173
226
  interface Person {
@@ -176,20 +229,45 @@ interface Person {
176
229
  }
177
230
 
178
231
  export infer function lookup(text: string) {
179
- return ask `the person described in: ${text}`<Person>;
232
+ return ask `the person described in: ${text}`: Person;
180
233
  }
181
234
  ```
182
235
 
183
- - With no `<T>`, the extractor asks for free text: the wire schema is a plain
236
+ - With no type, the extractor asks for free text: the wire schema is a plain
184
237
  string and the static TS type is `any`. Give every extractor an explicit
185
- `<T>` unless you deliberately want unconstrained prose.
186
- - The `..` is implied only directly after `ask`. Everywhere else it is
187
- required: `` const i = ..`x`<T> ``, `` fn(..`x`<T>) ``, `` { a: ..`x`<T> } ``,
188
- `` ask (..`x`<T>).withRetry(2) `` (parenthesized: the template is no longer
189
- the operand's first token). A `` `x`<T> `` outside `ask` is NOLA2014.
190
- `` ask ..`x`<T> `` is still legal and lowers identically. The SPACE is
191
- mandatory: `` ask`x` `` (backtick glued to `ask`, or to the name after
192
- `ask with`) is NOLA1017 — it reads as a tagged template.
238
+ type unless you deliberately want unconstrained prose.
239
+ - TWO SPELLINGS OF THE TYPE, one construct. `: T` — a colon GLUED to the
240
+ closing backtick, like an annotation — is the taught form and what every
241
+ example writes. `<T>` — a type argument after the backtick — is the same
242
+ construct: `` ask `the ticket id`<string> ``, `` ..`x`<T> ``,
243
+ `` fn(`x`<T>) ``, `` ask `q`<Choice<C>> `` — same AST, same emit, same
244
+ definition, same ledger entries; legal everywhere `: T` is. Rules for the
245
+ colon: a colon after whitespace is never the extractor's (so
246
+ `` cond ? ask `p` : fallback `` stays a ternary over an untyped ask, and
247
+ `` cond ? ask `p`: number : fallback `` asks for a number); the type starts
248
+ on the colon's line (NOLA1018 otherwise); a type NAME followed by `.`
249
+ continues the type (`` `p`: User.withRetry(2) `` is the type
250
+ `User.withRetry` called with 2) — chain intent methods after a
251
+ parenthesized extractor (`` ask (..`p`: User).withRetry(2) ``), a
252
+ delimited type (`string[]`, `Array<User>`, `{…}`, `(User)`) or `<T>`;
253
+ `<` after a type name opens type arguments with either spelling.
254
+ - The `..` is implied in two places: directly after `ask` (the template is
255
+ the operand's first token) and in a call's SLOTS — a typed template that
256
+ starts an argument, or a property value / element of a plain object or
257
+ array literal written in the arguments: `` fn(`x`: T) ``,
258
+ `` api.save({ note: `x`: T }) ``, `` fn([`x`: T]) ``. Everywhere else it is
259
+ required: `` const i = ..`x`: T ``, `` fn(cond ? ..`x`: T : y) ``,
260
+ `` fn(...[..`x`: T]) ``, `` new Foo(..`x`: T) ``,
261
+ `` ask (..`x`: T).withRetry(2) `` (parenthesized: the template is no longer
262
+ a first token — `` fn((`x`: T)) `` is a syntax error too). A `` `x`<T> ``
263
+ outside those places is NOLA2014; a `` `x`: T `` there is a plain syntax
264
+ error. An UNTYPED template in a slot is a plain string argument
265
+ (`` log(`done`) `` is an ordinary call), and in a slot `<` starts type
266
+ arguments only when `<…>` reads as such (`` fn(`a` < b) `` stays a
267
+ comparison). `` ask ..`x`: T `` and `` fn(..`x`: T) `` are still legal and
268
+ lower identically. The SPACE is mandatory after `ask`: `` ask`x` ``
269
+ (backtick glued to `ask`, or to the name after `ask with`) is NOLA1017 —
270
+ it reads as a tagged template.
193
271
  - DECISION TYPES (intrinsic, no import): `Choice<{ billing: "Payments";
194
272
  sales: null }>` (or `Choice<"a" | "b">`, 2–255 labels; number labels too,
195
273
  alone or mixed — `Choice<1 | 2 | 3>` / `Choice<{ 1: "Low"; 2: "High" }>` /
@@ -205,12 +283,12 @@ export infer function lookup(text: string) {
205
283
  at 0.5. RULE: adding criteria changes what the property evaluates to. A
206
284
  type containing a decision type needs a DECISION model (`typesafe()`,
207
285
  `mockProvider(replies, { decisions: true })` in tests) — on a chat model it
208
- is NOLA3018 before the network. Sugar, explicit sigil only: `` ask
209
- ..choice`Which team?`<{ billing: "Payments"; sales: null }> ``, `` ask
210
- ..scale`How bad?`<["low", "high"]> ``, `` ask ..prob`Urgent?` `` — each
211
- lowers to `` ..`q`<Choice<…>> `` etc. and shares its identity; another
212
- word after `..` is NOLA1016; malformed criteria are NOLA2015.
213
- - `<T>` accepts whatever resolves to a JSON shape: scalars, `Date`, arrays,
286
+ is NOLA3018 before the network. There is NO `..choice` / `..scale` /
287
+ `..prob` sugar (retired 2026-09-23; an identifier after `..` is NOLA1005):
288
+ write the type long-hand — `` ask `Which team?`: Choice<{ billing:
289
+ "Payments"; sales: null }> ``, `` ask `How bad?`: Scale<["low", "high"]> ``,
290
+ `` ask `Urgent?`: Prob ``; malformed criteria are NOLA2015.
291
+ - The type accepts whatever resolves to a JSON shape: scalars, `Date`, arrays,
214
292
  tuples, object literals, aliases/interfaces (same file, another file, a
215
293
  package; `extends`, intersections, `Partial`/`Pick`/`Omit`, instantiated
216
294
  generics), string-literal unions and string enums, object and nullable
@@ -230,7 +308,7 @@ export interface Conclusion {
230
308
  (infer body or module body):
231
309
 
232
310
  ```tsi
233
- export const nameIntent = ..`the user's full name`<string>; // legal, inert
311
+ export const nameIntent = ..`the user's full name`: string; // legal, inert
234
312
  ```
235
313
 
236
314
  ## The `ask` operator
@@ -245,7 +323,7 @@ import { getUserById } from "./users.tsi";
245
323
  type User = { name: string };
246
324
 
247
325
  export infer function report(.text: string) {
248
- const user = ask `the user named in the text`<User>; // extractor
326
+ const user = ask `the user named in the text`: User; // extractor
249
327
  const record = ask getUserById(user.name); // another infer function
250
328
  return record;
251
329
  }
@@ -255,8 +333,8 @@ export infer function report(.text: string) {
255
333
 
256
334
  ```tsi
257
335
  export infer function summarize(.text: string) {
258
- const draft = ask with fast `a rough summary`<string>;
259
- const final = ask with careful `a polished summary of: ${draft}`<string>;
336
+ const draft = ask with fast `a rough summary`: string;
337
+ const final = ask with careful `a polished summary of: ${draft}`: string;
260
338
  return final;
261
339
  }
262
340
  ```
@@ -282,8 +360,9 @@ Three spellings:
282
360
  declare function createTicket(title: string, priority: number): Promise<string>;
283
361
 
284
362
  export infer function file(.request: string) {
285
- // 1. sigil-less — a plain call whose arguments contain an extractor
286
- const a = ask createTicket(..`a short ticket title`<string>, 2);
363
+ // 1. sigil-less — a plain call whose arguments contain an extractor; the
364
+ // slot's `..` is implied (`..`a short ticket title`: string` still works)
365
+ const a = ask createTicket(`a short ticket title`: string, 2);
287
366
 
288
367
  // 2. empty marker — identical lowering; the only spelling for a call
289
368
  // intent whose arguments are all plain
@@ -291,8 +370,8 @@ export infer function file(.request: string) {
291
370
 
292
371
  // 3. hint marker — the ONLY carrier of instruction text for the call
293
372
  const c = ask createTicket`file the ticket the customer asked for`(
294
- ..`a short ticket title`<string>,
295
- ..`priority 1-5, 1 is most urgent`<number>,
373
+ `a short ticket title`: string,
374
+ `priority 1-5, 1 is most urgent`: number,
296
375
  );
297
376
 
298
377
  return { a, b, c };
@@ -304,33 +383,36 @@ Detection rule for the sigil-less form — BOTH must hold:
304
383
  - the callee is an `Identifier` or a `MemberExpression` (any nesting, computed
305
384
  included), and
306
385
  - at least one well-formed extractor appears in a slot position: a direct
307
- argument, or nested at any depth inside plain object/array literals.
386
+ argument, or nested at any depth inside plain object/array literals. In
387
+ exactly those positions a typed template is an extractor without the `..`;
388
+ an untyped one is a plain string argument.
308
389
 
309
390
  ```tsi
310
391
  declare const api: { save(order: { qty: number; note: string }): Promise<string> };
311
392
 
312
393
  export infer function place(.request: string) {
313
394
  // member callee + extractor nested in an object literal → call intent
314
- return ask api.save({ qty: 1, note: ..`a one-line note for the warehouse`<string> });
395
+ return ask api.save({ qty: 1, note: `a one-line note for the warehouse`: string });
315
396
  }
316
397
  ```
317
398
 
318
- These stay PLAIN calls (the extractor is just a value argument): an extractor
319
- inside a ternary, logical expression, spread element or template substitution;
320
- a nested call (in `` outer(inner(..`x`<T>)) `` the INNER call is the intent and
399
+ These stay PLAIN calls (the extractor is just a value argument, and needs its
400
+ `..` there): an extractor inside a ternary, logical expression, spread element
401
+ or template substitution; a nested call (in `` outer(inner(`x`: T)) `` the
402
+ INNER call is the intent and
321
403
  `outer` receives an `Askable`); `new Foo(...)`, `super(...)`, `import(...)`,
322
404
  optional calls (`fn?.(...)`, `a?.b(...)`); and exotic callees (`getFn()(...)`,
323
- IIFEs) — use the marker form if you want a call intent on one of those.
405
+ IIFEs) — use the hint form `` fn`hint`(…) `` if you want a call intent on one of those.
324
406
 
325
407
  Parenthesizing an extractor does NOT opt out. To pass an intent as a plain
326
408
  value, bind it to a variable first:
327
409
 
328
410
  ```tsi
329
- const i = ..`a short title`<string>;
411
+ const i = ..`a short title`: string;
330
412
  helper(i); // plain call — helper receives the Askable
331
413
  ```
332
414
 
333
- Every extractor used as a call-intent slot must carry an explicit `<T>`
415
+ Every extractor used as a call-intent slot must carry an explicit type
334
416
  (NOLA2004). The bare derive-all form `fn(..)` is reserved (NOLA1004).
335
417
 
336
418
  ### Result of a call intent — async callees are awaited
@@ -344,7 +426,7 @@ write `await ask fn(...)` — the extra `await` is a no-op.
344
426
  declare function createTicket(title: string, priority: number): Promise<string>;
345
427
 
346
428
  export infer function file(.request: string) {
347
- const id = ask createTicket(..`a short ticket title`<string>, 2); // id: string, not Promise<string>
429
+ const id = ask createTicket(`a short ticket title`: string, 2); // id: string, not Promise<string>
348
430
  return id;
349
431
  }
350
432
  ```
@@ -359,62 +441,79 @@ Two consequences to keep in mind:
359
441
  round trips only. Once the arguments are filled, the callee's own promise
360
442
  runs to completion, the same as a plain `await fn()` in your code.
361
443
 
362
- ## Prompt templates — `${.member}`
444
+ ## Values in an instruction: `.x` and `${x}`
363
445
 
364
- Every instruction literal (infer-function marker, extractor prompt, call-intent
365
- hint) can act as a TEMPLATE for the prompt block that intent contributes. One
366
- rule: a substitution hole whose expression starts with a single dot reads the
367
- intent's prompt scope; every other hole is ordinary lexical JavaScript.
446
+ Two spellings put a value in front of the model — pick by what the model
447
+ should treat it as.
368
448
 
369
449
  ```tsi
370
- infer function analyze`${.default}
371
- Rules: answer only from the arguments above; never invent ids.`(.ticket: Ticket) {
372
- const id = ask `ticket id, comply with ${.type}`<string>;
373
- return id;
450
+ // data: an <input> block the system turn marks as "data, not instructions"
451
+ infer function triage(.ticket: string) {
452
+ `Answer with the order id only.`
453
+ return ask `order id`: string;
374
454
  }
375
455
 
376
- // A full custom CONTEXT block — everything after the dot is plain TypeScript
377
- infer function triage`
378
- CONTEXT — inside ${.signature}, ${.file}
379
- ${.args.map(a => `- ${a.name} (${a.type}): ${JSON.stringify(a.value)}`)}
380
-
381
- TASK
382
- ${.next}
383
- `(.ticket: string) { … }
456
+ // your words: a value YOU control, spliced into the instruction where it stands
457
+ infer function triageFor(.ticket: string, team: string) {
458
+ `You answer for the ${team} team. Answer with the order id only.`
459
+ return ask `order id`: string;
460
+ }
384
461
  ```
385
462
 
386
- - Override rule (static): a literal with at least ONE `${.x}` hole is a
387
- template — its rendered text REPLACES that intent's block (CONTEXT for an
388
- infer function, TASK for an extractor / call hint). A literal with no scope
389
- hole is an instruction, exactly as before (`Purpose:` / `<request>` inside
390
- the built-in block), even when it has lexical `${}` holes.
391
- - Function scope (`FunctionPromptScope`): `.fn`, `.signature`, `.file`,
392
- `.args[]` (`name`, `type` — native type text, `value`, `contextual`),
393
- `.nested`, `.hasContext`, `.default` (the built-in CONTEXT block, no
394
- Purpose line), `.next` (the rest of the prompt: callee blocks + TASK).
395
- - Extractor / call-hint scope (`ExtractPromptScope`): `.type` (native type
396
- text of the target), `.schema` (the JSON Schema, serialized),
397
- `.hasContext`, `.default` (the built-in TASK block), `.format` (the JSON
398
- response rules).
399
- - Safe by default: a function template that never reads `.next` gets the
400
- rest of the prompt appended after it; an extractor template that never
401
- reads `.format` gets the JSON response rules appended. Read them only to
402
- choose WHERE they go (wrapping). `.next`/`.default`/`.format` are
403
- getters, memoized — reading twice does not compose twice.
404
- - Rendering: arrays render one item per line (no `.join` needed),
405
- `undefined`/`null` render as nothing, objects as JSON, `Date` as ISO.
406
- Templates render when the ask composes its prompt — lexical values inside
407
- a TEMPLATE are read then, not at construction (the only observable
408
- difference from an instruction's eager `${}`).
409
- - Nested holes follow the same rule: `${.file}` inside a `.map` callback's
410
- own template literal still reads the scope. Keyword members work
411
- (`${.default}`).
412
- - Editor: completion, hover and precise TS errors work inside the backticks
413
- (an unknown member is a TS2339 at the member).
414
- - Errors: `${.x}` in a template literal that is not a Nola instruction is
415
- NOLA2009; a Nola construct (`..`, call intent, `ask`) inside a marker /
416
- call-hint hole is NOLA2010; a template that throws or renders empty fails
417
- the ask with NOLA3014 (definitive).
463
+ - `.name` renders as `<input name="name">` after the instruction; a plain
464
+ parameter is never rendered. Both spellings on one value put it in the
465
+ prompt twice.
466
+ - Trust: interpolated text is INSTRUCTIONS the model follows. Interpolate
467
+ only what the developer controls (a team name, a count, a date); a
468
+ customer's message, a document, anything a user typed goes in `.name`,
469
+ where the system turn marks it as data.
470
+ - `${expr}` is ordinary TypeScript in EVERY instruction literal — a
471
+ context statement, an extractor prompt, a call hint. A string splices
472
+ verbatim; anything else is JSON (a `Date` as a quoted ISO string — write
473
+ `${d.toISOString()}` for bare text; `undefined` as `undefined`).
474
+ - When it is read: a context statement at EACH ask that sees it (a value
475
+ must be initialized by then — a `let` or `const` declared after the ask
476
+ is a ReferenceError at that ask); an extractor's prompt and a call hint when
477
+ the ask expression runs. A value after text in a context statement is the
478
+ same as a `${}` hole: `` `Page` oncall `…` `` ≡ `` `Page ${oncall} …` ``.
479
+ - A custom format is a function call in a hole (`${table(a, b)}`); a value
480
+ kept out of the prompt is a dotless parameter.
481
+ - Errors: a Nola construct (`..`, `ask`) in a context statement (a
482
+ parenthesized value, a hole) or in a call hint's hole is NOLA2010 — bare
483
+ right after a statement's text it is NOLA1020. A call intent is a legal
484
+ value in a context statement — it renders as its callee and arguments; a
485
+ HINTED one (`` fn`hint`(…) ``) must be parenthesized or written in a `${}`
486
+ hole (bare, the hint's backtick reads as the next text part: an extractor
487
+ argument is then NOLA2010). A bare `await` or `this` right after a statement's
488
+ text is NOLA1020; anywhere else in a value or hole it is a TypeScript
489
+ error (an item is read at each ask, outside the async body, in a function
490
+ of its own — TS1308 for `await`, TS2683 for `this`): compute the value
491
+ first, or assign `this` to a local. There is no
492
+ `${.member}` prompt scope: a dot cannot start a hole's expression (an
493
+ ordinary syntax error).
494
+ - Editor: completion, hover and TypeScript errors work inside `${…}`.
495
+
496
+ ## The prompt
497
+
498
+ What reaches the model is the developer's text in four utility tags —
499
+ nothing else of Nola's is in the user turn:
500
+
501
+ - `<context function="f">` / `<context module="path">`: one scope — its
502
+ context statements (each starting on its own line), then one
503
+ `<input name="x">` per contextual param and `const .x` binding (a string
504
+ verbatim, anything else as JSON). Plain params are NOT rendered. Outermost
505
+ caller first, asking scope last.
506
+ - `<task>` last: the extractor's instruction; a call intent is
507
+ `<task call="fn">hint</task>` (self-closing without a hint).
508
+ - `<correction>` on the retry, after the rejected reply.
509
+ - The schema is NEVER in the prompt: it rides the provider's structured
510
+ output (`structuredOutputs: false` on a vendor factory puts it in the
511
+ system turn as `<schema>` for servers that cannot enforce it).
512
+ - The system turn is four constant sentences (`DEFAULT_SYSTEM`, exported
513
+ from `@nola-lang/providers`); `system.message` in the config REPLACES it.
514
+ - Every provider implements `infer(req)` over `req.intent` (the ask as
515
+ data) and renders it itself — `renderPrompt(req.intent)` is the default.
516
+ A model that still implements `complete(req)` is NOLA3003.
418
517
 
419
518
  ## Intent methods
420
519
 
@@ -422,10 +521,10 @@ Every intent (extractor, call intent, infer-function result) accepts:
422
521
 
423
522
  ```tsi
424
523
  export infer function tuned(.text: string) {
425
- const a = ask (..`the title`<string>).withRetry(2);
426
- const b = ask (..`the body`<string>).withModel("careful");
427
- const c = ask (..`a creative tagline`<string>).withParams({ temperature: 0.9, maxOutputTokens: 200 });
428
- const d = ask (..`a one-paragraph summary`<string>).withTimeout(10_000);
524
+ const a = ask (..`the title`: string).withRetry(2);
525
+ const b = ask (..`the body`: string).withModel("careful");
526
+ const c = ask (..`a creative tagline`: string).withParams({ temperature: 0.9, maxOutputTokens: 200 });
527
+ const d = ask (..`a one-paragraph summary`: string).withTimeout(10_000);
429
528
  return { a, b, c, d };
430
529
  }
431
530
  ```
@@ -1,19 +1,19 @@
1
1
  {
2
- "name": "__NAME__",
2
+ "name": "empty",
3
3
  "version": "0.0.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "start": "nola run src/main.ts",
7
+ "start": "nola run src/main.tsi",
8
8
  "build": "nola build",
9
9
  "check": "nola check"
10
10
  },
11
11
  "dependencies": {
12
- "@nola-lang/providers": "__VERSION__",
13
- "@nola-lang/runtime": "__VERSION__"
12
+ "@nola-lang/providers": "*",
13
+ "@nola-lang/runtime": "*"
14
14
  },
15
15
  "devDependencies": {
16
- "nola-lang": "__VERSION__",
16
+ "nola-lang": "*",
17
17
  "typescript": "^5.6.0"
18
18
  },
19
19
  "engines": {
@@ -0,0 +1,8 @@
1
+ // This file is a Nola module: TypeScript plus `ask`. Write your program here.
2
+ // An ask is legal at the top level — nothing to declare or call first:
3
+ //
4
+ // const .text = "The demo went great and the customer signed on the spot.";
5
+ // const mood = ask `the mood of the text`: "happy" | "sad" | "neutral";
6
+ //
7
+ // Language reference: https://nola.sh/docs/language/ask/
8
+ console.log("Hello from Nola");
@@ -1,5 +0,0 @@
1
- __NEXT_STEPS__
2
-
3
- // Write your first infer function in a .tsi file next to this file, then
4
- // import and await it here — see https://github.com/nola-lang/nola#readme
5
- console.log("Hello from Nola");
@@ -1,20 +0,0 @@
1
- # __NAME__
2
-
3
- A [Nola](https://github.com/nola-lang/nola) project. Nola is a TypeScript
4
- superset (`.tsi`) where `ask` turns a prompt into a typed value. This
5
- project is one file, `src/main.tsi`: a first-line instruction, `const .x`
6
- context bindings, and `ask` at the top level — the file is the program.
7
-
8
- ```bash
9
- npm install
10
- npm start # runs src/main.tsi — __START_NOTE__
11
- npm run check # type-checks the .tsi file
12
- npm run build # compiles to plain JS in dist/
13
- npx nola-lang console # traces every ask in your browser (it prints the config line to add)
14
- ```
15
-
16
- __PROVIDER_NOTE__
17
-
18
- When a script outgrows one file, move the asks into an `infer function`
19
- and call it from plain TypeScript — `npm create nola -- --template typescript-interop`
20
- lays down that shape.
@@ -1,14 +0,0 @@
1
- import { replay } from "@nola-lang/providers";
2
- import { defineConfig } from "@nola-lang/runtime";
3
-
4
- export default defineConfig({
5
- // This project runs offline: answers replay from the committed ledger
6
- // (nola.replay.jsonl), so the first `npm start` needs no API key. The
7
- // ledger is keyed by the exact prompt — once you edit src/main.tsi or add
8
- // asks, switch to a real model:
9
- // model: "nola", // Nola serves inference; `npx nola-lang key` writes NOLA_API_KEY (25 free runs)
10
- // or bring your own:
11
- // import { openai } from "@nola-lang/providers";
12
- // model: openai("gpt-5-mini"), // reads OPENAI_API_KEY
13
- model: replay("./nola.replay.jsonl"),
14
- });