@noir-ai/create 1.15.0-beta.1 → 1.16.0-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 +72 -20
- package/dist/index.js +238 -76
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/templates/rules-seed.md.tmpl +15 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HostId, HostAdapter } from '@noir-ai/adapters';
|
|
2
|
-
import { ManagedBlock } from '@noir-ai/core';
|
|
2
|
+
import { FileModeOutcome, ManagedBlock } from '@noir-ai/core';
|
|
3
|
+
export { ensureOwnerOnly } from '@noir-ai/core';
|
|
3
4
|
|
|
4
5
|
/** Absolute path of the ancestor store under `root`. */
|
|
5
6
|
declare function ancestorsPath(root: string): string;
|
|
@@ -70,7 +71,6 @@ declare function detectStack(root: string): StackInfo;
|
|
|
70
71
|
* file may be refreshed during an upgrade.
|
|
71
72
|
*
|
|
72
73
|
* WHY THIS EXISTS
|
|
73
|
-
* ---------------
|
|
74
74
|
* `noir init` writes two files whose only job is to be read by a human:
|
|
75
75
|
* `.noir/.env.example` (documents the variable set) and `.noir/rules/RULES.md`
|
|
76
76
|
* (the AI working-contract seed). Both are emitted create-only-if-absent, and
|
|
@@ -92,7 +92,6 @@ declare function detectStack(root: string): StackInfo;
|
|
|
92
92
|
* leftover and refreshing it loses nothing.
|
|
93
93
|
*
|
|
94
94
|
* THE CONTRACT
|
|
95
|
-
* ------------
|
|
96
95
|
* {@link isStaleSeed} answers exactly that question. It is deliberately
|
|
97
96
|
* conservative in one direction: it returns `true` only on an exact match
|
|
98
97
|
* against a recorded past version, so ANY other byte difference — a user edit,
|
|
@@ -109,7 +108,6 @@ declare function detectStack(root: string): StackInfo;
|
|
|
109
108
|
* file it just read; the argument makes it say so.
|
|
110
109
|
*
|
|
111
110
|
* RENDERING
|
|
112
|
-
* ---------
|
|
113
111
|
* Seed templates are normally interpolated through `render(template, vars)`
|
|
114
112
|
* before writing. That step is a no-op for the two templates recorded here:
|
|
115
113
|
* neither contains a `{{...}}` token, so `render(text, vars) === text` for
|
|
@@ -126,7 +124,6 @@ declare function detectStack(root: string): StackInfo;
|
|
|
126
124
|
* requirement impossible to overlook.
|
|
127
125
|
*
|
|
128
126
|
* APPENDING TO THE HISTORY
|
|
129
|
-
* ------------------------
|
|
130
127
|
* Record the template bytes as they shipped, keyed by the scaffold version
|
|
131
128
|
* whose `init` wrote them, and never edit an existing entry — a user's disk
|
|
132
129
|
* still holds those old bytes, and rewriting history would silently reclassify
|
|
@@ -276,8 +273,15 @@ declare function buildRegion(block: ManagedBlock, body: string): string;
|
|
|
276
273
|
* `writeFileSync`: a requested mode must land on the temp, and an interrupted
|
|
277
274
|
* seed write should not leave a half-written `.noir/.env` behind. Byte output
|
|
278
275
|
* is unchanged for every existing caller (same content, umask default 0o644
|
|
279
|
-
* when no mode is requested).
|
|
276
|
+
* when no mode is requested).
|
|
277
|
+
*
|
|
278
|
+
* An EXISTING file keeps this writer's create-only contract, but its mode is
|
|
279
|
+
* not left to drift: the orchestrator calls {@link ensureOwnerOnly} after the
|
|
280
|
+
* emit, which tightens a group/world-readable credential to 0600. */
|
|
280
281
|
declare function skipIfExists(absPath: string, content: string, fileMode?: number): WriteOutcome;
|
|
282
|
+
/** What happened when the owner-only mode was re-asserted on a credential
|
|
283
|
+
* file — see {@link ensureOwnerOnly}. */
|
|
284
|
+
type EnvMode = FileModeOutcome;
|
|
281
285
|
|
|
282
286
|
/**
|
|
283
287
|
* Declarative scaffold manifest — the single source of truth for what
|
|
@@ -348,11 +352,12 @@ interface ManifestEntry {
|
|
|
348
352
|
/** One-line human description for `noir doctor` + logs. */
|
|
349
353
|
description?: string;
|
|
350
354
|
/** Permission for a NEWLY created file (e.g. `0o600` for a credential seed).
|
|
351
|
-
* Applied only on creation
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
* targets are regenerated rather
|
|
355
|
+
* Applied only on creation — the `skipIfExists` contract means an existing
|
|
356
|
+
* file is not even opened. The orchestrator forwards this to the writer and
|
|
357
|
+
* reports it on the result as {@link ScaffoldResult.fileModes}; the mode of
|
|
358
|
+
* an existing credential file is re-asserted separately (`ensureOwnerOnly`).
|
|
359
|
+
* Ignored by every other write mode, whose targets are regenerated rather
|
|
360
|
+
* than seeded.
|
|
356
361
|
* NOTE: POSIX-only. Windows permissions are ACL-based and ignore it. */
|
|
357
362
|
fileMode?: number;
|
|
358
363
|
/** Required for `mergeJson` mode: the JSON patch object (or a template that
|
|
@@ -385,6 +390,14 @@ type BuildManifestContext = {
|
|
|
385
390
|
* .prettierignore only for JS; .dockerignore only when a Dockerfile is
|
|
386
391
|
* present; an unknown/empty stack ⇒ all four, for backward compat). */
|
|
387
392
|
stack?: StackInfo;
|
|
393
|
+
/** The `rules.enabled` switch from the project's `.noir/config.yml`. False
|
|
394
|
+
* drops the working-rules seed from the manifest, so no command emits or
|
|
395
|
+
* backfills `.noir/rules/RULES.md` for a project that opted out. Undefined
|
|
396
|
+
* (the schema's default state, and every caller that predates the switch)
|
|
397
|
+
* means enabled — a project with no `rules:` block emits the seed exactly
|
|
398
|
+
* as before. A file already on disk is never removed: dropping the entry
|
|
399
|
+
* removes the emission, not the file. */
|
|
400
|
+
rulesEnabled?: boolean;
|
|
388
401
|
};
|
|
389
402
|
/** Co-owned NOIR.md auto-brief region. Defined locally (not exported from
|
|
390
403
|
* core) because core's own named block instances cover only the three
|
|
@@ -433,6 +446,12 @@ interface BuildHostArtifactsContext {
|
|
|
433
446
|
* native install is detected, else `'noir'`). Passed through to
|
|
434
447
|
* `adapter.emitMcpConfig`. */
|
|
435
448
|
command: string;
|
|
449
|
+
/** The project's `rules.enabled` switch. False drops the rules `@`-import a
|
|
450
|
+
* host would otherwise emit (CLAUDE.md's rules block, GEMINI.md's rules
|
|
451
|
+
* block, AGENTS.md's rules import) so no host file points at a
|
|
452
|
+
* `.noir/rules/RULES.md` that the switch withheld. Undefined means enabled —
|
|
453
|
+
* byte-identical to every build that predates the switch. */
|
|
454
|
+
rulesEnabled?: boolean;
|
|
436
455
|
}
|
|
437
456
|
/**
|
|
438
457
|
* Materialize the host-specific manifest entries from a resolved adapter.
|
|
@@ -466,12 +485,22 @@ interface BuildHostArtifactsContext {
|
|
|
466
485
|
* collided with the cursor flat-skill prune of `noir-*.mdc` under
|
|
467
486
|
* `.cursor/rules/`, and cursor's rules are already delivered via
|
|
468
487
|
* AGENTS.md's `@.noir/rules/RULES.md` import.)
|
|
488
|
+
*
|
|
489
|
+
* That rules import/block is dropped for every host when
|
|
490
|
+
* `ctx.rulesEnabled` is false: the switch withholds the seed, so pointing
|
|
491
|
+
* a host at it would resolve to nothing. The context import is unaffected
|
|
492
|
+
* — the switch gates the rules, not the brief. A host file that already
|
|
493
|
+
* carries the rules region from when the switch was on keeps it: dropping
|
|
494
|
+
* the entry stops the emission, and nothing here rewrites a region out of
|
|
495
|
+
* an existing file.
|
|
469
496
|
* 3. **Host MCP config** — `regenerate` at `adapter.mcpConfigPath(ctx)`
|
|
470
497
|
* (default `<root>/.mcp.json` for claude), content from
|
|
471
498
|
* `adapter.emitMcpConfig(ctx, {transport,url})`. Claude KEEPS the template
|
|
472
499
|
* path (byte-identical parity with v1.1 + the .mcp.json parity test that
|
|
473
|
-
* compares against `claudeAdapter.emitMcpConfig`)
|
|
474
|
-
*
|
|
500
|
+
* compares against `claudeAdapter.emitMcpConfig`) for a repo that has not
|
|
501
|
+
* joined a workspace; a joined repo — one carrying a `.noir/workspace.json`
|
|
502
|
+
* marker — gets the workspace entry instead, on every host, whatever
|
|
503
|
+
* transport the run asked for (see the entry's own comment below).
|
|
475
504
|
*
|
|
476
505
|
* Skills are OUT OF SCOPE here — the cli composes `emitSkillsToDir` with
|
|
477
506
|
* `adapter.skillsDir` + the host's `CompileTarget` (claude → `.claude/skills/`
|
|
@@ -546,10 +575,11 @@ declare function mergeThreeWay(base: string, ours: string, theirs: string, style
|
|
|
546
575
|
* `result.conflicts` so the caller (doctor/CI) can fail loudly with a
|
|
547
576
|
* reviewable artifact rather than hang on a prompt.
|
|
548
577
|
*
|
|
549
|
-
* The registry ({@link MIGRATIONS}) carries
|
|
550
|
-
* `1.0.0 → 1.0.0` smoke test that exercises the runner end-to-end, and
|
|
551
|
-
* version-to-version migrations, `1.0.0 → 1.1.0
|
|
552
|
-
* synthetic one is the copy-pasteable template for the
|
|
578
|
+
* The registry ({@link MIGRATIONS}) carries four entries: the synthetic
|
|
579
|
+
* `1.0.0 → 1.0.0` smoke test that exercises the runner end-to-end, and three
|
|
580
|
+
* real version-to-version migrations, `1.0.0 → 1.1.0`, `1.1.0 → 1.2.0` and
|
|
581
|
+
* `1.2.0 → 1.3.0`. The synthetic one is the copy-pasteable template for the
|
|
582
|
+
* next contributor.
|
|
553
583
|
*/
|
|
554
584
|
interface MigrationContext {
|
|
555
585
|
/** Absolute repo root the migration operates on. */
|
|
@@ -558,6 +588,12 @@ interface MigrationContext {
|
|
|
558
588
|
* the same `changed`/`conflicts` shape, but does NOT touch disk. Used by
|
|
559
589
|
* `noir doctor`/CI to preview. */
|
|
560
590
|
dryRun?: boolean;
|
|
591
|
+
/** The project's `rules.enabled` switch, when the caller knows it. False
|
|
592
|
+
* tells a migration that `.noir/rules/RULES.md` is not part of this project
|
|
593
|
+
* — the scaffold withholds the seed — so a migration must not write that
|
|
594
|
+
* file back, however convincing its evidence for a refresh. Undefined means
|
|
595
|
+
* enabled, which is the state of every project that predates the switch. */
|
|
596
|
+
rulesEnabled?: boolean;
|
|
561
597
|
}
|
|
562
598
|
interface MigrationResult {
|
|
563
599
|
/** Repo-relative paths the migration modified (or would modify, in dryRun). */
|
|
@@ -598,6 +634,7 @@ interface MigrationScript {
|
|
|
598
634
|
*/
|
|
599
635
|
declare function runMigrations(root: string, from: string | null, to: string, opts?: {
|
|
600
636
|
dryRun?: boolean;
|
|
637
|
+
rulesEnabled?: boolean;
|
|
601
638
|
}): MigrationResult & {
|
|
602
639
|
from: string | null;
|
|
603
640
|
to: string;
|
|
@@ -753,6 +790,18 @@ interface ScaffoldResult {
|
|
|
753
790
|
* mode is never changed. Optional so existing external constructors of this
|
|
754
791
|
* type (test stubs) stay valid; `scaffold()` always populates it. */
|
|
755
792
|
fileModes?: Record<string, number>;
|
|
793
|
+
/** What the run did to the permissions of `.noir/.env` — the outcome of
|
|
794
|
+
* {@link ensureOwnerOnly}, which is called on every run that may write, in
|
|
795
|
+
* every mode, because the seeded file's 0600 mode is applied only when it is
|
|
796
|
+
* created.
|
|
797
|
+
* `'healed'` means a group/world-readable file was tightened to 0600, so a
|
|
798
|
+
* caller can report the fix instead of leaving the user to notice it in a
|
|
799
|
+
* later diagnostic; `'unchanged'` and `'unsupported'` mean nothing needed
|
|
800
|
+
* doing (or the platform has no POSIX mode bits) — the `'unchanged'` a fresh
|
|
801
|
+
* `create` reports, whose seed was written 0600 in the same run. Absent on a
|
|
802
|
+
* dry run, which must not touch disk. Optional so existing external
|
|
803
|
+
* constructors of this type stay valid. */
|
|
804
|
+
envMode?: EnvMode;
|
|
756
805
|
}
|
|
757
806
|
/** One entry in {@link ScaffoldResult.conflicts}. */
|
|
758
807
|
interface ConflictRecord {
|
|
@@ -831,8 +880,11 @@ declare function scaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
|
831
880
|
* `key=value` shape is trivial to grep from a shell.
|
|
832
881
|
*/
|
|
833
882
|
/** The scaffold version this build of @noir-ai/create ships. Bumped atomically
|
|
834
|
-
* whenever a manifest entry, template, or migration changes shape.
|
|
835
|
-
|
|
883
|
+
* whenever a manifest entry, template, or migration changes shape. 1.3.0 adds
|
|
884
|
+
* the migration that points a joined repo's MCP entry back at the workspace
|
|
885
|
+
* bridge — a repair only the upgrade path can perform, so projects stamped
|
|
886
|
+
* 1.2.0 and below need a window that starts at their version to reach it. */
|
|
887
|
+
declare const CURRENT_SCAFFOLD_VERSION = "1.3.0";
|
|
836
888
|
/** Path to the stamp file under `root`. Exposed for tests + doctor. */
|
|
837
889
|
declare function scaffoldVersionPath(root: string): string;
|
|
838
890
|
/** Read the applied scaffold version, or `null` if the stamp is absent/unparseable.
|
|
@@ -878,4 +930,4 @@ declare function loadTemplate(name: string): string;
|
|
|
878
930
|
/** Resolve a template name to its absolute path (for diagnostics + tests). */
|
|
879
931
|
declare function templatesDir(): string;
|
|
880
932
|
|
|
881
|
-
export { BRIEF_BLOCK, type BuildHostArtifactsContext, type BuildManifestContext, CURRENT_SCAFFOLD_VERSION, type ConflictContext, type ConflictRecord, type ConflictResolution, type ConflictResolverReturn, type DiffLine, type HostTag, MANIFEST_PATH_PARITY, MIGRATIONS, type ManifestEntry, type MergeResult, type MigrationContext, type MigrationResult, type MigrationScript, SEED_TEMPLATE_HISTORY, type ScaffoldMode, type ScaffoldOptions, type ScaffoldResult, type SeedTemplateHistoryEntry, type StackInfo, type WriteMode, type WriteOutcome, ancestorsPath, applyInlineConflict, applyWithConflict, assertSafeRoot, buildHostArtifacts, buildManifest, buildRegion, detectStack, isStaleSeed, lineDiff, loadTemplate, managedBlock, mergeThreeWay, readAncestors, readScaffoldVersion, regenerate, render, runMigrations, scaffold, scaffoldVersionPath, skipIfExists, templatesDir, writeAncestors, writeScaffoldVersion, zdiff3Region };
|
|
933
|
+
export { BRIEF_BLOCK, type BuildHostArtifactsContext, type BuildManifestContext, CURRENT_SCAFFOLD_VERSION, type ConflictContext, type ConflictRecord, type ConflictResolution, type ConflictResolverReturn, type DiffLine, type EnvMode, type HostTag, MANIFEST_PATH_PARITY, MIGRATIONS, type ManifestEntry, type MergeResult, type MigrationContext, type MigrationResult, type MigrationScript, SEED_TEMPLATE_HISTORY, type ScaffoldMode, type ScaffoldOptions, type ScaffoldResult, type SeedTemplateHistoryEntry, type StackInfo, type WriteMode, type WriteOutcome, ancestorsPath, applyInlineConflict, applyWithConflict, assertSafeRoot, buildHostArtifacts, buildManifest, buildRegion, detectStack, isStaleSeed, lineDiff, loadTemplate, managedBlock, mergeThreeWay, readAncestors, readScaffoldVersion, regenerate, render, runMigrations, scaffold, scaffoldVersionPath, skipIfExists, templatesDir, writeAncestors, writeScaffoldVersion, zdiff3Region };
|