@noir-ai/cli 1.3.0-beta.1 → 1.3.0-beta.3

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/bin.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-J5HAAQI4.js";
6
6
  import {
7
7
  init
8
- } from "./chunk-E5XVH7QR.js";
8
+ } from "./chunk-J2Y3BOVK.js";
9
9
  import {
10
10
  EXIT,
11
11
  NoirCliError,
@@ -1330,7 +1330,7 @@ function createProgram() {
1330
1330
  const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
1331
1331
  const force = opts.force === true;
1332
1332
  const host = parseHost(opts.host);
1333
- const { create } = await import("./create-2P3T7UGZ.js");
1333
+ const { create } = await import("./create-VK7JX6IB.js");
1334
1334
  await create(dir, {
1335
1335
  transport,
1336
1336
  url: opts.url,
@@ -10,7 +10,7 @@ import { emitSkillsToDir } from "@noir-ai/skills";
10
10
  async function init(root, opts) {
11
11
  assertTransportUrl(opts);
12
12
  const host = opts.host ?? "claude";
13
- await scaffold({
13
+ const res = await scaffold({
14
14
  root,
15
15
  mode: "init",
16
16
  host,
@@ -20,6 +20,7 @@ async function init(root, opts) {
20
20
  ...opts.force === true ? { force: true } : {},
21
21
  ...buildConflictOpts({ force: opts.force })
22
22
  });
23
+ if (res.noop) return;
23
24
  await emitHostSkills(root, host);
24
25
  process.stderr.write(
25
26
  `Noir initialized in ${root} (host: ${host}, transport: ${opts.transport}).
@@ -72,4 +73,4 @@ export {
72
73
  init,
73
74
  assertTransportUrl
74
75
  };
75
- //# sourceMappingURL=chunk-E5XVH7QR.js.map
76
+ //# sourceMappingURL=chunk-J2Y3BOVK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/init.ts"],"sourcesContent":["// `noir init` — first-run + `--upgrade`.\n//\n// Slice S-T2 refactor: the ad-hoc writers (syncIgnores, writeManagedRegion,\n// writeFileSync for .mcp.json/project.id/config.yml/NOIR.md/RULES.md) are\n// replaced by a single call into `@noir-ai/create`'s `scaffold({mode:'init'})`.\n// The manifest + three-mode writer are the source of truth for what init\n// emits; this module is now a thin caller that owns ONLY:\n// - the transport/url precondition (the localhost security gate, whose\n// error strings are locked by url-validation.test.ts), and\n// - skills emission (out-of-manifest by design — composed after scaffold()).\n//\n// S10 multi-host: the 8 direct `claudeAdapter` imports across init/sync/create\n// collapsed to `resolveAdapter(host)` where `host` comes from `--host <id>`\n// (default `'claude'`). The adapter drives (a) the manifest via\n// `scaffold({host})` and (b) skills emission — claude/cursor have a skill dir;\n// gemini/agents-md/opencode have no skill concept and the call is skipped.\n//\n// Deliberate behavior changes vs the predecessor (spec-aligned latent-bug\n// fixes; see S-T1 contract notes + CHANGELOG):\n// - `.noir/project.id` → skipIfExists (predecessor overwrote on every init,\n// orphaning the store DB named after the id). Re-init now preserves it.\n// - `.noir/config.yml` → skipIfExists (predecessor overwrote).\n// - `.noir/NOIR.md` → managedBlock with BRIEF_BLOCK markers (predecessor\n// wrote the whole file with no markers). First-run output GAINS markers;\n// user notes outside the markers survive re-runs.\n// - `.noir/scaffold-version` is now stamped on init/create (engine-owned).\n\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\n\nexport interface InitOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** `noir init --upgrade`: run scaffold migrations from the on-disk\n * scaffold-version to current, then re-emit ONLY the runtime subset\n * (regenerate + managedBlock). skipIfExists seeds are left alone so user\n * edits survive. */\n upgrade?: boolean;\n /** S10 target host. Defaults to `'claude'` (the regression anchor). Drives\n * both scaffold emission (the manifest's host-specific half) and skills\n * emission (skipped for hosts with no `skillsDir`). */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized (bypasses the\n * already-initialized no-op guard in scaffold()). */\n force?: boolean;\n}\n\nexport async function init(root: string, opts: InitOptions): Promise<void> {\n assertTransportUrl(opts);\n\n const host: HostId = opts.host ?? 'claude';\n\n const res = await scaffold({\n root,\n mode: 'init',\n host,\n transport: opts.transport,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.upgrade === true ? { upgrade: true } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...buildConflictOpts({ force: opts.force }),\n });\n // SP-A: if the already-initialized guard no-op'd scaffold, stop — don't re-emit\n // skills or print \"initialized\" (scaffold already printed the no-op message).\n if (res.noop) return;\n\n await emitHostSkills(root, host);\n\n process.stderr.write(\n `Noir initialized in ${root} (host: ${host}, transport: ${opts.transport}).\\n`,\n );\n process.stderr.write(\n 'Next: run `noir` to open the home menu (or `noir status` for a snapshot).\\n',\n );\n}\n\n/**\n * Compose skill emission onto the resolved adapter's `skillsDir` (claude →\n * `.claude/skills/`; cursor → `.cursor/rules/` compiled as `.mdc`; the other\n * three hosts have no skill concept and are skipped with a stderr note).\n *\n * The `CompileTarget` matches the host id (S10 foundation widened the enum to\n * the same union) so cursor skills compile to the `.mdc` rule shape via\n * `compileSkill(_, 'cursor')`; the others keep the verbatim SKILL.md format.\n */\nasync function emitHostSkills(root: string, host: HostId): Promise<void> {\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n return;\n }\n const target: CompileTarget = host;\n const summary = await emitSkillsToDir(skillsDir, { includeIntegrations: true, target });\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n}\n\n/**\n * Transport + URL precondition. The error strings here are the SECURITY GATE\n * locked by `url-validation.test.ts` — do not rephrase. Thrown errors are\n * plain `Error`s (bin.ts's `main().catch` maps them to stderr + exitCode 1).\n *\n * Exported so `noir create` (commands/create.ts) reuses the SAME gate instead\n * of duplicating the localhost allowlist.\n */\nexport function assertTransportUrl(opts: {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n}): void {\n if (opts.transport === 'streamable-http' && opts.url === undefined) {\n throw new Error('--transport streamable-http requires --url');\n }\n if (opts.url !== undefined) {\n assertLocalhostUrl(opts.url);\n }\n}\n\n// Validate a streamable-http --url is http(s) and localhost-only. Gate 2's\n// daemon binds 127.0.0.1, so persisting a non-localhost URL is a footgun.\n// Not exported: the single external surface is assertTransportUrl(); thrown\n// errors propagate to bin.ts's main().catch (stderr + exitCode 1).\nfunction assertLocalhostUrl(raw: string): void {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(`Invalid URL: ${raw}`);\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new Error('Only http/https URLs are supported');\n }\n if (!['localhost', '127.0.0.1', '::1'].includes(url.hostname)) {\n throw new Error(`Only localhost URLs are supported (got ${url.hostname})`);\n }\n}\n"],"mappings":";;;;;;AA2BA,SAAsB,sBAAsB;AAC5C,SAAS,gBAAgB;AACzB,SAA6B,uBAAuB;AAoBpD,eAAsB,KAAK,MAAc,MAAkC;AACzE,qBAAmB,IAAI;AAEvB,QAAM,OAAe,KAAK,QAAQ;AAElC,QAAM,MAAM,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IAClD,GAAI,KAAK,YAAY,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IACjD,GAAI,KAAK,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAG,kBAAkB,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,EAC5C,CAAC;AAGD,MAAI,IAAI,KAAM;AAEd,QAAM,eAAe,MAAM,IAAI;AAE/B,UAAQ,OAAO;AAAA,IACb,uBAAuB,IAAI,WAAW,IAAI,gBAAgB,KAAK,SAAS;AAAA;AAAA,EAC1E;AACA,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACF;AAWA,eAAe,eAAe,MAAc,MAA6B;AACvE,QAAM,UAAU,eAAe,IAAI;AACnC,QAAM,YAAY,QAAQ,YAAY,EAAE,KAAK,CAAC;AAC9C,MAAI,cAAc,QAAW;AAG3B,YAAQ,OAAO,MAAM,SAAS,IAAI;AAAA,CAA2C;AAC7E;AAAA,EACF;AACA,QAAM,SAAwB;AAC9B,QAAM,UAAU,MAAM,gBAAgB,WAAW,EAAE,qBAAqB,MAAM,OAAO,CAAC;AACtF,QAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,UAAQ,OAAO;AAAA,IACb,WAAW,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,EAChF;AACF;AAUO,SAAS,mBAAmB,MAG1B;AACP,MAAI,KAAK,cAAc,qBAAqB,KAAK,QAAQ,QAAW;AAClE,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,MAAI,KAAK,QAAQ,QAAW;AAC1B,uBAAmB,KAAK,GAAG;AAAA,EAC7B;AACF;AAMA,SAAS,mBAAmB,KAAmB;AAC7C,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,GAAG;AAAA,EACnB,QAAQ;AACN,UAAM,IAAI,MAAM,gBAAgB,GAAG,EAAE;AAAA,EACvC;AACA,MAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,IAAI,QAAQ,GAAG;AAC/C,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,MAAI,CAAC,CAAC,aAAa,aAAa,KAAK,EAAE,SAAS,IAAI,QAAQ,GAAG;AAC7D,UAAM,IAAI,MAAM,0CAA0C,IAAI,QAAQ,GAAG;AAAA,EAC3E;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  assertTransportUrl
4
- } from "./chunk-E5XVH7QR.js";
4
+ } from "./chunk-J2Y3BOVK.js";
5
5
  import {
6
6
  buildConflictOpts
7
7
  } from "./chunk-OF4OO5W6.js";
@@ -15,7 +15,7 @@ async function create(dir, opts) {
15
15
  assertTransportUrl(opts);
16
16
  const root = resolve(dir ?? process.cwd());
17
17
  const host = opts.host ?? "claude";
18
- await scaffold({
18
+ const res = await scaffold({
19
19
  root,
20
20
  mode: "create",
21
21
  host,
@@ -24,6 +24,7 @@ async function create(dir, opts) {
24
24
  ...opts.force === true ? { force: true } : {},
25
25
  ...buildConflictOpts({ force: opts.force })
26
26
  });
27
+ if (res.noop) return;
27
28
  const adapter = resolveAdapter(host);
28
29
  const skillsDir = adapter.skillsDir?.({ root });
29
30
  if (skillsDir === void 0) {
@@ -45,4 +46,4 @@ async function create(dir, opts) {
45
46
  export {
46
47
  create
47
48
  };
48
- //# sourceMappingURL=create-2P3T7UGZ.js.map
49
+ //# sourceMappingURL=create-VK7JX6IB.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/create.ts"],"sourcesContent":["// `noir create [dir]` — greenfield first-run for the AI layer only.\n//\n// Slice S — new command. Bootstraps Noir's AI layer (.noir/ store, host\n// pointers, skills) into `dir` (created if absent), with NO external\n// scaffolder chaining (S-OQ1 = AI-layer only; no `pnpm create` wrapping).\n//\n// Reuses init's transport/url handling (including the localhost security\n// gate) by calling into the same engine: `scaffold({mode:'create', root})`.\n// The only difference from `noir init` is the mode tag (which selects the\n// manifest's first-run subset + generates a fresh project id unconditionally)\n// and that the target dir is `resolve(dir ?? cwd)` rather than `process.cwd()`.\n//\n// S10 multi-host: gains `--host <id>` (default `'claude'`); the host drives\n// both scaffold emission + skills emission via `resolveAdapter(host)` —\n// shared with init through `emitHostSkills`. See `init.ts` for the matrix.\n//\n// Stream discipline matches init: diagnostics → stderr, nothing on stdout\n// (so `noir create --json` is safe by construction — there is no data payload).\n//\n// Exit codes: `assertTransportUrl` is called DIRECTLY (M2) so transport/url\n// errors propagate as plain `Error` → bin.ts's handleError → exit 1, EXACTLY\n// matching `noir init` for the same error class. (The S9 spec lists usage=2 for\n// a missing `--url`; neither `init` nor `create` implements that split today —\n// both report it as a plain error=1 — and the review asked for consistency\n// over a one-sided fix. Changing them together is a separate, contract-level\n// change.) The target dir is created by the engine's `create` mode (N1: the\n// duplicate mkdir that used to live here is gone — one attributable failure\n// point, in scaffold.ts).\n\nimport { resolve } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from '../conflict.js';\nimport { assertTransportUrl } from '../init.js';\n\nexport interface CreateOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** S10 target host. Defaults to `'claude'`. */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized. */\n force?: boolean;\n}\n\n/**\n * Bootstrap Noir's AI layer in `dir`.\n *\n * @param dir Target directory (created if absent). Defaults to `process.cwd()`.\n * @param opts Transport + URL + host (same semantics as `noir init`).\n */\nexport async function create(dir: string | undefined, opts: CreateOptions): Promise<void> {\n // M2: call assertTransportUrl directly so the SAME error class yields the SAME\n // exit code as `noir init` (plain Error → exit 1 for both missing-url and\n // non-localhost). The previous NoirCliError(EXIT.USAGE) wrapper made `create`\n // exit 2 where `init` exits 1.\n assertTransportUrl(opts);\n\n const root = resolve(dir ?? process.cwd());\n const host: HostId = opts.host ?? 'claude';\n\n await scaffold({\n root,\n mode: 'create',\n host,\n transport: opts.transport,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...buildConflictOpts({ force: opts.force }),\n });\n\n // Skills are out-of-manifest by design — same composition as init.\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n const summary = await emitSkillsToDir(skillsDir, { includeIntegrations: true, target });\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n }\n\n process.stderr.write(`Noir created in ${root} (host: ${host}, transport: ${opts.transport}).\\n`);\n process.stderr.write('Next: cd into the new directory, then run `noir` to open the home menu.\\n');\n}\n"],"mappings":";;;;;;;;;AA6BA,SAAS,eAAe;AACxB,SAAsB,sBAAsB;AAC5C,SAAS,gBAAgB;AACzB,SAA6B,uBAAuB;AAmBpD,eAAsB,OAAO,KAAyB,MAAoC;AAKxF,qBAAmB,IAAI;AAEvB,QAAM,OAAO,QAAQ,OAAO,QAAQ,IAAI,CAAC;AACzC,QAAM,OAAe,KAAK,QAAQ;AAElC,QAAM,SAAS;AAAA,IACb;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IAClD,GAAI,KAAK,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAG,kBAAkB,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,EAC5C,CAAC;AAGD,QAAM,UAAU,eAAe,IAAI;AACnC,QAAM,YAAY,QAAQ,YAAY,EAAE,KAAK,CAAC;AAC9C,MAAI,cAAc,QAAW;AAG3B,YAAQ,OAAO,MAAM,SAAS,IAAI;AAAA,CAA2C;AAAA,EAC/E,OAAO;AACL,UAAM,SAAwB;AAC9B,UAAM,UAAU,MAAM,gBAAgB,WAAW,EAAE,qBAAqB,MAAM,OAAO,CAAC;AACtF,UAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,YAAQ,OAAO;AAAA,MACb,WAAW,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,IAChF;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,mBAAmB,IAAI,WAAW,IAAI,gBAAgB,KAAK,SAAS;AAAA,CAAM;AAC/F,UAAQ,OAAO,MAAM,2EAA2E;AAClG;","names":[]}
1
+ {"version":3,"sources":["../src/commands/create.ts"],"sourcesContent":["// `noir create [dir]` — greenfield first-run for the AI layer only.\n//\n// Slice S — new command. Bootstraps Noir's AI layer (.noir/ store, host\n// pointers, skills) into `dir` (created if absent), with NO external\n// scaffolder chaining (S-OQ1 = AI-layer only; no `pnpm create` wrapping).\n//\n// Reuses init's transport/url handling (including the localhost security\n// gate) by calling into the same engine: `scaffold({mode:'create', root})`.\n// The only difference from `noir init` is the mode tag (which selects the\n// manifest's first-run subset + generates a fresh project id unconditionally)\n// and that the target dir is `resolve(dir ?? cwd)` rather than `process.cwd()`.\n//\n// S10 multi-host: gains `--host <id>` (default `'claude'`); the host drives\n// both scaffold emission + skills emission via `resolveAdapter(host)` —\n// shared with init through `emitHostSkills`. See `init.ts` for the matrix.\n//\n// Stream discipline matches init: diagnostics → stderr, nothing on stdout\n// (so `noir create --json` is safe by construction — there is no data payload).\n//\n// Exit codes: `assertTransportUrl` is called DIRECTLY (M2) so transport/url\n// errors propagate as plain `Error` → bin.ts's handleError → exit 1, EXACTLY\n// matching `noir init` for the same error class. (The S9 spec lists usage=2 for\n// a missing `--url`; neither `init` nor `create` implements that split today —\n// both report it as a plain error=1 — and the review asked for consistency\n// over a one-sided fix. Changing them together is a separate, contract-level\n// change.) The target dir is created by the engine's `create` mode (N1: the\n// duplicate mkdir that used to live here is gone — one attributable failure\n// point, in scaffold.ts).\n\nimport { resolve } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from '../conflict.js';\nimport { assertTransportUrl } from '../init.js';\n\nexport interface CreateOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** S10 target host. Defaults to `'claude'`. */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized. */\n force?: boolean;\n}\n\n/**\n * Bootstrap Noir's AI layer in `dir`.\n *\n * @param dir Target directory (created if absent). Defaults to `process.cwd()`.\n * @param opts Transport + URL + host (same semantics as `noir init`).\n */\nexport async function create(dir: string | undefined, opts: CreateOptions): Promise<void> {\n // M2: call assertTransportUrl directly so the SAME error class yields the SAME\n // exit code as `noir init` (plain Error → exit 1 for both missing-url and\n // non-localhost). The previous NoirCliError(EXIT.USAGE) wrapper made `create`\n // exit 2 where `init` exits 1.\n assertTransportUrl(opts);\n\n const root = resolve(dir ?? process.cwd());\n const host: HostId = opts.host ?? 'claude';\n\n const res = await scaffold({\n root,\n mode: 'create',\n host,\n transport: opts.transport,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...buildConflictOpts({ force: opts.force }),\n });\n // SP-A: a no-op (already-initialized guard) must not re-emit skills.\n if (res.noop) return;\n\n // Skills are out-of-manifest by design — same composition as init.\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n const summary = await emitSkillsToDir(skillsDir, { includeIntegrations: true, target });\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n }\n\n process.stderr.write(`Noir created in ${root} (host: ${host}, transport: ${opts.transport}).\\n`);\n process.stderr.write('Next: cd into the new directory, then run `noir` to open the home menu.\\n');\n}\n"],"mappings":";;;;;;;;;AA6BA,SAAS,eAAe;AACxB,SAAsB,sBAAsB;AAC5C,SAAS,gBAAgB;AACzB,SAA6B,uBAAuB;AAmBpD,eAAsB,OAAO,KAAyB,MAAoC;AAKxF,qBAAmB,IAAI;AAEvB,QAAM,OAAO,QAAQ,OAAO,QAAQ,IAAI,CAAC;AACzC,QAAM,OAAe,KAAK,QAAQ;AAElC,QAAM,MAAM,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IAClD,GAAI,KAAK,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAG,kBAAkB,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,EAC5C,CAAC;AAED,MAAI,IAAI,KAAM;AAGd,QAAM,UAAU,eAAe,IAAI;AACnC,QAAM,YAAY,QAAQ,YAAY,EAAE,KAAK,CAAC;AAC9C,MAAI,cAAc,QAAW;AAG3B,YAAQ,OAAO,MAAM,SAAS,IAAI;AAAA,CAA2C;AAAA,EAC/E,OAAO;AACL,UAAM,SAAwB;AAC9B,UAAM,UAAU,MAAM,gBAAgB,WAAW,EAAE,qBAAqB,MAAM,OAAO,CAAC;AACtF,UAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,YAAQ,OAAO;AAAA,MACb,WAAW,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,IAChF;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM,mBAAmB,IAAI,WAAW,IAAI,gBAAgB,KAAK,SAAS;AAAA,CAAM;AAC/F,UAAQ,OAAO,MAAM,2EAA2E;AAClG;","names":[]}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-J5HAAQI4.js";
6
6
  import {
7
7
  init
8
- } from "./chunk-E5XVH7QR.js";
8
+ } from "./chunk-J2Y3BOVK.js";
9
9
  import "./chunk-OF4OO5W6.js";
10
10
  export {
11
11
  doctor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noir-ai/cli",
3
- "version": "1.3.0-beta.1",
3
+ "version": "1.3.0-beta.3",
4
4
  "description": "Noir CLI — the `noir` command tree (commander + @clack/prompts), the shell entry point to the Noir AI toolkit.",
5
5
  "license": "MIT",
6
6
  "author": "agaaaptr",
@@ -52,14 +52,14 @@
52
52
  "commander": "^12.1.0",
53
53
  "ora": "^8.1.0",
54
54
  "picocolors": "^1.1.1",
55
- "@noir-ai/context": "1.3.0-beta.1",
56
- "@noir-ai/adapters": "1.3.0-beta.1",
57
- "@noir-ai/core": "1.3.0-beta.1",
58
- "@noir-ai/create": "1.3.0-beta.1",
59
- "@noir-ai/model": "1.3.0-beta.1",
60
- "@noir-ai/skills": "1.3.0-beta.1",
61
- "@noir-ai/daemon": "1.3.0-beta.1",
62
- "@noir-ai/store": "1.3.0-beta.1"
55
+ "@noir-ai/adapters": "1.3.0-beta.3",
56
+ "@noir-ai/core": "1.3.0-beta.3",
57
+ "@noir-ai/daemon": "1.3.0-beta.3",
58
+ "@noir-ai/create": "1.3.0-beta.3",
59
+ "@noir-ai/skills": "1.3.0-beta.3",
60
+ "@noir-ai/store": "1.3.0-beta.3",
61
+ "@noir-ai/context": "1.3.0-beta.3",
62
+ "@noir-ai/model": "1.3.0-beta.3"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/node": "^26.1.1"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/init.ts"],"sourcesContent":["// `noir init` — first-run + `--upgrade`.\n//\n// Slice S-T2 refactor: the ad-hoc writers (syncIgnores, writeManagedRegion,\n// writeFileSync for .mcp.json/project.id/config.yml/NOIR.md/RULES.md) are\n// replaced by a single call into `@noir-ai/create`'s `scaffold({mode:'init'})`.\n// The manifest + three-mode writer are the source of truth for what init\n// emits; this module is now a thin caller that owns ONLY:\n// - the transport/url precondition (the localhost security gate, whose\n// error strings are locked by url-validation.test.ts), and\n// - skills emission (out-of-manifest by design — composed after scaffold()).\n//\n// S10 multi-host: the 8 direct `claudeAdapter` imports across init/sync/create\n// collapsed to `resolveAdapter(host)` where `host` comes from `--host <id>`\n// (default `'claude'`). The adapter drives (a) the manifest via\n// `scaffold({host})` and (b) skills emission — claude/cursor have a skill dir;\n// gemini/agents-md/opencode have no skill concept and the call is skipped.\n//\n// Deliberate behavior changes vs the predecessor (spec-aligned latent-bug\n// fixes; see S-T1 contract notes + CHANGELOG):\n// - `.noir/project.id` → skipIfExists (predecessor overwrote on every init,\n// orphaning the store DB named after the id). Re-init now preserves it.\n// - `.noir/config.yml` → skipIfExists (predecessor overwrote).\n// - `.noir/NOIR.md` → managedBlock with BRIEF_BLOCK markers (predecessor\n// wrote the whole file with no markers). First-run output GAINS markers;\n// user notes outside the markers survive re-runs.\n// - `.noir/scaffold-version` is now stamped on init/create (engine-owned).\n\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\n\nexport interface InitOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** `noir init --upgrade`: run scaffold migrations from the on-disk\n * scaffold-version to current, then re-emit ONLY the runtime subset\n * (regenerate + managedBlock). skipIfExists seeds are left alone so user\n * edits survive. */\n upgrade?: boolean;\n /** S10 target host. Defaults to `'claude'` (the regression anchor). Drives\n * both scaffold emission (the manifest's host-specific half) and skills\n * emission (skipped for hosts with no `skillsDir`). */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized (bypasses the\n * already-initialized no-op guard in scaffold()). */\n force?: boolean;\n}\n\nexport async function init(root: string, opts: InitOptions): Promise<void> {\n assertTransportUrl(opts);\n\n const host: HostId = opts.host ?? 'claude';\n\n await scaffold({\n root,\n mode: 'init',\n host,\n transport: opts.transport,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.upgrade === true ? { upgrade: true } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...buildConflictOpts({ force: opts.force }),\n });\n\n await emitHostSkills(root, host);\n\n process.stderr.write(\n `Noir initialized in ${root} (host: ${host}, transport: ${opts.transport}).\\n`,\n );\n process.stderr.write(\n 'Next: run `noir` to open the home menu (or `noir status` for a snapshot).\\n',\n );\n}\n\n/**\n * Compose skill emission onto the resolved adapter's `skillsDir` (claude →\n * `.claude/skills/`; cursor → `.cursor/rules/` compiled as `.mdc`; the other\n * three hosts have no skill concept and are skipped with a stderr note).\n *\n * The `CompileTarget` matches the host id (S10 foundation widened the enum to\n * the same union) so cursor skills compile to the `.mdc` rule shape via\n * `compileSkill(_, 'cursor')`; the others keep the verbatim SKILL.md format.\n */\nasync function emitHostSkills(root: string, host: HostId): Promise<void> {\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n return;\n }\n const target: CompileTarget = host;\n const summary = await emitSkillsToDir(skillsDir, { includeIntegrations: true, target });\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n}\n\n/**\n * Transport + URL precondition. The error strings here are the SECURITY GATE\n * locked by `url-validation.test.ts` — do not rephrase. Thrown errors are\n * plain `Error`s (bin.ts's `main().catch` maps them to stderr + exitCode 1).\n *\n * Exported so `noir create` (commands/create.ts) reuses the SAME gate instead\n * of duplicating the localhost allowlist.\n */\nexport function assertTransportUrl(opts: {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n}): void {\n if (opts.transport === 'streamable-http' && opts.url === undefined) {\n throw new Error('--transport streamable-http requires --url');\n }\n if (opts.url !== undefined) {\n assertLocalhostUrl(opts.url);\n }\n}\n\n// Validate a streamable-http --url is http(s) and localhost-only. Gate 2's\n// daemon binds 127.0.0.1, so persisting a non-localhost URL is a footgun.\n// Not exported: the single external surface is assertTransportUrl(); thrown\n// errors propagate to bin.ts's main().catch (stderr + exitCode 1).\nfunction assertLocalhostUrl(raw: string): void {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(`Invalid URL: ${raw}`);\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new Error('Only http/https URLs are supported');\n }\n if (!['localhost', '127.0.0.1', '::1'].includes(url.hostname)) {\n throw new Error(`Only localhost URLs are supported (got ${url.hostname})`);\n }\n}\n"],"mappings":";;;;;;AA2BA,SAAsB,sBAAsB;AAC5C,SAAS,gBAAgB;AACzB,SAA6B,uBAAuB;AAoBpD,eAAsB,KAAK,MAAc,MAAkC;AACzE,qBAAmB,IAAI;AAEvB,QAAM,OAAe,KAAK,QAAQ;AAElC,QAAM,SAAS;AAAA,IACb;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,GAAI,KAAK,QAAQ,SAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IAClD,GAAI,KAAK,YAAY,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IACjD,GAAI,KAAK,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAG,kBAAkB,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,EAC5C,CAAC;AAED,QAAM,eAAe,MAAM,IAAI;AAE/B,UAAQ,OAAO;AAAA,IACb,uBAAuB,IAAI,WAAW,IAAI,gBAAgB,KAAK,SAAS;AAAA;AAAA,EAC1E;AACA,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACF;AAWA,eAAe,eAAe,MAAc,MAA6B;AACvE,QAAM,UAAU,eAAe,IAAI;AACnC,QAAM,YAAY,QAAQ,YAAY,EAAE,KAAK,CAAC;AAC9C,MAAI,cAAc,QAAW;AAG3B,YAAQ,OAAO,MAAM,SAAS,IAAI;AAAA,CAA2C;AAC7E;AAAA,EACF;AACA,QAAM,SAAwB;AAC9B,QAAM,UAAU,MAAM,gBAAgB,WAAW,EAAE,qBAAqB,MAAM,OAAO,CAAC;AACtF,QAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,UAAQ,OAAO;AAAA,IACb,WAAW,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,EAChF;AACF;AAUO,SAAS,mBAAmB,MAG1B;AACP,MAAI,KAAK,cAAc,qBAAqB,KAAK,QAAQ,QAAW;AAClE,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,MAAI,KAAK,QAAQ,QAAW;AAC1B,uBAAmB,KAAK,GAAG;AAAA,EAC7B;AACF;AAMA,SAAS,mBAAmB,KAAmB;AAC7C,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,GAAG;AAAA,EACnB,QAAQ;AACN,UAAM,IAAI,MAAM,gBAAgB,GAAG,EAAE;AAAA,EACvC;AACA,MAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,IAAI,QAAQ,GAAG;AAC/C,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,MAAI,CAAC,CAAC,aAAa,aAAa,KAAK,EAAE,SAAS,IAAI,QAAQ,GAAG;AAC7D,UAAM,IAAI,MAAM,0CAA0C,IAAI,QAAQ,GAAG;AAAA,EAC3E;AACF;","names":[]}