@noir-ai/create 1.19.2-beta.1 → 1.19.3-beta.1

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/dist/index.d.ts CHANGED
@@ -9,13 +9,13 @@ declare function ancestorsPath(root: string): string;
9
9
  declare function readAncestors(root: string): Record<string, string>;
10
10
  /** Write the ancestor map. Ensures `.noir/` exists (a standalone caller may
11
11
  * invoke this before the scaffold has created the dir). Derived state, so the
12
- * plain write (no tmp) is fine. Content-hash dedup — when the serialized
12
+ * plain write (no tmp) is fine. Content-hash dedup: when the serialized
13
13
  * bytes equal what is already on disk, the rewrite is skipped so a no-op sync
14
14
  * leaves ancestors.json untouched (no mtime/git churn). */
15
15
  declare function writeAncestors(root: string, map: Record<string, string>): void;
16
16
 
17
17
  /**
18
- * Emit a host's lifecycle-hook config on demand — the write-once `mergeJson`
18
+ * Emit a host's lifecycle-hook config on demand: the write-once `mergeJson`
19
19
  * hook the scaffold deliberately does NOT re-emit on `noir init --upgrade` or
20
20
  * `noir sync`. This is the explicit restore path (`noir hooks sync`): it runs
21
21
  * the same `adapter.emitHooks` seam the scaffold uses and dispatches each entry
@@ -25,7 +25,7 @@ declare function writeAncestors(root: string, map: Record<string, string>): void
25
25
  * command instead of being left as-is; the user's other keys and non-Noir hooks
26
26
  * are never touched.
27
27
  *
28
- * Pure filesystem work — no daemon, no store, no prompt. A host with no hook
28
+ * Pure filesystem work: no daemon, no store, no prompt. A host with no hook
29
29
  * surface (agents-md, opencode) returns an empty outcome list.
30
30
  */
31
31
  /** One host hook-config write performed by {@link emitHooksToFile}. */
@@ -47,7 +47,7 @@ declare function emitHooksToFile(root: string, host: HostId, opts?: {
47
47
 
48
48
  /**
49
49
  * READ-ONLY stack detection. Probes for well-known marker files under `root`
50
- * and reports ONLY what is present — never assumes. The result feeds path
50
+ * and reports ONLY what is present: never assumes. The result feeds path
51
51
  * adaptation (where to drop `.claude/` vs `.cursor/` etc., future), ignore-file
52
52
  * selection, and the onboarding TUI's confirm step.
53
53
  *
@@ -85,7 +85,7 @@ interface StackInfo {
85
85
  ci: string | null;
86
86
  /**
87
87
  * Existing AI instruction files found under root.
88
- * Never clobbered — the scaffold confirms the write strategy (skip/add-section/
88
+ * Never clobbered: the scaffold confirms the write strategy (skip/add-section/
89
89
  * standalone).
90
90
  */
91
91
  existingAiFiles: {
@@ -233,19 +233,19 @@ declare const SEED_TEMPLATE_HISTORY: readonly SeedTemplateHistoryEntry[];
233
233
  declare function isStaleSeed(seed: SeedKind, fileBytes: string, currentRender: string): boolean;
234
234
 
235
235
  /**
236
- * The three-mode writer — generalizes the earlier `writeManagedRegion` into
236
+ * The three-mode writer: generalizes the earlier `writeManagedRegion` into
237
237
  * the declarative dispatch the scaffold manifest drives. Each mode maps 1:1 to
238
238
  * an artifact class in the artifact matrix:
239
239
  *
240
- * - {@link regenerate} — pure pointers (`.mcp.json`, `NOIR.md` brief, …).
240
+ * - {@link regenerate} : pure pointers (`.mcp.json`, `NOIR.md` brief, …).
241
241
  * Always overwritten, atomically.
242
- * - {@link managedBlock} — co-owned files (`CLAUDE.md` context/rules,
242
+ * - {@link managedBlock} : co-owned files (`CLAUDE.md` context/rules,
243
243
  * `.gitignore` noir block, …). DELEGATES to
244
244
  * core's `writeManagedRegion` so user content
245
245
  * outside the markers is preserved byte-for-byte and
246
246
  * re-runs are idempotent. Never duplicate the
247
247
  * managed-region logic.
248
- * - {@link skipIfExists} — user-owned seeds (`RULES.md`, `config.yml`,
248
+ * - {@link skipIfExists} : user-owned seeds (`RULES.md`, `config.yml`,
249
249
  * `project.id`). Write once; never clobber.
250
250
  *
251
251
  * The orchestrator (`scaffold.ts`) is the only intended caller; the per-mode
@@ -258,7 +258,7 @@ interface WriteOutcome {
258
258
  path: string;
259
259
  mode: WriteMode;
260
260
  /** true when bytes hit disk; false for skipIfExists no-ops. regenerate and
261
- * managedBlock always write (managedBlock may write identical bytes — that
261
+ * managedBlock always write (managedBlock may write identical bytes: that
262
262
  * still counts as a write for telemetry purposes; the file IS up to date). */
263
263
  written: boolean;
264
264
  }
@@ -267,7 +267,7 @@ interface WriteOutcome {
267
267
  * file (the pointer files this is used for are read by the host agent first —
268
268
  * a truncated CLAUDE.md/.mcp.json would break the very startup Noir serves).
269
269
  *
270
- * Parent directories are NOT created here — the orchestrator does that once
270
+ * Parent directories are NOT created here: the orchestrator does that once
271
271
  * for the whole manifest so a missing dir is a single, attributable failure
272
272
  * rather than N silent ones inside the writer.
273
273
  */
@@ -281,7 +281,7 @@ declare function managedBlock(absPath: string, block: ManagedBlock, regionText:
281
281
  /** Assemble `<begin>\n<body>\n<end>\n` for a managed block. Centralized here so
282
282
  * every caller (manifest rendering, tests, future migrations) produces the
283
283
  * exact byte shape `writeManagedRegion` strips/expects. The trailing newline
284
- * is part of the contract — `stripManagedBlock`'s regex eats a trailing `\n`
284
+ * is part of the contract: `stripManagedBlock`'s regex eats a trailing `\n`
285
285
  * so re-runs stay idempotent instead of accumulating blank lines.
286
286
  *
287
287
  * `body` is `trimEnd()`-ed before wrapping so template authors can keep the
@@ -298,7 +298,7 @@ declare function buildRegion(block: ManagedBlock, body: string): string;
298
298
  * BEFORE the rename (see `AtomicWriteOptions.mode`), so the file never exists
299
299
  * at its final path with a laxer mode. It is deliberately ignored when the
300
300
  * target already exists: this writer's whole contract is "never touch an
301
- * existing file", and that includes its permissions. POSIX-only — Windows
301
+ * existing file", and that includes its permissions. POSIX-only: Windows
302
302
  * permissions are ACL-based and the mode is dropped there.
303
303
  *
304
304
  * The write goes through `atomicWriteFile` (tmp + rename) rather than a bare
@@ -312,11 +312,11 @@ declare function buildRegion(block: ManagedBlock, body: string): string;
312
312
  * emit, which tightens a group/world-readable credential to 0600. */
313
313
  declare function skipIfExists(absPath: string, content: string, fileMode?: number): WriteOutcome;
314
314
  /** What happened when the owner-only mode was re-asserted on a credential
315
- * file — see {@link ensureOwnerOnly}. */
315
+ * file: see {@link ensureOwnerOnly}. */
316
316
  type EnvMode = FileModeOutcome;
317
317
 
318
318
  /**
319
- * Declarative scaffold manifest — the single source of truth for what
319
+ * Declarative scaffold manifest: the single source of truth for what
320
320
  * `init` / `create` / `sync` emit. Each entry is one artifact, tagged with its
321
321
  * write {@link WriteMode} so the orchestrator can dispatch without knowing
322
322
  * what's inside.
@@ -330,7 +330,7 @@ type EnvMode = FileModeOutcome;
330
330
  * per-host files
331
331
  * (CLAUDE.md/GEMINI.md for claude/gemini; AGENTS.md + .cursor/.../opencode.json
332
332
  * for agents-md/cursor/opencode) via the resolved adapter. The claude default
333
- * `noir init` stays BYTE-IDENTICAL to v1.1 — REMOVED the additive
333
+ * `noir init` stays BYTE-IDENTICAL to v1.1: REMOVED the additive
334
334
  * root `AGENTS.md` (it was double-importing `.noir/NOIR.md` + RULES.md via
335
335
  * CLAUDE.md's existing @-imports; claude's native surface is CLAUDE.md alone).
336
336
  *
@@ -348,7 +348,7 @@ interface ManifestEntry {
348
348
  /** Repo-relative POSIX path (forward slashes). Orchestrator joins with root. */
349
349
  path: string;
350
350
  mode: WriteMode;
351
- /** Opt-in flag for a DOC-ONLY seed — a file whose only job is to be read by a
351
+ /** Opt-in flag for a DOC-ONLY seed: a file whose only job is to be read by a
352
352
  * human. On an upgrade, a seed still byte-identical to what an older Noir
353
353
  * shipped is refreshed in place rather than staying frozen at that older
354
354
  * text forever; one the user edited is never refreshed (it goes to the
@@ -356,14 +356,14 @@ interface ManifestEntry {
356
356
  *
357
357
  * Honored on the upgrade emit only. A fresh `init`/`create` either creates
358
358
  * the file or leaves it alone, and `sync` does not emit seeds at all, so
359
- * neither can observe the flag — the behavior change is confined to the one
359
+ * neither can observe the flag: the behavior change is confined to the one
360
360
  * command a user runs to bring an initialized project up to date.
361
361
  *
362
362
  * DOC-ONLY SEEDS ONLY. A file the user owns the contents of must never carry
363
363
  * this flag: `.noir/.env`, `.noir/config.yml` and `.noir/project.id` are
364
364
  * theirs to shape, and an upgrade that rewrote one of them would destroy
365
365
  * exactly the work this engine exists to protect. Co-owned files (managed
366
- * blocks) must not set it either — they have a three-way merge that
366
+ * blocks) must not set it either: they have a three-way merge that
367
367
  * preserves user edits, which is the better tool for a shared file.
368
368
  *
369
369
  * Which recorded seed the bytes are compared against is declared separately
@@ -375,7 +375,7 @@ interface ManifestEntry {
375
375
  /** Required for `managedBlock` mode: the named block to re-emit. */
376
376
  block?: ManagedBlock;
377
377
  /** Literal content (`regenerate`/`skipIfExists`) or literal region BODY
378
- * (`managedBlock` — the orchestrator wraps it with the block markers).
378
+ * (`managedBlock`: the orchestrator wraps it with the block markers).
379
379
  * Mutually exclusive with {@link template}. */
380
380
  content?: string;
381
381
  /** Template name (resolved by `template-loader`) for content/body.
@@ -384,7 +384,7 @@ interface ManifestEntry {
384
384
  /** One-line human description for `noir doctor` + logs. */
385
385
  description?: string;
386
386
  /** Permission for a NEWLY created file (e.g. `0o600` for a credential seed).
387
- * Applied only on creation — the `skipIfExists` contract means an existing
387
+ * Applied only on creation: the `skipIfExists` contract means an existing
388
388
  * file is not even opened. The orchestrator forwards this to the writer and
389
389
  * reports it on the result as {@link ScaffoldResult.fileModes}; the mode of
390
390
  * an existing credential file is re-asserted separately (`ensureOwnerOnly`).
@@ -395,7 +395,7 @@ interface ManifestEntry {
395
395
  /** Required for `mergeJson` mode: the JSON patch object (or a template that
396
396
  * renders to one). Merged into the existing file, preserving user keys. */
397
397
  patch?: Record<string, unknown>;
398
- /** Required for `mergeJson` mode: dedup substring for `hooks.*` entries — an
398
+ /** Required for `mergeJson` mode: dedup substring for `hooks.*` entries: an
399
399
  * existing entry whose command contains this is NOT re-added. */
400
400
  dedupSubstring?: string;
401
401
  }
@@ -418,7 +418,7 @@ type BuildManifestContext = {
418
418
  * MCP clients that don't read shell profiles can spawn the server. See
419
419
  * `resolveNoirCommand()` in @noir-ai/core. */
420
420
  command: string;
421
- /** Detected stack — drives stack-aware ignore emission (.npmignore /
421
+ /** Detected stack: drives stack-aware ignore emission (.npmignore /
422
422
  * .prettierignore only for JS; .dockerignore only when a Dockerfile is
423
423
  * present; an unknown/empty stack ⇒ all four, for backward compat). */
424
424
  stack?: StackInfo;
@@ -429,7 +429,7 @@ type BuildManifestContext = {
429
429
  * drops the working-rules seed from the manifest, so no command emits or
430
430
  * backfills `.noir/rules/RULES.md` for a project that opted out. Undefined
431
431
  * (the schema's default state, and every caller that predates the switch)
432
- * means enabled — a project with no `rules:` block emits the seed exactly
432
+ * means enabled: a project with no `rules:` block emits the seed exactly
433
433
  * as before. A file already on disk is never removed: dropping the entry
434
434
  * removes the emission, not the file. */
435
435
  rulesEnabled?: boolean;
@@ -453,11 +453,11 @@ declare const MANIFEST_PATH_PARITY: ReadonlyArray<[
453
453
  * `resolveAdapter(ctx.host)`). The host-agnostic half is unchanged from v1.1
454
454
  * (canonical `.noir/` store + ignore files). {@link buildHostArtifacts}
455
455
  * emits AGENTS.md ONLY for agents-md/cursor/opencode (claude/gemini use their
456
- * own CLAUDE.md/GEMINI.md — emitting AGENTS.md too would double-import .noir/).
456
+ * own CLAUDE.md/GEMINI.md: emitting AGENTS.md too would double-import .noir/).
457
457
  *
458
458
  * Mode-tagging rationale per artifact:
459
459
  * - `project.id` → skipIfExists. First init writes a fresh id; re-init MUST
460
- * NOT overwrite — that would orphan the indexed store DB named after it.
460
+ * NOT overwrite: that would orphan the indexed store DB named after it.
461
461
  * - `config.yml` → skipIfExists. User-owned; the seed is written once.
462
462
  * (The seed renders `host: {{host}}` so a `--host gemini` init persists the
463
463
  * chosen host for `noir sync` to read back.)
@@ -470,7 +470,7 @@ declare const MANIFEST_PATH_PARITY: ReadonlyArray<[
470
470
  */
471
471
  declare function buildManifest(ctx: BuildManifestContext): ManifestEntry[];
472
472
  /** Context shape passed to {@link buildHostArtifacts}. A strict subset of
473
- * {@link BuildManifestContext} (no `projectId`/`host` — the adapter IS the
473
+ * {@link BuildManifestContext} (no `projectId`/`host`: the adapter IS the
474
474
  * resolved host, and host artifacts never need the project id). Exported
475
475
  * separately so callers + tests can name the narrower contract. */
476
476
  interface BuildHostArtifactsContext {
@@ -490,10 +490,10 @@ interface BuildHostArtifactsContext {
490
490
  }
491
491
  /**
492
492
  * Materialize the host-specific manifest entries from a resolved adapter.
493
- * SINGLE entry point — no scattered `if (host === '…')` conditionals in the
493
+ * SINGLE entry point: no scattered `if (host === '…')` conditionals in the
494
494
  * orchestrator. Returns entries in emission order:
495
495
  *
496
- * 1. **AGENTS.md** (universal baseline) — `regenerate` at
496
+ * 1. **AGENTS.md** (universal baseline): `regenerate` at
497
497
  * `adapter.agentsMdPath(ctx)` (default `<root>/AGENTS.md`), content from
498
498
  * the shared `emitAgentsMd(ctx)` helper. Emitted ONLY for hosts whose
499
499
  * `emitContext` IS the AGENTS.md content (agents-md, cursor, opencode) —
@@ -503,10 +503,10 @@ interface BuildHostArtifactsContext {
503
503
  * `@`-imports the canonical `.noir/` sources; emitting AGENTS.md too
504
504
  * would IMPORT THOSE FILES TWICE into the host's context (2× tokens +
505
505
  * drift risk), so for those two hosts AGENTS.md is SKIPPED. (Claude Code
506
- * still discovers AGENTS.md at the repo root when present — users who
506
+ * still discovers AGENTS.md at the repo root when present: users who
507
507
  * want the universal file can drop one in by hand; Noir's auto-emission
508
508
  * stays single-source per host.)
509
- * 2. **Host-native context file** — emitted ONLY for hosts whose `emitContext`
509
+ * 2. **Host-native context file**: emitted ONLY for hosts whose `emitContext`
510
510
  * is NOT the AGENTS.md content (i.e. the host has its OWN context file
511
511
  * with a distinct syntax). Concretely: claude → `CLAUDE.md` (CONTEXT +
512
512
  * RULES managed blocks, byte-identical to v1.1 via templates); gemini →
@@ -514,7 +514,7 @@ interface BuildHostArtifactsContext {
514
514
  * import syntax). For `agents-md`/`cursor`/`opencode` the context IS the
515
515
  * AGENTS.md (already emitted in step 1) → SKIP to avoid a duplicate.
516
516
  * Rules live INSIDE the host's context file: claude's in CLAUDE.md,
517
- * gemini's in GEMINI.md, agents-md/cursor/opencode's in AGENTS.md — NO
517
+ * gemini's in GEMINI.md, agents-md/cursor/opencode's in AGENTS.md: NO
518
518
  * host emits a separate rules file. (The prior cursor
519
519
  * `.cursor/rules/noir-contract.mdc` host-rules pointer was REMOVED: it
520
520
  * collided with the cursor flat-skill prune of `noir-*.mdc` under
@@ -524,20 +524,20 @@ interface BuildHostArtifactsContext {
524
524
  * That rules import/block is dropped for every host when
525
525
  * `ctx.rulesEnabled` is false: the switch withholds the seed, so pointing
526
526
  * a host at it would resolve to nothing. The context import is unaffected
527
- * — the switch gates the rules, not the brief. A host file that already
527
+ * : the switch gates the rules, not the brief. A host file that already
528
528
  * carries the rules region from when the switch was on keeps it: dropping
529
529
  * the entry stops the emission, and nothing here rewrites a region out of
530
530
  * an existing file.
531
- * 3. **Host MCP config** — `regenerate` at `adapter.mcpConfigPath(ctx)`
531
+ * 3. **Host MCP config**: `regenerate` at `adapter.mcpConfigPath(ctx)`
532
532
  * (default `<root>/.mcp.json` for claude), content from
533
533
  * `adapter.emitMcpConfig(ctx, {transport,url})`. Claude KEEPS the template
534
534
  * path (byte-identical parity with v1.1 + the .mcp.json parity test that
535
535
  * compares against `claudeAdapter.emitMcpConfig`) for a repo that has not
536
- * joined a workspace; a joined repo — one carrying a `.noir/workspace.json`
537
- * marker — gets the workspace entry instead, on every host, whatever
536
+ * joined a workspace; a joined repo: one carrying a `.noir/workspace.json`
537
+ * marker: gets the workspace entry instead, on every host, whatever
538
538
  * transport the run asked for (see the entry's own comment below).
539
539
  *
540
- * Skills are OUT OF SCOPE here — the cli composes `emitSkillsToDir` with
540
+ * Skills are OUT OF SCOPE here: the cli composes `emitSkillsToDir` with
541
541
  * `adapter.skillsDir` + the host's `CompileTarget` (claude → `.claude/skills/`
542
542
  * as SKILL.md; cursor → `.cursor/rules/<skill>.mdc` FLAT; gemini/
543
543
  * agents-md/opencode have no skill dir → skip).
@@ -558,7 +558,7 @@ interface MergeResult {
558
558
  * `-` red, context dim). Reuses the SAME LCS machinery {@link mergeThreeWay}
559
559
  * does so there is exactly one line-diff algorithm in the package.
560
560
  *
561
- * `context` (default 3) — number of unchanged lines to keep around each hunk
561
+ * `context` (default 3): number of unchanged lines to keep around each hunk
562
562
  * (git-style). 0 → every non-equal line stands alone; large → whole-file blob.
563
563
  * The resolver uses the default; tests pin to a specific shape.
564
564
  */
@@ -577,7 +577,7 @@ declare function lineDiff(base: string, head: string, context?: number): DiffLin
577
577
  * <theirs lines>
578
578
  * `>>>>>>> theirs`
579
579
  *
580
- * `diff3(zdiff3)` writes the SAME shape — included here so the engine + clack
580
+ * `diff3(zdiff3)` writes the SAME shape: included here so the engine + clack
581
581
  * resolver have a single source of truth for marker layout. Returns the marked
582
582
  * region body (no outer delimiters); the caller wraps it with surrounding
583
583
  * context.
@@ -588,22 +588,22 @@ declare function zdiff3Region(ours: readonly string[], base: readonly string[],
588
588
  * new template) with `base` (the last-emitted ancestor). Line-level diff3:
589
589
  * changes in disjoint line ranges merge; overlapping changes become inline
590
590
  * conflict markers. Default marker shape is the minimal diff3 form
591
- * (`<<<<<<< ours` / `=======` / `>>>>>>> theirs`) — byte-identical to v1.2 so
591
+ * (`<<<<<<< ours` / `=======` / `>>>>>>> theirs`): byte-identical to v1.2 so
592
592
  * {@link mergeManagedRegion} stays regression-anchored. Pass `'zdiff3'` to
593
- * include the base section (`||||||| base`) — used by the conflict resolver's
593
+ * include the base section (`||||||| base`): used by the conflict resolver's
594
594
  * 6th "merge (with conflict markers)" option. Pure + deterministic
595
595
  * (no IO) so it's unit-testable.
596
596
  */
597
597
  declare function mergeThreeWay(base: string, ours: string, theirs: string, style?: 'minimal' | 'zdiff3'): MergeResult;
598
598
 
599
599
  /**
600
- * Migration model — Nx-style declarative upgrade scripts, hand-rolled.
600
+ * Migration model: Nx-style declarative upgrade scripts, hand-rolled.
601
601
  *
602
602
  * Per the migration design (inline conflict markers, never
603
603
  * interactive prompts), every migration script:
604
604
  * - is a small, IDEMPOTENT TypeScript function (`run(root, opts) → result`).
605
605
  * Re-running it on an already-migrated tree is a no-op (or produces the
606
- * same conflict markers again — also safe).
606
+ * same conflict markers again: also safe).
607
607
  * - NEVER prompts. `--no-input` CI must survive. When a migration cannot
608
608
  * reconcile a co-edited region, it writes git-style conflict markers
609
609
  * (`<<<<<<<` / `=======` / `>>>>>>>`) into the file and records the path in
@@ -625,7 +625,7 @@ interface MigrationContext {
625
625
  dryRun?: boolean;
626
626
  /** The project's `rules.enabled` switch, when the caller knows it. False
627
627
  * tells a migration that `.noir/rules/RULES.md` is not part of this project
628
- * — the scaffold withholds the seed — so a migration must not write that
628
+ * : the scaffold withholds the seed: so a migration must not write that
629
629
  * file back, however convincing its evidence for a refresh. Undefined means
630
630
  * enabled, which is the state of every project that predates the switch. */
631
631
  rulesEnabled?: boolean;
@@ -659,7 +659,7 @@ interface MigrationScript {
659
659
  * skipped. Within the window, scripts run sorted by `to` ascending so a
660
660
  * multi-step upgrade (1.0.0 → 1.1.0 → 1.2.0) composes in order.
661
661
  *
662
- * The runner NEVER throws on a per-script failure — it captures the error,
662
+ * The runner NEVER throws on a per-script failure: it captures the error,
663
663
  * records a synthetic conflict entry (`<path>__error`), and continues so a
664
664
  * single broken migration doesn't block the rest of the chain. The orchestrator
665
665
  * decides whether non-empty `conflicts` is a hard failure (init --upgrade) or a
@@ -677,7 +677,7 @@ declare function runMigrations(root: string, from: string | null, to: string, op
677
677
  };
678
678
 
679
679
  /** The registry. The runner sorts the selected window by `to`, so declaration
680
- * order is documentation only — oldest step first. */
680
+ * order is documentation only: oldest step first. */
681
681
  declare const MIGRATIONS: readonly MigrationScript[];
682
682
  /** Write git-style inline conflict markers around `theirs`/`ours` so a human
683
683
  * or AI agent can resolve later. This is the CI-safe fallback —
@@ -686,7 +686,7 @@ declare function applyInlineConflict(ours: string, theirs: string, oursLabel?: s
686
686
  /** Apply `(ours, theirs)` to a region: if they're equal, return `ours` (no
687
687
  * conflict); otherwise emit inline markers. Migration authors should prefer
688
688
  * this over {@link applyInlineConflict} when the "no change needed" case is
689
- * common — it keeps re-runs truly idempotent (no spurious markers on a clean
689
+ * common: it keeps re-runs truly idempotent (no spurious markers on a clean
690
690
  * tree). */
691
691
  declare function applyWithConflict(ours: string, theirs: string, path: string): {
692
692
  text: string;
@@ -735,7 +735,7 @@ interface ScaffoldOptions {
735
735
  upgrade?: boolean;
736
736
  /** Re-scaffold even when the target is already initialized (bypasses
737
737
  * the already-initialized no-op guard). Does NOT bypass `assertSafeRoot`
738
- * — root-safety is hard, never bypassable. */
738
+ * : root-safety is hard, never bypassable. */
739
739
  force?: boolean;
740
740
  /** Preview: compute the same written/skipped/migrated lists without touching
741
741
  * disk. `noir doctor`/CI use this to report drift. */
@@ -748,10 +748,10 @@ interface ScaffoldOptions {
748
748
  * `'overwrite'` is byte-backward-compatible. `'preserve'` keeps the user's
749
749
  * file (the non-TTY / CI default in the cli). */
750
750
  conflictPolicy?: 'overwrite' | 'preserve';
751
- /** Per-file conflict resolver — the UI seam (the engine stays UI-free;
751
+ /** Per-file conflict resolver: the UI seam (the engine stays UI-free;
752
752
  * the cli injects a @clack-based resolver). Called when a `regenerate`
753
753
  * file exists and differs from the template. The return type widens to
754
- * accept a rich shape carrying `applyToAll` — the engine then reuses the
754
+ * accept a rich shape carrying `applyToAll`: the engine then reuses the
755
755
  * decision for the rest of the run (per artifact CLASS). */
756
756
  onConflict?: (ctx: ConflictContext) => Promise<ConflictResolverReturn> | ConflictResolverReturn;
757
757
  /** Three-way merge managed regions (base/ours/theirs) using a persisted
@@ -760,7 +760,7 @@ interface ScaffoldOptions {
760
760
  * DEFAULT TRUE: ancestor capture is unconditional, so the first merge
761
761
  * run always has a base. Set to `false` (CLI `--no-merge-regions`) to
762
762
  * restore strip-replace. Supports BOTH single-region (NOIR.md, ignores) AND
763
- * multi-region (CLAUDE.md CONTEXT+RULES) managed files — the multi-region
763
+ * multi-region (CLAUDE.md CONTEXT+RULES) managed files: the multi-region
764
764
  * path merges managed regions per block (`managedBlocks` +
765
765
  * `mergeManagedRegion`). */
766
766
  mergeManagedRegions?: boolean;
@@ -777,7 +777,7 @@ interface ScaffoldResult {
777
777
  /** Repo-relative paths skipIfExists'd (already present). */
778
778
  skipped: string[];
779
779
  /** Repo-relative `regenerate` paths skipped because byte-identical to
780
- * the template (content-hash dedup — no rewrite). */
780
+ * the template (content-hash dedup: no rewrite). */
781
781
  identical: string[];
782
782
  /** Repo-relative doc-only seed paths REPLACED on the upgrade path because the
783
783
  * copy on disk was still byte-identical to an older Noir's shipped text —
@@ -787,7 +787,7 @@ interface ScaffoldResult {
787
787
  * "Noir updated documentation you never touched" apart from "Noir created
788
788
  * something new".
789
789
  *
790
- * A seed the user DID edit is never listed here — it goes through the
790
+ * A seed the user DID edit is never listed here: it goes through the
791
791
  * ordinary conflict flow, so it appears in {@link written} only when a
792
792
  * resolver explicitly chose to replace it (or `--force` did). Empty on every
793
793
  * other run: a fresh `init`/`create` has nothing to compare against and
@@ -795,7 +795,7 @@ interface ScaffoldResult {
795
795
  refreshed: string[];
796
796
  /** True when the already-initialized guard short-circuited (a bare
797
797
  * `noir init`/`create` on an initialized project). Callers (init.ts/create.ts)
798
- * gate skills emission + the "initialized" message on this — a no-op must NOT
798
+ * gate skills emission + the "initialized" message on this: a no-op must NOT
799
799
  * re-emit skills or claim it initialized. */
800
800
  noop: boolean;
801
801
  /** Migration steps executed (`<from>→<to>`), when upgrade ran. */
@@ -803,7 +803,7 @@ interface ScaffoldResult {
803
803
  /** Migration conflicts (repo-relative or `<runner>:…`), when upgrade ran. */
804
804
  migrationConflicts: string[];
805
805
  /** Repo-relative paths a migration CHANGED (wrote or refreshed), when upgrade
806
- * ran — reported separately from {@link refreshed} so callers can name
806
+ * ran: reported separately from {@link refreshed} so callers can name
807
807
  * "migrated" files (e.g. `.noir/config.yml`) apart from doc seeds. */
808
808
  migrationChanged: string[];
809
809
  stack: StackInfo;
@@ -812,7 +812,7 @@ interface ScaffoldResult {
812
812
  toVersion: string;
813
813
  /** The host actually emitted (post-default). */
814
814
  host: HostTag;
815
- /** Structured conflict report — one record per file that existed AND
815
+ /** Structured conflict report: one record per file that existed AND
816
816
  * differed from the proposed bytes (regenerate conflict path). Populated in
817
817
  * `--json`/`--no-input`/non-TTY runs (no prompt fires) so a CI/JSON caller
818
818
  * can see exactly which files diverged + how they were resolved, without
@@ -823,19 +823,19 @@ interface ScaffoldResult {
823
823
  * (repo-relative path → mode), i.e. the {@link ManifestEntry.fileMode} of a
824
824
  * `skipIfExists` entry that had no file on disk. A real run reports the mode
825
825
  * it applied; a `dryRun` reports the mode it WOULD apply (the predictor's
826
- * job — the caller can preview `.noir/.env` at 0600 without writing it).
826
+ * job: the caller can preview `.noir/.env` at 0600 without writing it).
827
827
  * Paths the entry skipped (already present) are absent: an existing file's
828
828
  * mode is never changed. Optional so existing external constructors of this
829
829
  * type (test stubs) stay valid; `scaffold()` always populates it. */
830
830
  fileModes?: Record<string, number>;
831
- /** What the run did to the permissions of `.noir/.env` — the outcome of
831
+ /** What the run did to the permissions of `.noir/.env`: the outcome of
832
832
  * {@link ensureOwnerOnly}, which is called on every run that may write, in
833
833
  * every mode, because the seeded file's 0600 mode is applied only when it is
834
834
  * created.
835
835
  * `'healed'` means a group/world-readable file was tightened to 0600, so a
836
836
  * caller can report the fix instead of leaving the user to notice it in a
837
837
  * later diagnostic; `'unchanged'` and `'unsupported'` mean nothing needed
838
- * doing (or the platform has no POSIX mode bits) — the `'unchanged'` a fresh
838
+ * doing (or the platform has no POSIX mode bits): the `'unchanged'` a fresh
839
839
  * `create` reports, whose seed was written 0600 in the same run. Absent on a
840
840
  * dry run, which must not touch disk. Optional so existing external
841
841
  * constructors of this type stay valid. */
@@ -845,7 +845,7 @@ interface ScaffoldResult {
845
845
  interface ConflictRecord {
846
846
  /** Repo-relative path of the conflicting file. */
847
847
  path: string;
848
- /** Artifact class — drives apply-to-all memory scope + report grouping. */
848
+ /** Artifact class: drives apply-to-all memory scope + report grouping. */
849
849
  mode: ConflictContext['mode'];
850
850
  /** LCS similarity (0-1) between existing and proposed. */
851
851
  similarity?: number;
@@ -865,7 +865,7 @@ interface ConflictContext {
865
865
  existing: string;
866
866
  /** The content the scaffold would write. */
867
867
  proposed: string;
868
- /** Artifact class — drives apply-to-all memory scope (`regenerate` shares
868
+ /** Artifact class: drives apply-to-all memory scope (`regenerate` shares
869
869
  * one decision across a run; `managedBlock`/`managedBlocks` stay per-file).
870
870
  * Defaults to `'regenerate'` for backward compatibility with older resolvers
871
871
  * that pre-date the field (the engine always sets it). */
@@ -883,32 +883,32 @@ type ConflictResolution = 'replace' | 'preserve' | 'rename' | 'duplicate' | 'can
883
883
  * `'one'`) OR a rich shape carrying `applyToAll`. The engine unwraps both; when
884
884
  * `applyToAll` is true it stores the choice in its per-run memory keyed by
885
885
  * artifact CLASS (`regenerate` shares one decision across a run; managedBlock
886
- * stays per-file regardless — user edits there need individual review). */
886
+ * stays per-file regardless: user edits there need individual review). */
887
887
  type ConflictResolverReturn = ConflictResolution | {
888
888
  resolution: ConflictResolution;
889
889
  applyToAll?: boolean;
890
890
  };
891
891
  /**
892
- * Refuse to scaffold when `root` is — or is inside — a `.noir/` directory.
892
+ * Refuse to scaffold when `root` is: or is inside: a `.noir/` directory.
893
893
  * Running `noir init`/`create`/`sync` while cwd = `.noir/` would
894
894
  * otherwise mint a FRESH project id (because `<root>/.noir/project.id` is
895
895
  * absent) and build a NESTED second project (`.noir/.noir/`, `.noir/CLAUDE.md`,
896
- * `.noir/.claude/skills/`, …) — the duplicate-`.noir` bug. The walk ascends the
896
+ * `.noir/.claude/skills/`, …): the duplicate-`.noir` bug. The walk ascends the
897
897
  * ancestor chain only, so a legitimate project root that merely CONTAINS a
898
898
  * `.noir/` store is never flagged. String-based: works whether or not `root`
899
899
  * exists on disk yet (so `noir create <new-dir>` is still guarded).
900
900
  *
901
- * Hard against literal `.noir` path segments — NOT bypassable by `--force`
901
+ * Hard against literal `.noir` path segments: NOT bypassable by `--force`
902
902
  * (which is reserved for the already-initialized no-op). NOTE: the walk is
903
903
  * string-based, so a symlink whose TARGET is inside `.noir/` is not resolved
904
- * (a local self-foot-gun only — the caller controls `root` — not externally
904
+ * (a local self-foot-gun only: the caller controls `root`: not externally
905
905
  * exploitable); in practice `--cwd`/positional roots are literal paths.
906
906
  */
907
907
  declare function assertSafeRoot(root: string): void;
908
908
  declare function scaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
909
909
 
910
910
  /**
911
- * Scaffold version stamp — Noir's equivalent of Copier's `last-applied`.
911
+ * Scaffold version stamp: Noir's equivalent of Copier's `last-applied`.
912
912
  * Stored at `.noir/scaffold-version` as a single `noir-scaffold=<semver>` line
913
913
  * so `noir init --upgrade` / `noir doctor` can diff against
914
914
  * {@link CURRENT_SCAFFOLD_VERSION} and decide which migrations to run.
@@ -928,7 +928,7 @@ declare const CURRENT_SCAFFOLD_VERSION = "1.5.0";
928
928
  /** Path to the stamp file under `root`. Exposed for tests + doctor. */
929
929
  declare function scaffoldVersionPath(root: string): string;
930
930
  /** Read the applied scaffold version, or `null` if the stamp is absent/unparseable.
931
- * Never throws — doctor must keep reporting even on a malformed stamp. */
931
+ * Never throws: doctor must keep reporting even on a malformed stamp. */
932
932
  declare function readScaffoldVersion(root: string): string | null;
933
933
  /** Write the stamp, creating `.noir/` if needed. The orchestrator writes it
934
934
  * LAST so a crash mid-scaffold leaves an old/absent stamp rather than a
@@ -946,10 +946,10 @@ declare function writeScaffoldVersion(root: string, version: string): void;
946
946
  * map lookup is sufficient and keeps `@noir-ai/create` dependency-light.
947
947
  *
948
948
  * Semantics (documented):
949
- * - Tokens are `{{ name }}` / `{{name}}` — any amount of ASCII whitespace
949
+ * - Tokens are `{{ name }}` / `{{name}}`: any amount of ASCII whitespace
950
950
  * inside the braces is permitted. The name is trimmed.
951
951
  * - Known vars (value is a string) are substituted verbatim. NO escaping is
952
- * applied — callers must pre-escape for the target format (JSON/YAML/MD).
952
+ * applied: callers must pre-escape for the target format (JSON/YAML/MD).
953
953
  * This is intentional: the engine renders into multiple formats and a
954
954
  * single universal escaper would be wrong for at least one of them.
955
955
  * - Unknown vars (not in `vars`, or value `undefined`) → the original token
@@ -964,7 +964,7 @@ declare function writeScaffoldVersion(root: string, version: string): void;
964
964
  declare function render(template: string, vars: Record<string, unknown>): string;
965
965
 
966
966
  /** Read a template's raw text by name (e.g. `noir.md.tmpl`). Throws on missing
967
- * — an unknown template is a manifest bug and should fail loudly, not render
967
+ * : an unknown template is a manifest bug and should fail loudly, not render
968
968
  * an empty string silently. */
969
969
  declare function loadTemplate(name: string): string;
970
970
  /** Resolve a template name to its absolute path (for diagnostics + tests). */