create-nola-lang 0.1.13 → 0.1.15

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 (54) hide show
  1. package/README.md +8 -3
  2. package/dist/add.d.ts +3 -0
  3. package/dist/add.js +2 -1
  4. package/dist/checkout.js +1 -0
  5. package/dist/{chunk-LUSVIOAN.js → chunk-UTVGG44K.js} +124 -95
  6. package/dist/examples.d.ts +4 -2
  7. package/dist/examples.js +4 -2
  8. package/dist/flow.d.ts +20 -3
  9. package/dist/flow.js +49 -17
  10. package/dist/github.d.ts +1 -1
  11. package/dist/github.js +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/launch.d.ts +2 -2
  14. package/dist/launch.js +1 -1
  15. package/dist/main.js +1 -1
  16. package/dist/node-version.d.ts +3 -2
  17. package/dist/node-version.js +4 -3
  18. package/dist/package-manager.d.ts +5 -0
  19. package/dist/package-manager.js +7 -0
  20. package/dist/providers.d.ts +1 -1
  21. package/dist/providers.js +1 -1
  22. package/dist/registry.d.ts +21 -12
  23. package/dist/registry.js +27 -5
  24. package/dist/scaffold.d.ts +36 -16
  25. package/dist/scaffold.js +96 -116
  26. package/package.json +1 -1
  27. package/skills/nola/SKILL.md +72 -17
  28. package/skills/nola/references/config.md +35 -6
  29. package/skills/nola/references/patterns.md +129 -31
  30. package/skills/nola/references/pitfalls.md +318 -56
  31. package/skills/nola/references/syntax.md +345 -121
  32. package/templates/empty/package.json +5 -5
  33. package/templates/empty/src/main.tsi +8 -0
  34. package/templates/empty/src/main.ts +0 -5
  35. package/templates/feature-extraction/README.md +0 -20
  36. package/templates/feature-extraction/nola.config.ts +0 -14
  37. package/templates/feature-extraction/nola.replay.jsonl +0 -2
  38. package/templates/feature-extraction/package.json +0 -22
  39. package/templates/feature-extraction/src/main.tsi +0 -22
  40. package/templates/feature-extraction/tsconfig.json +0 -12
  41. package/templates/function-calling/README.md +0 -21
  42. package/templates/function-calling/nola.config.ts +0 -14
  43. package/templates/function-calling/nola.replay.jsonl +0 -1
  44. package/templates/function-calling/package.json +0 -22
  45. package/templates/function-calling/src/main.tsi +0 -12
  46. package/templates/function-calling/src/tickets.ts +0 -17
  47. package/templates/function-calling/tsconfig.json +0 -12
  48. package/templates/typescript-interop/README.md +0 -15
  49. package/templates/typescript-interop/nola.config.ts +0 -14
  50. package/templates/typescript-interop/nola.replay.jsonl +0 -1
  51. package/templates/typescript-interop/package.json +0 -22
  52. package/templates/typescript-interop/src/main.ts +0 -8
  53. package/templates/typescript-interop/src/person.tsi +0 -11
  54. 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,51 @@ 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 after the
240
+ closing backtick on the same line, glued like an annotation or spaced
241
+ (`` `p`: T `` and `` `p` : T `` are the same ask) — is the taught form and
242
+ what every example writes. `<T>` — a type argument after the backtick — is
243
+ the same construct: `` ask `the ticket id`<string> ``, `` ..`x`<T> ``,
244
+ `` fn(`x`<T>) ``, `` ask `q`<Choice<C>> `` — same AST, same emit, same
245
+ definition, same ledger entries; legal everywhere `: T` is. Rules for the
246
+ colon: it may follow glued, spaced, or on the next line (a line break
247
+ alone never ends an ask); an ask directly
248
+ in a ternary's consequent is parenthesized, on one line or three,
249
+ `` cond ? (ask `p`: number) : fallback `` — bare it is NOLA1023, because
250
+ the colon after the ask is its type and `` cond ? ask `p` : fallback ``
251
+ would read `fallback` as that type (a call intent and the `<T>` form need
252
+ no parentheses, and the alternate position is free:
253
+ `` cond ? y : ask `p`: number ``); the
254
+ type starts on the colon's line (NOLA1018 otherwise); a type NAME followed by `.`
255
+ continues the type (`` `p`: User.withRetry(2) `` is the type
256
+ `User.withRetry` called with 2) — chain intent methods after a
257
+ parenthesized extractor (`` ask (..`p`: User).withRetry(2) ``), a
258
+ delimited type (`string[]`, `Array<User>`, `{…}`, `(User)`) or `<T>`;
259
+ `<` after a type name opens type arguments with either spelling.
260
+ - The `..` is implied in two places: directly after `ask` (the template is
261
+ the operand's first token) and in a call's SLOTS — a typed template that
262
+ starts an argument, or a property value / element of a plain object or
263
+ array literal written in the arguments: `` fn(`x`: T) ``,
264
+ `` api.save({ note: `x`: T }) ``, `` fn([`x`: T]) ``. Everywhere else it is
265
+ required: `` const i = ..`x`: T ``, `` fn(cond ? (..`x`: T) : y) ``,
266
+ `` fn(...[..`x`: T]) ``, `` new Foo(..`x`: T) ``,
267
+ `` ask (..`x`: T).withRetry(2) `` (parenthesized: the template is no longer
268
+ a first token — `` fn((`x`: T)) `` is a syntax error too). A `` `x`<T> ``
269
+ outside those places is NOLA2014; a `` `x`: T `` there is a plain syntax
270
+ error. An UNTYPED template in a slot is a plain string argument
271
+ (`` log(`done`) `` is an ordinary call), and in a slot `<` starts type
272
+ arguments only when `<…>` reads as such (`` fn(`a` < b) `` stays a
273
+ comparison). `` ask ..`x`: T `` and `` fn(..`x`: T) `` are still legal and
274
+ lower identically. The SPACE is mandatory after `ask`: `` ask`x` ``
275
+ (backtick glued to `ask`, or to the name after `ask with`) is NOLA1017 —
276
+ it reads as a tagged template.
193
277
  - DECISION TYPES (intrinsic, no import): `Choice<{ billing: "Payments";
194
278
  sales: null }>` (or `Choice<"a" | "b">`, 2–255 labels; number labels too,
195
279
  alone or mixed — `Choice<1 | 2 | 3>` / `Choice<{ 1: "Low"; 2: "High" }>` /
@@ -205,12 +289,12 @@ export infer function lookup(text: string) {
205
289
  at 0.5. RULE: adding criteria changes what the property evaluates to. A
206
290
  type containing a decision type needs a DECISION model (`typesafe()`,
207
291
  `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,
292
+ is NOLA3018 before the network. There is NO `..choice` / `..scale` /
293
+ `..prob` sugar (retired 2026-09-23; an identifier after `..` is NOLA1005):
294
+ write the type long-hand — `` ask `Which team?`: Choice<{ billing:
295
+ "Payments"; sales: null }> ``, `` ask `How bad?`: Scale<["low", "high"]> ``,
296
+ `` ask `Urgent?`: Prob ``; malformed criteria are NOLA2015.
297
+ - The type accepts whatever resolves to a JSON shape: scalars, `Date`, arrays,
214
298
  tuples, object literals, aliases/interfaces (same file, another file, a
215
299
  package; `extends`, intersections, `Partial`/`Pick`/`Omit`, instantiated
216
300
  generics), string-literal unions and string enums, object and nullable
@@ -230,7 +314,7 @@ export interface Conclusion {
230
314
  (infer body or module body):
231
315
 
232
316
  ```tsi
233
- export const nameIntent = ..`the user's full name`<string>; // legal, inert
317
+ export const nameIntent = ..`the user's full name`: string; // legal, inert
234
318
  ```
235
319
 
236
320
  ## The `ask` operator
@@ -245,7 +329,7 @@ import { getUserById } from "./users.tsi";
245
329
  type User = { name: string };
246
330
 
247
331
  export infer function report(.text: string) {
248
- const user = ask `the user named in the text`<User>; // extractor
332
+ const user = ask `the user named in the text`: User; // extractor
249
333
  const record = ask getUserById(user.name); // another infer function
250
334
  return record;
251
335
  }
@@ -255,8 +339,8 @@ export infer function report(.text: string) {
255
339
 
256
340
  ```tsi
257
341
  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>;
342
+ const draft = ask with fast `a rough summary`: string;
343
+ const final = ask with careful `a polished summary of: ${draft}`: string;
260
344
  return final;
261
345
  }
262
346
  ```
@@ -282,8 +366,9 @@ Three spellings:
282
366
  declare function createTicket(title: string, priority: number): Promise<string>;
283
367
 
284
368
  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);
369
+ // 1. sigil-less — a plain call whose arguments contain an extractor; the
370
+ // slot's `..` is implied (`..`a short ticket title`: string` still works)
371
+ const a = ask createTicket(`a short ticket title`: string, 2);
287
372
 
288
373
  // 2. empty marker — identical lowering; the only spelling for a call
289
374
  // intent whose arguments are all plain
@@ -291,8 +376,8 @@ export infer function file(.request: string) {
291
376
 
292
377
  // 3. hint marker — the ONLY carrier of instruction text for the call
293
378
  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>,
379
+ `a short ticket title`: string,
380
+ `priority 1-5, 1 is most urgent`: number,
296
381
  );
297
382
 
298
383
  return { a, b, c };
@@ -304,33 +389,36 @@ Detection rule for the sigil-less form — BOTH must hold:
304
389
  - the callee is an `Identifier` or a `MemberExpression` (any nesting, computed
305
390
  included), and
306
391
  - 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.
392
+ argument, or nested at any depth inside plain object/array literals. In
393
+ exactly those positions a typed template is an extractor without the `..`;
394
+ an untyped one is a plain string argument.
308
395
 
309
396
  ```tsi
310
397
  declare const api: { save(order: { qty: number; note: string }): Promise<string> };
311
398
 
312
399
  export infer function place(.request: string) {
313
400
  // 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> });
401
+ return ask api.save({ qty: 1, note: `a one-line note for the warehouse`: string });
315
402
  }
316
403
  ```
317
404
 
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
405
+ These stay PLAIN calls (the extractor is just a value argument, and needs its
406
+ `..` there): an extractor inside a ternary, logical expression, spread element
407
+ or template substitution; a nested call (in `` outer(inner(`x`: T)) `` the
408
+ INNER call is the intent and
321
409
  `outer` receives an `Askable`); `new Foo(...)`, `super(...)`, `import(...)`,
322
410
  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.
411
+ IIFEs) — use the hint form `` fn`hint`(…) `` if you want a call intent on one of those.
324
412
 
325
413
  Parenthesizing an extractor does NOT opt out. To pass an intent as a plain
326
414
  value, bind it to a variable first:
327
415
 
328
416
  ```tsi
329
- const i = ..`a short title`<string>;
417
+ const i = ..`a short title`: string;
330
418
  helper(i); // plain call — helper receives the Askable
331
419
  ```
332
420
 
333
- Every extractor used as a call-intent slot must carry an explicit `<T>`
421
+ Every extractor used as a call-intent slot must carry an explicit type
334
422
  (NOLA2004). The bare derive-all form `fn(..)` is reserved (NOLA1004).
335
423
 
336
424
  ### Result of a call intent — async callees are awaited
@@ -344,7 +432,7 @@ write `await ask fn(...)` — the extra `await` is a no-op.
344
432
  declare function createTicket(title: string, priority: number): Promise<string>;
345
433
 
346
434
  export infer function file(.request: string) {
347
- const id = ask createTicket(..`a short ticket title`<string>, 2); // id: string, not Promise<string>
435
+ const id = ask createTicket(`a short ticket title`: string, 2); // id: string, not Promise<string>
348
436
  return id;
349
437
  }
350
438
  ```
@@ -359,62 +447,198 @@ Two consequences to keep in mind:
359
447
  round trips only. Once the arguments are filled, the callee's own promise
360
448
  runs to completion, the same as a plain `await fn()` in your code.
361
449
 
362
- ## Prompt templates — `${.member}`
450
+ ## Tool use
363
451
 
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.
452
+ A tool-use ask offers the model functions it may call before it answers —
453
+ a bracketed list of function names between the task and the type:
368
454
 
369
455
  ```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;
456
+ import { readFile, searchFiles } from "./tools.js";
457
+
458
+ export infer function find(.query: string) {
459
+ return ask `the files on disc D that belong to a US visa application` [searchFiles, readFile]: string[];
374
460
  }
461
+ ```
462
+
463
+ ```tsi
464
+ import * as fileTools from "./tools.js";
465
+ import { dbTools } from "./db.js";
466
+
467
+ const report = ask `analyze this report` [fileTools, dbTools]: Report; // the model sees fileTools.search, dbTools.search, …
468
+ const files = ask `the matching files` [...fileTools]: string[]; // flat: search, readFile
469
+ ```
375
470
 
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)}`)}
471
+ Brackets OFFER, parentheses RUN: `` ask getUser(`id`: string) `` is a call
472
+ intent (getUser runs exactly once, the value is what it returned);
473
+ `` ask `the user's name` [getUser]: string `` offers getUser (the model
474
+ calls it zero or more times, the value is the model's answer). Reach for
475
+ the call when you want the function's own result, for the list when the
476
+ answer needs lookups first.
477
+
478
+ - Each provider call is a TURN: the model answers or requests tool calls;
479
+ Nola validates the arguments against the function's parameters, runs
480
+ the calls in the reply's order, renders each result (`fmt`: a string
481
+ verbatim, anything else JSON) and asks again with the transcript. An
482
+ unknown tool name or invalid arguments never reach the function — they
483
+ go back as that call's result. The answer then gets the usual validation
484
+ and single correction turn (not counted against the cap).
485
+ - Entries are VALUES, judged by the type checker: a name or dotted chain
486
+ names one function (`searchFiles`, `fs.readFile`) or a GROUP — a module
487
+ namespace, an object of functions, an array of them — whose functions
488
+ the model sees as `group.member`; `[...group]` offers them flat by their
489
+ own names; any other expression that is one function (`tools[0]`) is
490
+ named by the function. Group tools by what they are for: two groups
491
+ sharing a `search` never collide, a flat list with two does (NOLA2018).
492
+ The contract derives from each signature: one object over the
493
+ parameters, by name in order; optional/defaulted parameters optional; the
494
+ JSDoc summary is the description, `@param` tags describe the properties.
495
+ A member chain keeps its receiver (its object must be a plain chain —
496
+ a name, `this`, dotted / indexed / optional access; `api.client().search`
497
+ is NOLA2018, name the object first); a group's functions are called on
498
+ the group. One declared signature per tool: a union of functions, `any`,
499
+ `unknown`, a class, a group with nothing callable, a group reached
500
+ through an expression (name it or spread it): NOLA2018. An underivable
501
+ parameter (a rest parameter, a Map): NOLA2002 at the entry; a group
502
+ member it cannot use fails the group, naming the member. At run time a
503
+ list whose values do not match their types, an unnamed function or a
504
+ name collision is NOLA3022 before the network.
505
+ - The cap: `.withMaxTurns(n)` on the intent, else config `ask.maxTurns`
506
+ (10); past it NOLA3020, definitive, transcript attached. A throwing tool
507
+ is NOLA3021 with the error as `cause`; return a failure as a value when
508
+ the model should recover from it. `.withRetry(n)` re-runs the WHOLE
509
+ loop, tools included; `.withTimeout` bounds provider calls only.
510
+ - The model must support tool calls: `openai`, `anthropic`, `google`,
511
+ `model: "nola"`; `mockProvider(…, { tools: true })` with `toolCalls([{
512
+ name, args }])` values scripting tool turns; `replay()` of a recorded
513
+ ledger. Anything else is NOLA3019 before the network; `fallback` /
514
+ `roundRobin` skip members without support on a tool-use ask. A vendor
515
+ factory's `toolUse` option, beside `structuredOutputs`, names the
516
+ strategy (`"native"`, the default and the only one today) or marks a
517
+ host without tool calling (`toolUse: false` — unbranded, NOLA3019,
518
+ skipped by a group); a group mixing strategies is NOLA3003.
519
+ - Where the list is legal: directly after the template at the ask site
520
+ (`ask`, `ask with <name>`) and on the stored form (`` ..`p` [fn]: T ``);
521
+ the type follows the list (`]: T` — or `] : T` — or `]<T>`). Never in a call's
522
+ slots. A call-shaped entry (`` [getUser(`id`: string)] ``) is reserved
523
+ (NOLA1022); a literal or an empty list is NOLA1021. In a ternary's
524
+ consequent, parenthesize the typed ask (NOLA1023), as always.
525
+ - Receipts carry `turns` (each call's arguments, result or error,
526
+ duration); telemetry fires `onToolCallStart` / `onToolCallEnd`; the
527
+ terminal trace and `nola console` show the calls under the ask.
528
+
529
+ ## The ask sequence
530
+
531
+ After the first text, texts and values alternate (spec 2026-10-12):
380
532
 
381
- TASK
382
- ${.next}
383
- `(.ticket: string) { … }
533
+ ```tsi
534
+ const owner = ask `the person responsible for` project `given this policy` policy: Person;
535
+ const user = ask `register the applicant` createUser(`user id`: string, `email`: string);
536
+ const files = ask `find` [searchFiles, readFile] `or, when stale,` [readDir] `and return paths`: string[];
537
+ ```
538
+
539
+ - A bare value (a name, a member chain, a literal, `new X()`, a plain call,
540
+ a parenthesized expression) is DATA: the model reads an `<input name>`
541
+ block where you wrote it, named by what you wrote, with its type on the
542
+ wire; `${x}` inside a text stays your words. Prefer a hole for a literal.
543
+ - A call with a typed slot is the ACTION: the kind is function calling, the
544
+ call's result is the value, no `: T`. A `[list]` offers tools; several
545
+ lists union (the same entry in two lists is one tool), and the words
546
+ between them are guidance, nothing enforces "or".
547
+ - The kind follows the parts: texts and data → extract; one call → function
548
+ calling; one or more lists → tool use; a list beside a call is reserved
549
+ (NOLA2019); two calls, `: T` beside a call, text after a bare call
550
+ (`` ask f(`x`: T) `more` `` — put the words first), or a call with typed
551
+ slots inside a data value: NOLA2020.
552
+ - Two values never touch (`user [x]` is an index, `user (x)` a call); text
553
+ first; `: T` follows the last part (a backtick, `]` or the value) — glued,
554
+ spaced, or on its own line; `<T>` only after a text or a list. `this`,
555
+ `await`, `ask`, an unparenthesized function and a value ending in a
556
+ template are NOLA1020.
557
+ - An ask may span lines, one part per line: a line break alone never ends
558
+ it. After a text the next part may start on the next line, and the type
559
+ may stand on its own line; the ask ends at `;`, a closer, an operator, a
560
+ statement keyword (`const`, `return`, `type X`, `let x`, …) or its type.
561
+ After a VALUE a name on the next line is a new statement, as in JS. The
562
+ one trap: semicolon-free code after a call sequence that ends on a text —
563
+ `` …`done`\nnotify(files) `` takes `notify(files)` as a value; end the ask
564
+ with `;`.
565
+ A call's slot stays one text (a `..` sequence there is NOLA2019). A
566
+ sequence lowers to an instruction ARRAY of parts; a one-text ask lowers
567
+ as before.
568
+
569
+ ## Values in an instruction: `.x` and `${x}`
570
+
571
+ Two spellings put a value in front of the model — pick by what the model
572
+ should treat it as.
573
+
574
+ ```tsi
575
+ // data: an <input> block the system turn marks as "data, not instructions"
576
+ infer function triage(.ticket: string) {
577
+ `Answer with the order id only.`
578
+ return ask `order id`: string;
579
+ }
580
+
581
+ // your words: a value YOU control, spliced into the instruction where it stands
582
+ infer function triageFor(.ticket: string, team: string) {
583
+ `You answer for the ${team} team. Answer with the order id only.`
584
+ return ask `order id`: string;
585
+ }
384
586
  ```
385
587
 
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).
588
+ - `.name` renders as `<input name="name">` after the instruction; a plain
589
+ parameter is never rendered. Both spellings on one value put it in the
590
+ prompt twice.
591
+ - Trust: interpolated text is INSTRUCTIONS the model follows. Interpolate
592
+ only what the developer controls (a team name, a count, a date); a
593
+ customer's message, a document, anything a user typed goes in `.name`,
594
+ where the system turn marks it as data.
595
+ - `${expr}` is ordinary TypeScript in EVERY instruction literal — a
596
+ context statement, an extractor prompt, a call hint. A string splices
597
+ verbatim; anything else is JSON (a `Date` as a quoted ISO string — write
598
+ `${d.toISOString()}` for bare text; `undefined` as `undefined`).
599
+ - When it is read: a context statement at EACH ask that sees it (a value
600
+ must be initialized by then — a `let` or `const` declared after the ask
601
+ is a ReferenceError at that ask); an extractor's prompt and a call hint when
602
+ the ask expression runs. A value after text in a context statement is the
603
+ same as a `${}` hole: `` `Page` oncall `…` `` ≡ `` `Page ${oncall} …` ``.
604
+ - A custom format is a function call in a hole (`${table(a, b)}`); a value
605
+ kept out of the prompt is a dotless parameter.
606
+ - Errors: a Nola construct (`..`, `ask`) in a context statement (a
607
+ parenthesized value, a hole) or in a call hint's hole is NOLA2010 — bare
608
+ right after a statement's text it is NOLA1020. A call intent is a legal
609
+ value in a context statement — it renders as its callee and arguments; a
610
+ HINTED one (`` fn`hint`(…) ``) must be parenthesized or written in a `${}`
611
+ hole (bare, the hint's backtick reads as the next text part: an extractor
612
+ argument is then NOLA2010). A bare `await` or `this` right after a statement's
613
+ text is NOLA1020; anywhere else in a value or hole it is a TypeScript
614
+ error (an item is read at each ask, outside the async body, in a function
615
+ of its own — TS1308 for `await`, TS2683 for `this`): compute the value
616
+ first, or assign `this` to a local. There is no
617
+ `${.member}` prompt scope: a dot cannot start a hole's expression (an
618
+ ordinary syntax error).
619
+ - Editor: completion, hover and TypeScript errors work inside `${…}`.
620
+
621
+ ## The prompt
622
+
623
+ What reaches the model is the developer's text in four utility tags —
624
+ nothing else of Nola's is in the user turn:
625
+
626
+ - `<context function="f">` / `<context module="path">`: one scope — its
627
+ context statements (each starting on its own line), then one
628
+ `<input name="x">` per contextual param and `const .x` binding (a string
629
+ verbatim, anything else as JSON). Plain params are NOT rendered. Outermost
630
+ caller first, asking scope last.
631
+ - `<task>` last: the extractor's instruction; a call intent is
632
+ `<task call="fn">hint</task>` (self-closing without a hint).
633
+ - `<correction>` on the retry, after the rejected reply.
634
+ - The schema is NEVER in the prompt: it rides the provider's structured
635
+ output (`structuredOutputs: false` on a vendor factory puts it in the
636
+ system turn as `<schema>` for servers that cannot enforce it).
637
+ - The system turn is four constant sentences (`DEFAULT_SYSTEM`, exported
638
+ from `@nola-lang/providers`); `system.message` in the config REPLACES it.
639
+ - Every provider implements `infer(req)` over `req.intent` (the ask as
640
+ data) and renders it itself — `renderPrompt(req.intent)` is the default.
641
+ A model that still implements `complete(req)` is NOLA3003.
418
642
 
419
643
  ## Intent methods
420
644
 
@@ -422,10 +646,10 @@ Every intent (extractor, call intent, infer-function result) accepts:
422
646
 
423
647
  ```tsi
424
648
  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);
649
+ const a = ask (..`the title`: string).withRetry(2);
650
+ const b = ask (..`the body`: string).withModel("careful");
651
+ const c = ask (..`a creative tagline`: string).withParams({ temperature: 0.9, maxOutputTokens: 200 });
652
+ const d = ask (..`a one-paragraph summary`: string).withTimeout(10_000);
429
653
  return { a, b, c, d };
430
654
  }
431
655
  ```