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
package/dist/flow.js CHANGED
@@ -16,9 +16,9 @@ import { acquireKey, SignInRequiredError } from "./key.js";
16
16
  import { realLauncher } from "./launch.js";
17
17
  import { nodeVersionWarning } from "./node-version.js";
18
18
  import { openBrowser } from "./open-url.js";
19
- import { detectPackageManager, packageManagerCommands } from "./package-manager.js";
19
+ import { defaultRuntime, detectPackageManager, isRuntime, packageManagerCommands } from "./package-manager.js";
20
20
  import { isProviderId, PROVIDERS, providerById, providerIds } from "./providers.js";
21
- import { entryFile, exampleNames, featuredNames, TEMPLATES, templateByName, templateNames } from "./registry.js";
21
+ import { entryFile, exampleNames, featuredNames, templateByName, templateNames } from "./registry.js";
22
22
  import { ownVersion, scaffold, vendorEnvVar } from "./scaffold.js";
23
23
  import { applyTrial } from "./trial.js";
24
24
  const DEFAULT_DIR = "nola-app";
@@ -62,9 +62,10 @@ function templateOption(t) {
62
62
  /*
63
63
  * The template step is TWO menus (clack's select has no sections): the first
64
64
  * lists the first-menu templates — feature-extraction, function-calling,
65
- * typescript-interop, triage-ticket (the featured example), empty — and one
66
- * "More examples…" row; that row opens the remaining curated examples, with a
67
- * Back row to return. `--template <name>` answers either level directly.
65
+ * agent-loop, typescript-interop, triage-ticket (the featured examples),
66
+ * empty (the one builtin) — and one "More examples…" row; that row opens the
67
+ * remaining curated examples, with a Back row to return. `--template <name>`
68
+ * answers either level directly.
68
69
  */
69
70
  export const TEMPLATE_QUESTION = "Select a template:";
70
71
  export const EXAMPLE_QUESTION = "Select an example:";
@@ -72,11 +73,7 @@ export const EXAMPLE_QUESTION = "Select an example:";
72
73
  export const MORE_EXAMPLES = "more-examples";
73
74
  /** the examples menu's last row — back to the first menu */
74
75
  export const BACK = "back";
75
- /** `"feature-extraction", "function-calling", …` — for the fetch-failure note. */
76
- const builtinNames = () => TEMPLATES.filter((t) => t.source === "builtin")
77
- .map((t) => `"${t.name}"`)
78
- .join(", ");
79
- /** The first menu: the builtin templates and the featured example in registry order, then the examples row. */
76
+ /** The first menu: the featured examples and `empty` in registry order, then the examples row. */
80
77
  export function templateMenu() {
81
78
  const featured = featuredNames().map((name) => templateOption(templateByName(name)));
82
79
  return [...featured, { value: MORE_EXAMPLES, label: "More examples…", hint: exampleNames().join(", ") }];
@@ -84,8 +81,14 @@ export function templateMenu() {
84
81
  /** The second menu: the curated examples the first menu does not carry, in registry order, then Back. */
85
82
  export function exampleMenu() {
86
83
  const examples = exampleNames().map((name) => templateOption(templateByName(name)));
87
- return [...examples, { value: BACK, label: "← Back", hint: "the builtin templates" }];
84
+ return [...examples, { value: BACK, label: "← Back", hint: "the first menu" }];
88
85
  }
86
+ /**
87
+ * The note a failed example fetch gets (interactive: the menu is asked
88
+ * again). Only `empty` ships inside the CLI; every other template is fetched
89
+ * from GitHub, so an offline machine can still scaffold that one.
90
+ */
91
+ export const FETCH_FAILURE_HINT = 'Only "empty" ships inside the CLI and needs no download — every other template is fetched from GitHub.';
89
92
  /** Ask the two-level template menu until a template is chosen; null = cancelled. */
90
93
  async function selectTemplate(prompter) {
91
94
  for (;;) {
@@ -227,13 +230,22 @@ async function resolveProvider(input, prompter, template) {
227
230
  return choice;
228
231
  }
229
232
  }
233
+ /** `--runtime`, else the manager's default (bun → bun). Validated up front like the other flags. */
234
+ function resolveRuntime(input) {
235
+ if (input.runtime !== undefined) {
236
+ if (!isRuntime(input.runtime))
237
+ throw new Error(`invalid --runtime "${input.runtime}" (valid: node, bun)`);
238
+ return input.runtime;
239
+ }
240
+ return defaultRuntime(input.packageManager ?? "npm");
241
+ }
230
242
  /** The provider and setup (editor + agents) questions, in that order, shared by every outcome site; null = cancelled. */
231
243
  async function resolveExtras(input, prompter, dir, template) {
232
244
  const provider = await resolveProvider(input, prompter, template);
233
245
  if (provider === null)
234
246
  return null;
235
247
  const setup = await resolveSetup(input, prompter, dir);
236
- return setup === null ? null : { provider, ...setup };
248
+ return setup === null ? null : { provider, ...setup, runtime: resolveRuntime(input) };
237
249
  }
238
250
  /** Args fill prompts; whatever is missing is asked (never asked non-interactively). */
239
251
  export async function resolveScaffoldOptions(input, prompter) {
@@ -243,6 +255,9 @@ export async function resolveScaffoldOptions(input, prompter) {
243
255
  if (input.ide !== undefined && input.ide !== "vscode" && input.ide !== "none") {
244
256
  throw new Error(`invalid --ide "${input.ide}" (valid: vscode, none)`);
245
257
  }
258
+ if (input.runtime !== undefined && !isRuntime(input.runtime)) {
259
+ throw new Error(`invalid --runtime "${input.runtime}" (valid: node, bun)`);
260
+ }
246
261
  if (input.agents !== undefined)
247
262
  parseAgentsFlag(input.agents); // throws on unknown ids
248
263
  flaggedProvider(input); // throws on an unknown id or a contradiction
@@ -370,10 +385,14 @@ export function plainPrompter() {
370
385
  export const FLOW_OPTIONS = {
371
386
  template: {
372
387
  type: "string",
373
- description: "template to scaffold (feature-extraction, function-calling, typescript-interop, empty, or a curated example)",
388
+ description: "template to scaffold: feature-extraction (the default), function-calling, agent-loop, typescript-interop, triage-ticket, empty, or another curated example",
374
389
  },
375
390
  add: { type: "boolean", description: "add Nola to the existing project in [dir] instead of scaffolding" },
376
391
  ide: { type: "string", description: "editor setup: vscode (the default) | none" },
392
+ runtime: {
393
+ type: "string",
394
+ description: "runtime the project runs on: node | bun (default: bun when bun ran the scaffolder — bunfig.toml preload + `bun src/main.tsi`)",
395
+ },
377
396
  agents: {
378
397
  type: "string",
379
398
  description: `agent skill files: ${AGENT_IDS.join(",")} | all | none (default: claude,universal)`,
@@ -593,6 +612,8 @@ export async function runFlow(args, opts = {}) {
593
612
  agents: args.agents,
594
613
  provider: args.provider,
595
614
  trial: args.trial,
615
+ runtime: args.runtime,
616
+ packageManager: pm,
596
617
  keyPath,
597
618
  signIn: signInNow,
598
619
  cwd: opts.cwd,
@@ -606,7 +627,7 @@ export async function runFlow(args, opts = {}) {
606
627
  }
607
628
  if (outcome.kind === "add") {
608
629
  const grant = outcome.provider === "nola" ? await obtainTrial(prompter, opts, interactive) : null;
609
- const result = await addNola(outcome.dir, { provider: outcome.provider });
630
+ const result = await addNola(outcome.dir, { provider: outcome.provider, runtime: outcome.runtime });
610
631
  const trial = grant
611
632
  ? await applyTrial(outcome.dir, { apiKey: grant.apiKey, hasConfig: !result.wrote.includes("nola.config.ts") })
612
633
  : { wrote: [], skipped: [] };
@@ -626,7 +647,12 @@ export async function runFlow(args, opts = {}) {
626
647
  }
627
648
  const lines = [
628
649
  packageManagerCommands(pm).install,
629
- 'optional start script: "start": "nola run src/main.ts"',
650
+ ...(outcome.runtime === "bun"
651
+ ? [
652
+ 'bunfig.toml: preload = ["@nola-lang/bun/preload"] (runs .tsi from source under Bun)',
653
+ 'optional start script: "start": "bun src/main.ts"',
654
+ ]
655
+ : ['optional start script: "start": "nola run src/main.ts"']),
630
656
  'tsconfig tip: directory-style include (e.g. ["src"]) lets the editor see .tsi files',
631
657
  commented(CONSOLE_COMMAND, CONSOLE_NOTE),
632
658
  ];
@@ -641,7 +667,13 @@ export async function runFlow(args, opts = {}) {
641
667
  const grant = outcome.provider === "nola" ? await obtainTrial(prompter, opts, interactive) : null;
642
668
  // A nola choice without a key (declined sign-in, API failure) scaffolds the plain template — the note above says why.
643
669
  const provider = outcome.provider === "nola" ? (grant ? "nola" : "none") : outcome.provider;
644
- const { files } = await scaffold(outcome.dir, { template: outcome.template, force: outcome.force, provider, ide: outcome.ide });
670
+ const { files } = await scaffold(outcome.dir, {
671
+ template: outcome.template,
672
+ force: outcome.force,
673
+ provider,
674
+ ide: outcome.ide,
675
+ runtime: outcome.runtime,
676
+ });
645
677
  let trialWrote = [];
646
678
  if (grant) {
647
679
  const trial = await applyTrial(outcome.dir, { apiKey: grant.apiKey, hasConfig: false });
@@ -675,7 +707,7 @@ export async function runFlow(args, opts = {}) {
675
707
  }
676
708
  catch (err) {
677
709
  if (err instanceof ExampleFetchError && interactive) {
678
- prompter.note(`${err.message}\nThe builtin templates (${builtinNames()}) work offline.`);
710
+ prompter.note(`${err.message}\n${FETCH_FAILURE_HINT}`);
679
711
  input.dir = outcome.dir; // keep the chosen dir, re-pick the template
680
712
  input.template = undefined;
681
713
  continue;
package/dist/github.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /** Network/acquisition failure — the builtin templates always work offline. */
1
+ /** Network/acquisition failure — `empty`, the one builtin template, always works offline. */
2
2
  export declare class ExampleFetchError extends Error {
3
3
  }
4
4
  export type FetchLike = (url: string) => Promise<{
package/dist/github.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Production example acquisition: GitHub Trees API + raw file fetches. */
2
2
  const REPO = "nola-lang/nola";
3
- /** Network/acquisition failure — the builtin templates always work offline. */
3
+ /** Network/acquisition failure — `empty`, the one builtin template, always works offline. */
4
4
  export class ExampleFetchError extends Error {
5
5
  }
6
6
  /** Try the lockstep release tag first; a repo without that tag serves main. */
package/dist/index.js CHANGED
@@ -87,7 +87,7 @@ import {
87
87
  writeEnvKey,
88
88
  writeSession,
89
89
  writeVscodeSetup
90
- } from "./chunk-LUSVIOAN.js";
90
+ } from "./chunk-UTVGG44K.js";
91
91
  import {
92
92
  clackPrompter
93
93
  } from "./chunk-7IQIRMK6.js";
package/dist/launch.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface Launcher {
17
17
  /**
18
18
  * `code <dir> [<dir>/<entry>]`, detached; "not-found" when VS Code's `code`
19
19
  * command is not on PATH. `entry` is a project-relative file to open as the
20
- * active editor on top of the folder (the scaffold's src/main.ts).
20
+ * active editor on top of the folder (the template's entry file).
21
21
  */
22
22
  openVscode(dir: string, entry?: string): Promise<"opened" | "not-found">;
23
23
  }
@@ -25,7 +25,7 @@ export interface Launcher {
25
25
  * The arguments that open a project in VS Code: the folder alone, or the
26
26
  * folder plus one file. `code <folder> <file>` opens the folder as the
27
27
  * workspace and the file as the active editor — that is how the scaffold
28
- * lands the user on src/main.ts instead of an empty window.
28
+ * lands the user on the entry file instead of an empty window.
29
29
  */
30
30
  export declare function vscodeArgs(dir: string, entry?: string): string[];
31
31
  /**
package/dist/launch.js CHANGED
@@ -5,7 +5,7 @@ import { join } from "node:path";
5
5
  * The arguments that open a project in VS Code: the folder alone, or the
6
6
  * folder plus one file. `code <folder> <file>` opens the folder as the
7
7
  * workspace and the file as the active editor — that is how the scaffold
8
- * lands the user on src/main.ts instead of an empty window.
8
+ * lands the user on the entry file instead of an empty window.
9
9
  */
10
10
  export function vscodeArgs(dir, entry) {
11
11
  return entry === undefined ? [dir] : [dir, join(dir, entry)];
package/dist/main.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  CREATE_COMMAND,
4
4
  dispatch,
5
5
  ownVersion
6
- } from "./chunk-LUSVIOAN.js";
6
+ } from "./chunk-UTVGG44K.js";
7
7
  import "./chunk-FPS2V3S4.js";
8
8
 
9
9
  // src/main.ts
@@ -1,6 +1,7 @@
1
1
  /**
2
- * The Node floor the docs state. Plain `.ts` (the templates' `src/main.ts`)
3
- * is loaded by Node's native type stripping, which is on by default only from
2
+ * The Node floor the docs state. Plain `.ts` (an example's `src/main.ts`, or the
3
+ * plain modules a `.tsi` entry imports) is loaded by Node's native type
4
+ * stripping, which is on by default only from
4
5
  * 22.18 on the 22 line and 23.6 on the 23 line; before that it sits behind
5
6
  * `--experimental-strip-types` and the default loader rejects `.ts` outright
6
7
  * (ERR_UNKNOWN_FILE_EXTENSION) — `npm start` and the F5 launch both die on
@@ -1,6 +1,7 @@
1
1
  /**
2
- * The Node floor the docs state. Plain `.ts` (the templates' `src/main.ts`)
3
- * is loaded by Node's native type stripping, which is on by default only from
2
+ * The Node floor the docs state. Plain `.ts` (an example's `src/main.ts`, or the
3
+ * plain modules a `.tsi` entry imports) is loaded by Node's native type
4
+ * stripping, which is on by default only from
4
5
  * 22.18 on the 22 line and 23.6 on the 23 line; before that it sits behind
5
6
  * `--experimental-strip-types` and the default loader rejects `.ts` outright
6
7
  * (ERR_UNKNOWN_FILE_EXTENSION) — `npm start` and the F5 launch both die on
@@ -47,7 +48,7 @@ export function nodeVersionWarning(version = process.versions.node) {
47
48
  return undefined;
48
49
  const lines = [
49
50
  `Node ${version} is older than Nola needs: Node >= ${NODE_FLOOR}, the first release that runs .ts files natively.`,
50
- "On this Node, `npm start` and the VS Code F5 launch fail with ERR_UNKNOWN_FILE_EXTENSION for src/main.ts.",
51
+ "On this Node, `npm start` and the VS Code F5 launch fail with ERR_UNKNOWN_FILE_EXTENSION on the first plain .ts file they load.",
51
52
  ];
52
53
  if (hasStripTypesFlag(version)) {
53
54
  lines.push("Upgrade Node (recommended), or set NODE_OPTIONS=--experimental-strip-types for `npm start` and add", '"--experimental-strip-types" to runtimeArgs in .vscode/launch.json.');
@@ -14,4 +14,9 @@ export interface PackageManagerCommands {
14
14
  run: string;
15
15
  }
16
16
  export declare function packageManagerCommands(pm: PackageManager): PackageManagerCommands;
17
+ /** The runtime a scaffold targets: Node (the loader, `nola run`) or Bun (`@nola-lang/bun`'s preload, `bun src/main.tsi`). */
18
+ export type Runtime = "node" | "bun";
19
+ export declare function isRuntime(value: string): value is Runtime;
20
+ /** `bun create nola` produces a Bun project unless `--runtime node` says otherwise; every other manager scaffolds for Node. */
21
+ export declare function defaultRuntime(pm: PackageManager): Runtime;
17
22
  //# sourceMappingURL=package-manager.d.ts.map
@@ -5,4 +5,11 @@ export function detectPackageManager(userAgent = process.env.npm_config_user_age
5
5
  export function packageManagerCommands(pm) {
6
6
  return { install: `${pm} install`, start: `${pm} start`, run: `${pm} run` };
7
7
  }
8
+ export function isRuntime(value) {
9
+ return value === "node" || value === "bun";
10
+ }
11
+ /** `bun create nola` produces a Bun project unless `--runtime node` says otherwise; every other manager scaffolds for Node. */
12
+ export function defaultRuntime(pm) {
13
+ return pm === "bun" ? "bun" : "node";
14
+ }
8
15
  //# sourceMappingURL=package-manager.js.map
@@ -4,7 +4,7 @@
4
4
  * template registry: `nola` is the platform, labelled "nola: dev" so the row
5
5
  * reads as a mode of the project rather than a fourth vendor (the free trial, or a key on the
6
6
  * account — see `keyPath`), the four vendors write a bring-your-own config,
7
- * `none` is the offline default (the builtin templates replay their ledgers). typesafe.ai
7
+ * `none` is the offline default (the template's own config: a recorded ledger or a mock). typesafe.ai
8
8
  * serves literal unions and booleans only; it is listed for every template all
9
9
  * the same, and `providerOptions` (flow.ts) brackets that caveat into its hint.
10
10
  */
package/dist/providers.js CHANGED
@@ -10,7 +10,7 @@ export const PROVIDERS = [
10
10
  },
11
11
  { id: "google", label: "Gemini", hint: 'google("gemini-2.5-flash"), reads GEMINI_API_KEY', envVar: "GEMINI_API_KEY", model: 'google("gemini-2.5-flash")' },
12
12
  { id: "typesafe", label: "typesafe.ai", hint: "typesafe(), reads TYPESAFE_API_KEY", envVar: "TYPESAFE_API_KEY", model: "typesafe()" },
13
- { id: "none", label: "Skip for now", hint: "the builtin templates run offline from a replay ledger; pick a model in nola.config.ts later" },
13
+ { id: "none", label: "Skip for now", hint: "keeps the template's own config (the recorded ones run offline); pick a model in nola.config.ts later" },
14
14
  ];
15
15
  export function providerById(id) {
16
16
  return PROVIDERS.find((p) => p.id === id);
@@ -2,31 +2,40 @@
2
2
  * The template menu. Static by design: it is versioned in lockstep with the
3
3
  * git tag the example fetcher pulls from, so names and labels cannot drift.
4
4
  *
5
+ * `empty` is the ONLY builtin template (config + tsconfig + a stub entry,
6
+ * shipped inside this package). Every other template IS a curated example:
7
+ * its files come from `examples/` — on disk in a dev checkout, from GitHub
8
+ * (the lockstep release tag) in production — and the scaffolder adds the
9
+ * project name, the lockstep ranges, the recommended `.gitignore`, the
10
+ * chosen provider's config and the next-steps comment on top.
11
+ *
5
12
  * The first menu leads (in this order) and is organized BY FEATURE:
6
- * `feature-extraction` (the default) and `function-calling` are each one
7
- * `.tsi` file with a top-level ask — the two things Nola does —
13
+ * `feature-extraction` (the default), `function-calling` and `tool-use` are
14
+ * each one `.tsi` file with a top-level ask — the three things Nola does —
15
+ * `agent-loop` is the same shape around a top-level `while`,
8
16
  * `typescript-interop` is the infer-function shape called from plain
9
- * TypeScript, `triage-ticket` (a FEATURED example: fetched from examples/
10
- * like the rest, listed here because it is the one-file shape on a
11
- * non-chat vendor) and `empty`. The other curated examples follow (behind
12
- * "More examples…"). extract-person is deliberately absent —
13
- * typescript-interop IS extract-person plus the replay ledger.
17
+ * TypeScript, `triage-ticket` the one-file shape on a non-chat vendor, then
18
+ * `empty` (a stub `src/main.tsi` to write your own in). The other curated
19
+ * examples follow (behind "More examples…").
20
+ * extract-person is deliberately absent — typescript-interop IS
21
+ * extract-person plus the replay ledger.
14
22
  */
15
23
  export interface TemplateDef {
16
24
  name: string;
17
25
  /** one-line menu description */
18
26
  label: string;
27
+ /** where the files come from: this package's `templates/` dir, or `examples/` */
19
28
  source: "builtin" | "example";
20
29
  /**
21
- * An example listed on the FIRST menu, in registry order beside the
22
- * builtin templates, instead of behind "More examples…". Its files still
23
- * come from examples/ (`source` is unchanged).
30
+ * An example listed on the FIRST menu, in registry order beside `empty`,
31
+ * instead of behind "More examples…". Its files still come from examples/
32
+ * (`source` is unchanged).
24
33
  */
25
34
  featured?: true;
26
35
  /**
27
36
  * The file that IS the program — what `start` runs, launch.json's
28
37
  * `program`, what VS Code opens, where the next-steps comment lands.
29
- * Absent = the plain-TS `src/main.ts` every other template has.
38
+ * Absent = the plain-TS `src/main.ts` the other examples have.
30
39
  */
31
40
  entry?: string;
32
41
  /**
@@ -43,7 +52,7 @@ export interface TemplateDef {
43
52
  /** What a template runs unless it names its own entry: the plain-TS main. */
44
53
  export declare const DEFAULT_ENTRY = "src/main.ts";
45
54
  export declare const TEMPLATES: readonly TemplateDef[];
46
- /** The first menu's templates: the builtins and the featured example, in registry order. */
55
+ /** The first menu's templates: the featured examples and `empty`, in registry order. */
47
56
  export declare function featuredNames(): string[];
48
57
  /** The examples behind "More examples…": every example the first menu does not carry. */
49
58
  export declare function exampleNames(): string[];
package/dist/registry.js CHANGED
@@ -1,17 +1,39 @@
1
1
  /** What a template runs unless it names its own entry: the plain-TS main. */
2
2
  export const DEFAULT_ENTRY = "src/main.ts";
3
3
  export const TEMPLATES = [
4
- { name: "feature-extraction", label: "extract typed data from context", source: "builtin", entry: "src/main.tsi" },
4
+ {
5
+ name: "feature-extraction",
6
+ label: "extract typed data from context",
7
+ source: "example",
8
+ featured: true,
9
+ entry: "src/main.tsi",
10
+ },
5
11
  {
6
12
  name: "function-calling",
7
13
  label: "call an async function from a TypeScript file",
8
- source: "builtin",
14
+ source: "example",
15
+ featured: true,
16
+ entry: "src/main.tsi",
17
+ },
18
+ {
19
+ name: "tool-use",
20
+ label: "offer functions as tools: the model calls them as it needs before it answers",
21
+ source: "example",
22
+ featured: true,
23
+ entry: "src/main.tsi",
24
+ },
25
+ {
26
+ name: "agent-loop",
27
+ label: "an agent loop in one .tsi file: a top-level while with an ask, and a context statement read on every pass",
28
+ source: "example",
29
+ featured: true,
9
30
  entry: "src/main.tsi",
10
31
  },
11
32
  {
12
33
  name: "typescript-interop",
13
34
  label: "an infer function example, imported and awaited from plain TypeScript",
14
- source: "builtin",
35
+ source: "example",
36
+ featured: true,
15
37
  },
16
38
  {
17
39
  name: "triage-ticket",
@@ -21,7 +43,7 @@ export const TEMPLATES = [
21
43
  entry: "src/main.tsi",
22
44
  provider: { label: "typesafe.ai", envVar: "TYPESAFE_API_KEY" },
23
45
  },
24
- { name: "empty", label: "nola.config + tsconfig only, bring your own code", source: "builtin" },
46
+ { name: "empty", label: "nola.config + tsconfig only, bring your own code", source: "builtin", entry: "src/main.tsi" },
25
47
  { name: "file-ticket", label: "call intents: the model fills a function's arguments", source: "example" },
26
48
  { name: "extract-resume", label: "nested arrays of objects, JSDoc schema descriptions", source: "example" },
27
49
  { name: "extract-invoice", label: "same-file type references, optional fields", source: "example" },
@@ -29,7 +51,7 @@ export const TEMPLATES = [
29
51
  { name: "chain-of-thought", label: "two asks sharing accumulating context", source: "example" },
30
52
  { name: "research-notes", label: "TS control flow orchestrating nola functions", source: "example" },
31
53
  ];
32
- /** The first menu's templates: the builtins and the featured example, in registry order. */
54
+ /** The first menu's templates: the featured examples and `empty`, in registry order. */
33
55
  export function featuredNames() {
34
56
  return TEMPLATES.filter((t) => t.source === "builtin" || t.featured).map((t) => t.name);
35
57
  }
@@ -1,3 +1,4 @@
1
+ import type { Runtime } from "./package-manager.js";
1
2
  import { type ProviderId } from "./providers.js";
2
3
  export interface ScaffoldResult {
3
4
  root: string;
@@ -13,18 +14,22 @@ export interface ScaffoldOptions {
13
14
  /**
14
15
  * The inference provider chosen in the wizard; default "none" (the
15
16
  * 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.
17
+ * that provider's config and drops the offline templates' replay ledger.
18
18
  */
19
19
  provider?: ProviderId;
20
20
  /**
21
21
  * 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.
22
+ * comment that opens the template's entry file: with VS Code it names F5,
23
+ * breakpoints and the recommended extension (all of which the .vscode
24
+ * files the flow writes make true); without one it stays editor-neutral.
26
25
  */
27
26
  ide?: "vscode" | "none";
27
+ /**
28
+ * The runtime the project runs on; default "node". "bun" adds bunfig.toml with the
29
+ * @nola-lang/bun preload, a `bun <entry>` start script, the devDependency and
30
+ * engines.bun — build/check stay the Node CLI (spec 2026-10-14-bun-support).
31
+ */
32
+ runtime?: Runtime;
28
33
  }
29
34
  /** The config a live provider choice writes over the template's own (`templates/_providers/<id>.config.ts`). */
30
35
  export declare function providerConfigUrl(provider: Exclude<ProviderId, "none">): URL;
@@ -41,24 +46,39 @@ export declare function vendorEnvVar(template: string, provider: ProviderId): st
41
46
  * `.gitignore` keeps trackable (`!.env.example`), so it can be committed.
42
47
  */
43
48
  export declare function envExample(envVar: string): string;
49
+ /** The Bun project's bunfig.toml: the preload for `bun src/main.tsi` and for `bun test`. */
50
+ export declare const BUNFIG_TOML = "preload = [\"@nola-lang/bun/preload\"]\n\n[test]\npreload = [\"@nola-lang/bun/preload\"]\n";
51
+ /** The Bun floor @nola-lang/bun needs (process.getBuiltinModule). */
52
+ export declare const BUN_ENGINE = ">=1.3.0";
53
+ /** A Bun project's manifest additions on top of the template's: the start script, the devDependency, engines.bun. */
54
+ export declare function applyBunRuntime(content: string, opts: {
55
+ entry: string;
56
+ version: string;
57
+ }): string;
44
58
  /**
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.
59
+ * Add the recommended `.gitignore` to a template's files. Templates are
60
+ * copied as committed — examples from `examples/`, where the monorepo's root
61
+ * ignore file covers them — so they carry none; one a template does carry wins.
48
62
  */
49
63
  export declare function withRecommendedGitignore(files: Map<string, string>): Promise<Map<string, string>>;
50
64
  /**
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.
65
+ * The comment that opens the entry file (the `.tsi` entry of a one-file
66
+ * template and of `empty`, else the plain-TS src/main.ts) — the file the
67
+ * scaffold lands the user on (VS Code opens it as the active editor), so it
68
+ * carries the first three things to do. The VS Code variant only ships with
69
+ * the editor step's .vscode files, which are what make F5 and the extension
70
+ * prompt real.
56
71
  */
57
- export declare function nextStepsComment(ide: "vscode" | "none", template: string): string;
72
+ export declare function nextStepsComment(ide: "vscode" | "none", template: string, runtime?: Runtime): string;
58
73
  export declare function ownVersion(): Promise<string>;
59
74
  /**
60
75
  * 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`.
76
+ * points — the `create-nola-lang` bin (`npm create nola-lang`) and `nola init`
77
+ * — and both sources: the files are collected as committed, then the scaffold
78
+ * adds what a project needs on top — the chosen provider's config (dropping
79
+ * the replay ledger), the project name and the lockstep ranges in
80
+ * package.json, the recommended .gitignore, the vendor key's .env.example,
81
+ * and the next-steps comment at the top of the entry file.
62
82
  */
63
83
  export declare function scaffold(targetDir: string, opts?: ScaffoldOptions): Promise<ScaffoldResult>;
64
84
  //# sourceMappingURL=scaffold.d.ts.map