create-nola-lang 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Scaffolds a new [Nola](https://github.com/nola-lang/nola) project:
4
4
 
5
5
  ```bash
6
- npm create nola-lang my-app
6
+ npm create nola my-app # or: npm create nola-lang my-app
7
7
  cd my-app
8
8
  npm install
9
9
  npm start # runs offline via a committed replay ledger - no API key needed
@@ -11,5 +11,6 @@ npm start # runs offline via a committed replay ledger - no API key neede
11
11
 
12
12
  You get a typed extraction example (`src/person.tsi` + a plain-TS consumer),
13
13
  `nola.config.ts`, tsconfig, and a recorded replay ledger so the first run
14
- works without any API key. `nola init` (from the `nola-lang` package) lays
14
+ works without any API key. `npm create nola` is a short alias of this package
15
+ (`create-nola`); `nola init` (from the `nola-lang` package) lays
15
16
  down the same starter.
package/dist/agents.d.ts CHANGED
@@ -6,6 +6,22 @@ export declare const AGENT_OPTIONS: {
6
6
  label: string;
7
7
  hint: string;
8
8
  }[];
9
+ /**
10
+ * Adapters that inject context inline embed SKILL.md's BODY verbatim, so no
11
+ * adapter restates the language rules in its own words and the four can
12
+ * never drift from each other (spec §2).
13
+ */
14
+ export interface SkillSource {
15
+ /** the frontmatter block of SKILL.md, without the --- fences */
16
+ frontmatter: string;
17
+ /** the first heading up to (not including) `## References` */
18
+ body: string;
19
+ /** the whole file, verbatim — what the claude adapter copies */
20
+ full: string;
21
+ }
22
+ export declare function readSkillSource(): Promise<SkillSource>;
23
+ /** The `## Nola` section for AGENTS.md — also the paste snippet when skipped. */
24
+ export declare function agentsSection(s: SkillSource, version: string): string;
9
25
  /** Agents whose tooling is already present in the project. */
10
26
  export declare function detectAgents(targetDir: string): AgentId[];
11
27
  /** The interactive preselection: detected agents + the cross-agent baseline. */
@@ -17,7 +33,16 @@ export interface AgentSetupResult {
17
33
  wrote: string[];
18
34
  /** human-readable notes about files left untouched */
19
35
  skipped: string[];
36
+ /** true when something was left stale — the caller can suggest --force */
37
+ stale: boolean;
38
+ }
39
+ export interface WriteAgentSkillsOptions {
40
+ /** replace files stamped by another version (never touches unstamped files) */
41
+ force?: boolean;
20
42
  }
21
- /** Write the selected adapters; never rewrites an existing file. */
22
- export declare function writeAgentSkills(targetDir: string, agents: AgentId[]): Promise<AgentSetupResult>;
43
+ /**
44
+ * Write the selected adapters as self-contained, version-stamped content.
45
+ * Existing files are classified, never blindly overwritten (spec §3).
46
+ */
47
+ export declare function writeAgentSkills(targetDir: string, agents: AgentId[], opts?: WriteAgentSkillsOptions): Promise<AgentSetupResult>;
23
48
  //# sourceMappingURL=agents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAEpE,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EAAiD,CAAC;AAE1F,wEAAwE;AACxE,eAAO,MAAM,aAAa,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAK1E,CAAC;AAkEF,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAOzD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAE1D;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAUxD;AAED,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,oEAAoE;AACpE,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsBtG"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAEpE,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EAAiD,CAAC;AAE1F,wEAAwE;AACxE,eAAO,MAAM,aAAa,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAK1E,CAAC;AAiBF;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAQ5D;AA6BD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAMD,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAOzD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAE1D;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAaxD;AAED,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAyCD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,EAAE,EACjB,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAsD3B"}
package/dist/agents.js CHANGED
@@ -1,70 +1,68 @@
1
1
  import { existsSync } from "node:fs";
2
- import { mkdir, writeFile } from "node:fs/promises";
2
+ import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { ownVersion } from "./scaffold.js";
4
6
  export const AGENT_IDS = ["claude", "cursor", "copilot", "agents-md"];
5
7
  /** Shared option list for every "Set up coding agents?" multiselect. */
6
8
  export const AGENT_OPTIONS = [
7
- { value: "agents-md", label: "AGENTS.md", hint: "cross-agent pointer file (Codex, Gemini CLI, …)" },
8
- { value: "claude", label: "Claude Code", hint: ".claude/skills/nola/SKILL.md" },
9
+ { value: "agents-md", label: "AGENTS.md", hint: "cross-agent instructions (Codex, Gemini CLI, …)" },
10
+ { value: "claude", label: "Claude Code", hint: ".claude/skills/nola/ (full skill + references)" },
9
11
  { value: "cursor", label: "Cursor", hint: ".cursor/rules/nola.mdc" },
10
12
  { value: "copilot", label: "GitHub Copilot", hint: ".github/instructions/nola.instructions.md" },
11
13
  ];
12
14
  /**
13
- * Adapters are POINTERS into the installed package, never copiesthe
14
- * content an agent reads then always version-matches the installed compiler
15
- * (spec 2026-08-14-agent-skill-distribution-design.md §1).
15
+ * The canonical skill content, resolved relative to this module`src/` in
16
+ * this checkout, `dist/` in the published tarball, both one level under the
17
+ * package root (the same trick TEMPLATES_DIR uses in scaffold.ts).
16
18
  */
17
- const POINTER_BODY = `Read \`node_modules/nola-lang/skills/nola/SKILL.md\` and the files under
18
- \`node_modules/nola-lang/skills/nola/references/\` before writing or editing
19
- \`.tsi\` files. If that path is missing, run \`npm install\` first.
20
- `;
21
- /**
22
- * Clause-free variant of POINTER_BODY: just the paths + the npm-install
23
- * fallback. For adapters whose own lead-in sentence already states the
24
- * "before writing or editing .tsi files" clause, so it isn't said twice.
25
- */
26
- const POINTER_PATHS = `\`node_modules/nola-lang/skills/nola/SKILL.md\` and the files under
27
- \`node_modules/nola-lang/skills/nola/references/\`. If that path is missing, run
28
- \`npm install\` first.
29
- `;
30
- const CLAUDE_SKILL = `---
31
- name: nola
32
- description: Writing Nola .tsi files — a TypeScript superset with infer
33
- functions and ask extractors. Use when creating or editing .tsi files or
34
- nola.config.ts.
35
- ---
36
-
37
- This project uses Nola. Before writing or editing \`.tsi\` files, read the
38
- version-matched skill content installed with the language:
39
- ${POINTER_PATHS}`;
40
- const AGENTS_SECTION = `## Nola
41
-
42
- This project uses Nola (\`.tsi\` files — a TypeScript superset; not valid
43
- TS). Before writing or editing \`.tsi\` files or \`nola.config.ts\`, read the
44
- version-matched skill content installed with the language:
45
-
46
- ${POINTER_PATHS}`;
47
- const AGENTS_MD = `# AGENTS.md
48
-
49
- ${AGENTS_SECTION}`;
50
- const CURSOR_RULE = `---
51
- description: Nola (.tsi) language rules
52
- globs: ["**/*.tsi", "nola.config.ts"]
53
- alwaysApply: false
54
- ---
55
-
56
- ${POINTER_BODY}`;
57
- const COPILOT_INSTRUCTIONS = `---
58
- applyTo: "**/*.tsi"
59
- ---
60
-
61
- ${POINTER_BODY}`;
62
- const ADAPTERS = {
63
- claude: { path: ".claude/skills/nola/SKILL.md", content: CLAUDE_SKILL },
64
- cursor: { path: ".cursor/rules/nola.mdc", content: CURSOR_RULE },
65
- copilot: { path: ".github/instructions/nola.instructions.md", content: COPILOT_INSTRUCTIONS },
66
- "agents-md": { path: "AGENTS.md", content: AGENTS_MD },
67
- };
19
+ const SKILLS_DIR = fileURLToPath(new URL("../skills/nola/", import.meta.url));
20
+ /** Marks a generated file so staleness is detectable (spec §3). */
21
+ const STAMP_RE = /^<!-- nola-skill v(\S+)/m;
22
+ const stamp = (version) => `<!-- nola-skill v${version} — regenerate with: nola skill install --force -->`;
23
+ /** Pointer-era adapters (0.1.0–0.1.3) named this path; they are superseded. */
24
+ const LEGACY_MARKER = "node_modules/nola-lang/skills/nola";
25
+ export async function readSkillSource() {
26
+ const full = (await readFile(join(SKILLS_DIR, "SKILL.md"), "utf8")).replaceAll("\r\n", "\n");
27
+ const fm = /^---\n([\s\S]*?)\n---\n/.exec(full);
28
+ if (!fm)
29
+ throw new Error("SKILL.md is missing its frontmatter block");
30
+ const afterFm = full.slice(fm[0].length);
31
+ const refsAt = afterFm.indexOf("\n## References");
32
+ const body = (refsAt === -1 ? afterFm : afterFm.slice(0, refsAt)).trim();
33
+ return { frontmatter: fm[1], body, full };
34
+ }
35
+ /** Points at the project-local copy never node_modules (spec §1). */
36
+ const LOCAL_REFERENCES = [
37
+ "Deeper references, when this repo has them:",
38
+ "`.claude/skills/nola/references/` — syntax.md, patterns.md, config.md,",
39
+ "pitfalls.md.",
40
+ ].join("\n");
41
+ function cursorRule(s, version) {
42
+ return [
43
+ "---",
44
+ "description: Nola (.tsi) language rules",
45
+ 'globs: ["**/*.tsi", "nola.config.ts"]',
46
+ "alwaysApply: false",
47
+ "---",
48
+ stamp(version),
49
+ "",
50
+ s.body,
51
+ "",
52
+ LOCAL_REFERENCES,
53
+ "",
54
+ ].join("\n");
55
+ }
56
+ function copilotInstructions(s, version) {
57
+ return ["---", 'applyTo: "**/*.tsi"', "---", stamp(version), "", s.body, "", LOCAL_REFERENCES, ""].join("\n");
58
+ }
59
+ /** The `## Nola` section for AGENTS.md — also the paste snippet when skipped. */
60
+ export function agentsSection(s, version) {
61
+ return ["## Nola", "", stamp(version), "", s.body, "", LOCAL_REFERENCES, ""].join("\n");
62
+ }
63
+ function agentsMd(s, version) {
64
+ return `# AGENTS.md\n\n${agentsSection(s, version)}`;
65
+ }
68
66
  /** Agents whose tooling is already present in the project. */
69
67
  export function detectAgents(targetDir) {
70
68
  const root = resolve(targetDir);
@@ -87,7 +85,10 @@ export function parseAgentsFlag(value) {
87
85
  return [...AGENT_IDS];
88
86
  if (value === "none")
89
87
  return [];
90
- const ids = value.split(",").map((s) => s.trim()).filter(Boolean);
88
+ const ids = value
89
+ .split(",")
90
+ .map((s) => s.trim())
91
+ .filter(Boolean);
91
92
  for (const id of ids) {
92
93
  if (!AGENT_IDS.includes(id)) {
93
94
  throw new Error(`invalid --agents "${value}" (valid: ${AGENT_IDS.join(", ")} — or all, none)`);
@@ -95,27 +96,95 @@ export function parseAgentsFlag(value) {
95
96
  }
96
97
  return [...new Set(ids)];
97
98
  }
98
- /** Write the selected adapters; never rewrites an existing file. */
99
- export async function writeAgentSkills(targetDir, agents) {
99
+ /** Classify an existing target per spec §3. */
100
+ function disposition(path, existing, version, force) {
101
+ const found = STAMP_RE.exec(existing);
102
+ // Legacy pointers are checked FIRST and independently of the stamp: the
103
+ // 0.1.0–0.1.3 adapters predate stamping, so they carry none at all.
104
+ const legacy = existing.includes(LEGACY_MARKER);
105
+ if (!legacy) {
106
+ if (!found) {
107
+ return {
108
+ write: false,
109
+ note: `${path} already exists and was not generated by nola — left untouched`,
110
+ stale: false,
111
+ };
112
+ }
113
+ if (found[1] === version)
114
+ return { write: false, note: `${path} is up to date (v${version})`, stale: false };
115
+ }
116
+ if (force)
117
+ return { write: true };
118
+ const what = legacy
119
+ ? `${path} uses the superseded pointer form`
120
+ : `${path} is stale (v${found?.[1]} → v${version})`;
121
+ return { write: false, note: `${what} — re-run with --force to replace it`, stale: true };
122
+ }
123
+ /** Copy the canonical skill dir verbatim (stamp aside) into .claude/skills/nola/. */
124
+ async function writeClaudeSkill(root, s, version) {
125
+ const dir = join(root, ".claude", "skills", "nola");
126
+ await mkdir(join(dir, "references"), { recursive: true });
127
+ const stamped = s.full.replace(/^(---\n[\s\S]*?\n---\n)/, `$1${stamp(version)}\n`);
128
+ await writeFile(join(dir, "SKILL.md"), stamped);
129
+ const wrote = [".claude/skills/nola/SKILL.md"];
130
+ for (const name of (await readdir(join(SKILLS_DIR, "references"))).sort()) {
131
+ await writeFile(join(dir, "references", name), await readFile(join(SKILLS_DIR, "references", name), "utf8"));
132
+ wrote.push(`.claude/skills/nola/references/${name}`);
133
+ }
134
+ return wrote;
135
+ }
136
+ /**
137
+ * Write the selected adapters as self-contained, version-stamped content.
138
+ * Existing files are classified, never blindly overwritten (spec §3).
139
+ */
140
+ export async function writeAgentSkills(targetDir, agents, opts = {}) {
100
141
  const root = resolve(targetDir);
142
+ const version = await ownVersion();
143
+ const source = await readSkillSource();
144
+ const force = opts.force === true;
101
145
  const wrote = [];
102
146
  const skipped = [];
147
+ let stale = false;
148
+ const flat = {
149
+ cursor: { path: ".cursor/rules/nola.mdc", content: cursorRule(source, version) },
150
+ copilot: { path: ".github/instructions/nola.instructions.md", content: copilotInstructions(source, version) },
151
+ "agents-md": { path: "AGENTS.md", content: agentsMd(source, version) },
152
+ };
103
153
  for (const id of AGENT_IDS) {
104
154
  if (!agents.includes(id))
105
155
  continue;
106
- const { path, content } = ADAPTERS[id];
156
+ if (id === "claude") {
157
+ const marker = join(root, ".claude", "skills", "nola", "SKILL.md");
158
+ if (existsSync(marker)) {
159
+ const d = disposition(".claude/skills/nola/SKILL.md", await readFile(marker, "utf8"), version, force);
160
+ if (!d.write) {
161
+ skipped.push(d.note);
162
+ stale ||= d.stale;
163
+ continue;
164
+ }
165
+ }
166
+ wrote.push(...(await writeClaudeSkill(root, source, version)));
167
+ continue;
168
+ }
169
+ const { path, content } = flat[id];
107
170
  const abs = join(root, ...path.split("/"));
108
171
  if (existsSync(abs)) {
109
- // AGENTS.md commonly pre-exists; hand the section over for manual paste.
110
- skipped.push(id === "agents-md"
111
- ? `AGENTS.md already exists — left untouched. Add this section manually:\n\n${AGENTS_SECTION}`
112
- : `${path} already exists — left untouched`);
113
- continue;
172
+ // AGENTS.md is a user file: never modified, --force included.
173
+ if (id === "agents-md") {
174
+ skipped.push(`AGENTS.md already exists — left untouched. Add this section manually:\n\n${agentsSection(source, version)}`);
175
+ continue;
176
+ }
177
+ const d = disposition(path, await readFile(abs, "utf8"), version, force);
178
+ if (!d.write) {
179
+ skipped.push(d.note);
180
+ stale ||= d.stale;
181
+ continue;
182
+ }
114
183
  }
115
184
  await mkdir(dirname(abs), { recursive: true });
116
185
  await writeFile(abs, content);
117
186
  wrote.push(path);
118
187
  }
119
- return { wrote, skipped };
188
+ return { wrote, skipped, stale };
120
189
  }
121
190
  //# sourceMappingURL=agents.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
@@ -224,61 +224,60 @@ async function addNola(targetDir, opts = {}) {
224
224
 
225
225
  // src/agents.ts
226
226
  import { existsSync as existsSync4 } from "node:fs";
227
- import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
227
+ import { mkdir as mkdir2, readdir as readdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
228
228
  import { dirname, join as join4, resolve as resolve3 } from "node:path";
229
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
229
230
  var AGENT_IDS = ["claude", "cursor", "copilot", "agents-md"];
230
231
  var AGENT_OPTIONS = [
231
- { value: "agents-md", label: "AGENTS.md", hint: "cross-agent pointer file (Codex, Gemini CLI, \u2026)" },
232
- { value: "claude", label: "Claude Code", hint: ".claude/skills/nola/SKILL.md" },
232
+ { value: "agents-md", label: "AGENTS.md", hint: "cross-agent instructions (Codex, Gemini CLI, \u2026)" },
233
+ { value: "claude", label: "Claude Code", hint: ".claude/skills/nola/ (full skill + references)" },
233
234
  { value: "cursor", label: "Cursor", hint: ".cursor/rules/nola.mdc" },
234
235
  { value: "copilot", label: "GitHub Copilot", hint: ".github/instructions/nola.instructions.md" }
235
236
  ];
236
- var POINTER_BODY = `Read \`node_modules/nola-lang/skills/nola/SKILL.md\` and the files under
237
- \`node_modules/nola-lang/skills/nola/references/\` before writing or editing
238
- \`.tsi\` files. If that path is missing, run \`npm install\` first.
239
- `;
240
- var POINTER_PATHS = `\`node_modules/nola-lang/skills/nola/SKILL.md\` and the files under
241
- \`node_modules/nola-lang/skills/nola/references/\`. If that path is missing, run
242
- \`npm install\` first.
243
- `;
244
- var CLAUDE_SKILL = `---
245
- name: nola
246
- description: Writing Nola .tsi files \u2014 a TypeScript superset with infer
247
- functions and ask extractors. Use when creating or editing .tsi files or
248
- nola.config.ts.
249
- ---
250
-
251
- This project uses Nola. Before writing or editing \`.tsi\` files, read the
252
- version-matched skill content installed with the language:
253
- ${POINTER_PATHS}`;
254
- var AGENTS_SECTION = `## Nola
255
-
256
- This project uses Nola (\`.tsi\` files \u2014 a TypeScript superset; not valid
257
- TS). Before writing or editing \`.tsi\` files or \`nola.config.ts\`, read the
258
- version-matched skill content installed with the language:
259
-
260
- ${POINTER_PATHS}`;
261
- var AGENTS_MD = `# AGENTS.md
262
-
263
- ${AGENTS_SECTION}`;
264
- var CURSOR_RULE = `---
265
- description: Nola (.tsi) language rules
266
- globs: ["**/*.tsi", "nola.config.ts"]
267
- alwaysApply: false
268
- ---
269
-
270
- ${POINTER_BODY}`;
271
- var COPILOT_INSTRUCTIONS = `---
272
- applyTo: "**/*.tsi"
273
- ---
237
+ var SKILLS_DIR = fileURLToPath3(new URL("../skills/nola/", import.meta.url));
238
+ var STAMP_RE = /^<!-- nola-skill v(\S+)/m;
239
+ var stamp = (version) => `<!-- nola-skill v${version} \u2014 regenerate with: nola skill install --force -->`;
240
+ var LEGACY_MARKER = "node_modules/nola-lang/skills/nola";
241
+ async function readSkillSource() {
242
+ const full = (await readFile4(join4(SKILLS_DIR, "SKILL.md"), "utf8")).replaceAll("\r\n", "\n");
243
+ const fm = /^---\n([\s\S]*?)\n---\n/.exec(full);
244
+ if (!fm) throw new Error("SKILL.md is missing its frontmatter block");
245
+ const afterFm = full.slice(fm[0].length);
246
+ const refsAt = afterFm.indexOf("\n## References");
247
+ const body = (refsAt === -1 ? afterFm : afterFm.slice(0, refsAt)).trim();
248
+ return { frontmatter: fm[1], body, full };
249
+ }
250
+ var LOCAL_REFERENCES = [
251
+ "Deeper references, when this repo has them:",
252
+ "`.claude/skills/nola/references/` \u2014 syntax.md, patterns.md, config.md,",
253
+ "pitfalls.md."
254
+ ].join("\n");
255
+ function cursorRule(s, version) {
256
+ return [
257
+ "---",
258
+ "description: Nola (.tsi) language rules",
259
+ 'globs: ["**/*.tsi", "nola.config.ts"]',
260
+ "alwaysApply: false",
261
+ "---",
262
+ stamp(version),
263
+ "",
264
+ s.body,
265
+ "",
266
+ LOCAL_REFERENCES,
267
+ ""
268
+ ].join("\n");
269
+ }
270
+ function copilotInstructions(s, version) {
271
+ return ["---", 'applyTo: "**/*.tsi"', "---", stamp(version), "", s.body, "", LOCAL_REFERENCES, ""].join("\n");
272
+ }
273
+ function agentsSection(s, version) {
274
+ return ["## Nola", "", stamp(version), "", s.body, "", LOCAL_REFERENCES, ""].join("\n");
275
+ }
276
+ function agentsMd(s, version) {
277
+ return `# AGENTS.md
274
278
 
275
- ${POINTER_BODY}`;
276
- var ADAPTERS = {
277
- claude: { path: ".claude/skills/nola/SKILL.md", content: CLAUDE_SKILL },
278
- cursor: { path: ".cursor/rules/nola.mdc", content: CURSOR_RULE },
279
- copilot: { path: ".github/instructions/nola.instructions.md", content: COPILOT_INSTRUCTIONS },
280
- "agents-md": { path: "AGENTS.md", content: AGENTS_MD }
281
- };
279
+ ${agentsSection(s, version)}`;
280
+ }
282
281
  function detectAgents(targetDir) {
283
282
  const root = resolve3(targetDir);
284
283
  const found = [];
@@ -301,27 +300,87 @@ function parseAgentsFlag(value) {
301
300
  }
302
301
  return [...new Set(ids)];
303
302
  }
304
- async function writeAgentSkills(targetDir, agents) {
303
+ function disposition(path, existing, version, force) {
304
+ const found = STAMP_RE.exec(existing);
305
+ const legacy = existing.includes(LEGACY_MARKER);
306
+ if (!legacy) {
307
+ if (!found) {
308
+ return {
309
+ write: false,
310
+ note: `${path} already exists and was not generated by nola \u2014 left untouched`,
311
+ stale: false
312
+ };
313
+ }
314
+ if (found[1] === version) return { write: false, note: `${path} is up to date (v${version})`, stale: false };
315
+ }
316
+ if (force) return { write: true };
317
+ const what = legacy ? `${path} uses the superseded pointer form` : `${path} is stale (v${found?.[1]} \u2192 v${version})`;
318
+ return { write: false, note: `${what} \u2014 re-run with --force to replace it`, stale: true };
319
+ }
320
+ async function writeClaudeSkill(root, s, version) {
321
+ const dir = join4(root, ".claude", "skills", "nola");
322
+ await mkdir2(join4(dir, "references"), { recursive: true });
323
+ const stamped = s.full.replace(/^(---\n[\s\S]*?\n---\n)/, `$1${stamp(version)}
324
+ `);
325
+ await writeFile3(join4(dir, "SKILL.md"), stamped);
326
+ const wrote = [".claude/skills/nola/SKILL.md"];
327
+ for (const name of (await readdir3(join4(SKILLS_DIR, "references"))).sort()) {
328
+ await writeFile3(join4(dir, "references", name), await readFile4(join4(SKILLS_DIR, "references", name), "utf8"));
329
+ wrote.push(`.claude/skills/nola/references/${name}`);
330
+ }
331
+ return wrote;
332
+ }
333
+ async function writeAgentSkills(targetDir, agents, opts = {}) {
305
334
  const root = resolve3(targetDir);
335
+ const version = await ownVersion();
336
+ const source = await readSkillSource();
337
+ const force = opts.force === true;
306
338
  const wrote = [];
307
339
  const skipped = [];
340
+ let stale = false;
341
+ const flat = {
342
+ cursor: { path: ".cursor/rules/nola.mdc", content: cursorRule(source, version) },
343
+ copilot: { path: ".github/instructions/nola.instructions.md", content: copilotInstructions(source, version) },
344
+ "agents-md": { path: "AGENTS.md", content: agentsMd(source, version) }
345
+ };
308
346
  for (const id of AGENT_IDS) {
309
347
  if (!agents.includes(id)) continue;
310
- const { path, content } = ADAPTERS[id];
348
+ if (id === "claude") {
349
+ const marker = join4(root, ".claude", "skills", "nola", "SKILL.md");
350
+ if (existsSync4(marker)) {
351
+ const d = disposition(".claude/skills/nola/SKILL.md", await readFile4(marker, "utf8"), version, force);
352
+ if (!d.write) {
353
+ skipped.push(d.note);
354
+ stale ||= d.stale;
355
+ continue;
356
+ }
357
+ }
358
+ wrote.push(...await writeClaudeSkill(root, source, version));
359
+ continue;
360
+ }
361
+ const { path, content } = flat[id];
311
362
  const abs = join4(root, ...path.split("/"));
312
363
  if (existsSync4(abs)) {
313
- skipped.push(
314
- id === "agents-md" ? `AGENTS.md already exists \u2014 left untouched. Add this section manually:
364
+ if (id === "agents-md") {
365
+ skipped.push(
366
+ `AGENTS.md already exists \u2014 left untouched. Add this section manually:
315
367
 
316
- ${AGENTS_SECTION}` : `${path} already exists \u2014 left untouched`
317
- );
318
- continue;
368
+ ${agentsSection(source, version)}`
369
+ );
370
+ continue;
371
+ }
372
+ const d = disposition(path, await readFile4(abs, "utf8"), version, force);
373
+ if (!d.write) {
374
+ skipped.push(d.note);
375
+ stale ||= d.stale;
376
+ continue;
377
+ }
319
378
  }
320
379
  await mkdir2(dirname(abs), { recursive: true });
321
380
  await writeFile3(abs, content);
322
381
  wrote.push(path);
323
382
  }
324
- return { wrote, skipped };
383
+ return { wrote, skipped, stale };
325
384
  }
326
385
 
327
386
  // src/ide.ts
@@ -375,8 +434,19 @@ async function writeVscodeSetup(targetDir) {
375
434
 
376
435
  // src/flow.ts
377
436
  import { existsSync as existsSync6 } from "node:fs";
378
- import { readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
437
+ import { readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
379
438
  import { basename as basename2, join as join6, resolve as resolve5 } from "node:path";
439
+
440
+ // src/package-manager.ts
441
+ function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
442
+ const name = userAgent?.split("/")[0]?.trim();
443
+ return name === "pnpm" || name === "yarn" || name === "bun" ? name : "npm";
444
+ }
445
+ function packageManagerCommands(pm) {
446
+ return { install: `${pm} install`, start: `${pm} start`, run: `${pm} run` };
447
+ }
448
+
449
+ // src/flow.ts
380
450
  var DEFAULT_DIR = "nola-app";
381
451
  var DEFAULT_TEMPLATE = "starter";
382
452
  function templateOption(t) {
@@ -427,7 +497,7 @@ async function resolveScaffoldOptions(input, prompter) {
427
497
  if (existsSync6(cwdManifest)) {
428
498
  let pkgName = "this project";
429
499
  try {
430
- const parsed = JSON.parse(await readFile4(cwdManifest, "utf8"));
500
+ const parsed = JSON.parse(await readFile5(cwdManifest, "utf8"));
431
501
  if (parsed.name) pkgName = `"${parsed.name}"`;
432
502
  } catch {
433
503
  }
@@ -453,7 +523,7 @@ async function resolveScaffoldOptions(input, prompter) {
453
523
  }
454
524
  let force = false;
455
525
  const absRoot = resolve5(dir);
456
- if (existsSync6(absRoot) && (await readdir3(absRoot)).length > 0) {
526
+ if (existsSync6(absRoot) && (await readdir4(absRoot)).length > 0) {
457
527
  if (!input.interactive) throw new Error(`target directory ${absRoot} is not empty`);
458
528
  if (existsSync6(join6(absRoot, "package.json"))) {
459
529
  const choice = await prompter.select(`Target directory "${dir}" is not empty and has a package.json.`, [
@@ -511,9 +581,11 @@ function plainPrompter() {
511
581
  outro: (m) => console.log(m)
512
582
  };
513
583
  }
514
- function nextSteps(outcome, fileCount, name) {
515
- const keyless = outcome.template === "empty" ? "npm start # set OPENAI_API_KEY first" : "npm start # runs offline \u2014 no API key needed";
516
- const lines = outcome.dir === "." ? ["npm install", keyless] : [`cd ${outcome.dir}`, "npm install", keyless];
584
+ function nextSteps(outcome, fileCount, name, pm) {
585
+ const cmd = packageManagerCommands(pm);
586
+ const start = cmd.start.padEnd(16);
587
+ const keyless = outcome.template === "empty" ? `${start} # set OPENAI_API_KEY first` : `${start} # runs offline \u2014 no API key needed`;
588
+ const lines = outcome.dir === "." ? [cmd.install, keyless] : [`cd ${outcome.dir}`, cmd.install, keyless];
517
589
  return `Scaffolded ${name} (${fileCount} files).
518
590
 
519
591
  Next steps:
@@ -531,6 +603,7 @@ async function runFlow(args, opts = {}) {
531
603
  }
532
604
  }
533
605
  if (interactive) prompter.intro?.(opts.intro ?? `create-nola-lang v${await ownVersion()}`);
606
+ const pm = opts.packageManager ?? detectPackageManager();
534
607
  const input = {
535
608
  dir: args.dir,
536
609
  template: args.template,
@@ -557,7 +630,7 @@ async function runFlow(args, opts = {}) {
557
630
  return 0;
558
631
  }
559
632
  const lines = [
560
- "npm install",
633
+ packageManagerCommands(pm).install,
561
634
  'optional start script: "start": "nola run src/main.ts"',
562
635
  'tsconfig tip: directory-style include (e.g. ["src"]) lets the editor see .tsi files'
563
636
  ];
@@ -586,7 +659,8 @@ Next steps:
586
659
  const message = nextSteps(
587
660
  outcome,
588
661
  files.length + ideWrote.length + agWrote.length,
589
- basename2(resolve5(outcome.dir))
662
+ basename2(resolve5(outcome.dir)),
663
+ pm
590
664
  );
591
665
  if (prompter.outro) prompter.outro(message);
592
666
  else console.log(message);
package/dist/flow.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type AgentId } from "./agents.js";
2
+ import { type PackageManager } from "./package-manager.js";
2
3
  export interface PrompterOption {
3
4
  value: string;
4
5
  label: string;
@@ -58,6 +59,8 @@ export interface RunFlowOptions {
58
59
  interactive?: boolean;
59
60
  /** bare-run detection root; default "." — tests pass a tmp dir */
60
61
  cwd?: string;
62
+ /** the manager that invoked us; default: detected from npm_config_user_agent */
63
+ packageManager?: PackageManager;
61
64
  }
62
65
  /** The shared entry for both `npm create nola-lang` and `nola init`. */
63
66
  export declare function runFlow(args: RunFlowArgs, opts?: RunFlowOptions): Promise<number>;