@noir-ai/cli 1.11.2-beta.1 → 1.12.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.
Files changed (45) hide show
  1. package/dist/bin.js +3295 -575
  2. package/dist/bin.js.map +1 -1
  3. package/dist/index.js +6136 -6
  4. package/dist/index.js.map +1 -1
  5. package/dist/{chunk-QIVHIKYD.js → tui/chunk-2RK6AFZQ.js} +37 -16
  6. package/dist/tui/chunk-2RK6AFZQ.js.map +1 -0
  7. package/dist/tui/{chunk-R5YX44UL.js → chunk-ESZTOQEC.js} +211 -2
  8. package/dist/tui/chunk-ESZTOQEC.js.map +1 -0
  9. package/dist/tui/chunk-J77RHAWG.js +5010 -0
  10. package/dist/tui/chunk-J77RHAWG.js.map +1 -0
  11. package/dist/{create-VK2QMXYS.js → tui/create-BTYLBVV6.js} +4 -4
  12. package/dist/tui/{create-WKFVM237.js.map → create-BTYLBVV6.js.map} +1 -1
  13. package/dist/tui/index.js +45 -3454
  14. package/dist/tui/index.js.map +1 -1
  15. package/dist/tui/{install-JR6ZXNYA.js → install-YGTUSLRO.js} +2 -3
  16. package/dist/{sync-VXZKODQA.js → tui/sync-LRHPJ5VV.js} +4 -4
  17. package/dist/tui/{sync-DUWJAF7Z.js.map → sync-LRHPJ5VV.js.map} +1 -1
  18. package/dist/tui/{update-PGAM6OVL.js → update-LET5BWOU.js} +3 -4
  19. package/package.json +12 -12
  20. package/dist/chunk-A5W2E3X2.js +0 -33
  21. package/dist/chunk-A5W2E3X2.js.map +0 -1
  22. package/dist/chunk-CT6TYLHN.js +0 -1105
  23. package/dist/chunk-CT6TYLHN.js.map +0 -1
  24. package/dist/chunk-I2R55ACD.js +0 -369
  25. package/dist/chunk-I2R55ACD.js.map +0 -1
  26. package/dist/chunk-QIVHIKYD.js.map +0 -1
  27. package/dist/chunk-RON2XESF.js +0 -210
  28. package/dist/chunk-RON2XESF.js.map +0 -1
  29. package/dist/create-VK2QMXYS.js.map +0 -1
  30. package/dist/install-4U4UDDNE.js +0 -13
  31. package/dist/sync-VXZKODQA.js.map +0 -1
  32. package/dist/tui/chunk-F6XRS72N.js +0 -1106
  33. package/dist/tui/chunk-F6XRS72N.js.map +0 -1
  34. package/dist/tui/chunk-NLK6UNIA.js +0 -223
  35. package/dist/tui/chunk-NLK6UNIA.js.map +0 -1
  36. package/dist/tui/chunk-QF6777NJ.js +0 -209
  37. package/dist/tui/chunk-QF6777NJ.js.map +0 -1
  38. package/dist/tui/chunk-R5YX44UL.js.map +0 -1
  39. package/dist/tui/create-WKFVM237.js +0 -79
  40. package/dist/tui/sync-DUWJAF7Z.js +0 -90
  41. package/dist/tui/update-PGAM6OVL.js.map +0 -1
  42. package/dist/update-ZDAVLOXB.js +0 -20
  43. package/dist/update-ZDAVLOXB.js.map +0 -1
  44. /package/dist/{install-4U4UDDNE.js.map → tui/install-YGTUSLRO.js.map} +0 -0
  45. /package/dist/tui/{install-JR6ZXNYA.js.map → update-LET5BWOU.js.map} +0 -0
@@ -2,11 +2,10 @@ import {
2
2
  buildMigrationPlan,
3
3
  install,
4
4
  installManagedNode
5
- } from "./chunk-QF6777NJ.js";
6
- import "./chunk-R5YX44UL.js";
5
+ } from "./chunk-ESZTOQEC.js";
7
6
  export {
8
7
  buildMigrationPlan,
9
8
  install,
10
9
  installManagedNode
11
10
  };
12
- //# sourceMappingURL=install-JR6ZXNYA.js.map
11
+ //# sourceMappingURL=install-YGTUSLRO.js.map
@@ -1,12 +1,12 @@
1
- #!/usr/bin/env node
2
1
  import {
3
2
  buildConflictOpts,
4
3
  checkWritePathDedup,
5
4
  reportPlannedWrites
6
- } from "./chunk-CT6TYLHN.js";
5
+ } from "./chunk-J77RHAWG.js";
6
+ import "./chunk-2RK6AFZQ.js";
7
7
  import {
8
8
  resolveInteractive
9
- } from "./chunk-I2R55ACD.js";
9
+ } from "./chunk-ESZTOQEC.js";
10
10
 
11
11
  // src/sync.ts
12
12
  import { existsSync } from "fs";
@@ -88,4 +88,4 @@ function resolveSyncHost(root, opts) {
88
88
  export {
89
89
  sync
90
90
  };
91
- //# sourceMappingURL=sync-VXZKODQA.js.map
91
+ //# sourceMappingURL=sync-LRHPJ5VV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/sync.ts"],"sourcesContent":["// `noir sync` — re-emit the runtime subset of the scaffold manifest.\n//\n// Slice S-T2 refactor: delegates to `@noir-ai/create`'s `scaffold({mode:'sync'})`,\n// which emits ONLY the regenerate + managedBlock entries (the always-safe-to-\n// rewrite subset). Per the S-T2 contract notes, this ADOPTS THE ENGINE'S\n// SEMANTICS and retires the predecessor's ad-hoc behavior:\n//\n// - The engine re-emits `.mcp.json` (regenerate) + NOIR.md brief\n// (managedBlock) + CLAUDE.md context/rules blocks + ignore-file blocks.\n// The predecessor's sync re-emitted only CLAUDE.md blocks + ignores and\n// did NOT refresh `.mcp.json`/NOIR.md — the engine is a strict superset\n// here, so a transport/url change is now picked up by `noir sync`.\n// - The engine does NOT seed `.noir/rules/RULES.md` on sync (RULES.md is\n// skipIfExists, owned by init/create). The predecessor seeded it when\n// missing; init now owns all seeds. (Spec-aligned: \"sync re-emits\n// generated/managed content; init owns seeds.\")\n//\n// S10 multi-host: sync resolves the host from `.noir/config.yml` (persisted by\n// `noir init --host <id>` via the config seed's `host: {{host}}` literal) OR\n// from an explicit `--host <id>` override. The resolved adapter drives the\n// manifest + skills emission. Skills emission stays a core sync feature for\n// hosts with a `skillsDir` (claude → `.claude/skills/`; cursor → `.cursor/rules/`\n// as `.mdc`); gemini/agents-md/opencode have no skill concept and the step is\n// skipped. `scaffold({mode:'sync'})` throws when the project isn't initialized\n// (no .noir/project.id), matching the predecessor's `loadProjectInfo` gate.\n\nimport { existsSync } from 'node:fs';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, paths } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { reportPlannedWrites } from './init.js';\nimport { resolveInteractive } from './output.js';\n\nexport interface SyncOptions {\n /** S10 `--host <id>` override. When set, takes precedence over the\n * `.noir/config.yml` `host:` field. Useful for re-emitting under a\n * different host without re-init (advanced — the canonical host stays\n * whatever init wrote). */\n host?: HostId;\n /** SP-C: overwrite differing regenerated files without prompting (bypasses\n * the conflict menu). */\n force?: boolean;\n /** SP-D: three-way merge managed regions (preserve hand-edits inside\n * `<!-- noir:* -->` markers across a template update). DEFAULT TRUE;\n * `--merge` is now a no-op (kept for backward compatibility). */\n merge?: boolean;\n /** Opt OUT of managed-region merge (restore strip-replace). Exposed on\n * the bin as `--no-merge-regions`. When explicitly `false`, hand-edits\n * inside `<!-- noir:* -->` markers are discarded on a template upgrade. */\n mergeManagedRegions?: boolean;\n /** F1: `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk. sync still requires a valid `.noir/project.id`\n * (the engine's \"not initialized\" gate fires regardless). */\n dryRun?: boolean;\n /** F1: alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\nexport async function sync(root: string, opts: SyncOptions = {}): Promise<ScaffoldResult> {\n const host = resolveSyncHost(root, opts);\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n\n // Load project info once for both the dedup hook (embedder config)\n // and the existing config-host fallback. Best-effort: a missing/corrupt file\n // is left to scaffold's \"not initialized\" gate below.\n let projectInfo: ReturnType<typeof loadProjectInfo> | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n\n // F1: --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'sync',\n host,\n interactive,\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n // Merge defaults TRUE inside scaffold; only forward an explicit opt-out.\n ...(opts.mergeManagedRegions === false ? { mergeManagedRegions: false } : {}),\n });\n\n // F1: dry-run reports the planned writes and stops BEFORE skills emission +\n // dedup (both would touch disk / load the embedder). The engine still throws\n // its \"not initialized\" gate when `.noir/project.id` is missing, so a dry-run\n // sync on an uninitialized project is a clean error (not a misleading no-op).\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n\n // Surface the no-op so users see sync was a true no-op on disk (the\n // scaffold wrote nothing — every runtime file was content-hash identical).\n // Skills emission below still runs (it has its own dedup).\n if (res.written.length === 0 && res.identical.length > 0) {\n process.stderr.write('Nothing to sync (Noir-managed files are up to date).\\n');\n }\n\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 sync phrased this as \"nothing to sync\".)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so the skills-emit conflict flow is\n // live in interactive mode (mirrors init.ts). --json/--no-input stays\n // prompt-free via the `interactive: false` guard.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Synced ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // Surface stale-dir pruning so a user can see when a previous Noir\n // version's builtin was removed (the dir was deleted from .claude/skills/).\n // Pure hygiene; never affects correctness of the freshly-emitted pack.\n const pruned = summary.pruned ?? [];\n if (pruned.length > 0) {\n process.stderr.write(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}\\n`,\n );\n }\n }\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n return res;\n}\n\n/** Resolve the sync host: explicit `--host` override > `.noir/config.yml`\n * `host:` field > `'claude'` (default — preserves the v1.1 regression anchor\n * for projects initialized before S10). The config read is best-effort: if\n * `.noir/config.yml` is absent OR fails to parse, scaffold's own\n * \"not initialized\" gate (no `.noir/project.id`) fires below with the locked\n * error string. */\nfunction resolveSyncHost(root: string, opts: SyncOptions): HostId {\n if (opts.host !== undefined) return opts.host;\n if (!existsSync(paths.config(root))) return 'claude';\n try {\n return loadProjectInfo(root).config.host;\n } catch {\n return 'claude';\n }\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAS,kBAAkB;AAC3B,SAAsB,sBAAsB;AAC5C,SAAS,iBAAiB,aAAa;AACvC,SAA8B,gBAAgB;AAC9C,SAA6B,uBAAuB;AAgCpD,eAAsB,KAAK,MAAc,OAAoB,CAAC,GAA4B;AACxF,QAAM,OAAO,gBAAgB,MAAM,IAAI;AAGvC,QAAM,cAAc,mBAAmB;AACvC,QAAM,eAAe,kBAAkB,EAAE,OAAO,KAAK,OAAO,YAAY,CAAC;AAKzE,MAAI;AACJ,MAAI;AACF,kBAAc,gBAAgB,IAAI;AAAA,EACpC,QAAQ;AACN,kBAAc;AAAA,EAChB;AAGA,QAAM,SAAS,KAAK,WAAW,QAAQ,KAAK,YAAY;AAExD,QAAM,MAAM,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,GAAI,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,IACjC,GAAG;AAAA;AAAA,IAEH,GAAI,KAAK,wBAAwB,QAAQ,EAAE,qBAAqB,MAAM,IAAI,CAAC;AAAA,EAC7E,CAAC;AAMD,MAAI,QAAQ;AACV,wBAAoB,GAAG;AACvB,WAAO;AAAA,EACT;AAKA,MAAI,IAAI,QAAQ,WAAW,KAAK,IAAI,UAAU,SAAS,GAAG;AACxD,YAAQ,OAAO,MAAM,wDAAwD;AAAA,EAC/E;AAEA,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;AAK9B,UAAM,YAA2B;AAAA,MAC/B,qBAAqB;AAAA,MACrB;AAAA,MACA,gBAAgB,aAAa;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,aAAa,eAAe,QAAW;AACzC,gBAAU,aAAa,aAAa;AAAA,IACtC;AACA,UAAM,UAAU,MAAM,gBAAgB,WAAW,SAAS;AAC1D,UAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,YAAQ,OAAO;AAAA,MACb,UAAU,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,IAC/E;AAIA,UAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,OAAO;AAAA,QACb,UAAU,OAAO,MAAM,0BAA0B,OAAO,WAAW,IAAI,KAAK,GAAG,KAAK,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA,MACvG;AAAA,IACF;AAAA,EACF;AAKA,QAAM,QAAQ,MAAM,oBAAoB,MAAM,KAAK,EAAE,aAAa,SAAS,YAAY,CAAC;AACxF,MAAI,MAAM,UAAU,SAAS,EAAG,KAAI,UAAU,KAAK,GAAG,MAAM,SAAS;AAErE,SAAO;AACT;AAQA,SAAS,gBAAgB,MAAc,MAA2B;AAChE,MAAI,KAAK,SAAS,OAAW,QAAO,KAAK;AACzC,MAAI,CAAC,WAAW,MAAM,OAAO,IAAI,CAAC,EAAG,QAAO;AAC5C,MAAI;AACF,WAAO,gBAAgB,IAAI,EAAE,OAAO;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/sync.ts"],"sourcesContent":["// `noir sync` — re-emit the runtime subset of the scaffold manifest.\n//\n// Slice S-T2 refactor: delegates to `@noir-ai/create`'s `scaffold({mode:'sync'})`,\n// which emits ONLY the regenerate + managedBlock entries (the always-safe-to-\n// rewrite subset). Per the S-T2 contract notes, this ADOPTS THE ENGINE'S\n// SEMANTICS and retires the predecessor's ad-hoc behavior:\n//\n// - The engine re-emits `.mcp.json` (regenerate) + NOIR.md brief\n// (managedBlock) + CLAUDE.md context/rules blocks + ignore-file blocks.\n// The predecessor's sync re-emitted only CLAUDE.md blocks + ignores and\n// did NOT refresh `.mcp.json`/NOIR.md — the engine is a strict superset\n// here, so a transport/url change is now picked up by `noir sync`.\n// - The engine does NOT seed `.noir/rules/RULES.md` on sync (RULES.md is\n// skipIfExists, owned by init/create). The predecessor seeded it when\n// missing; init now owns all seeds. (Spec-aligned: \"sync re-emits\n// generated/managed content; init owns seeds.\")\n//\n// S10 multi-host: sync resolves the host from `.noir/config.yml` (persisted by\n// `noir init --host <id>` via the config seed's `host: {{host}}` literal) OR\n// from an explicit `--host <id>` override. The resolved adapter drives the\n// manifest + skills emission. Skills emission stays a core sync feature for\n// hosts with a `skillsDir` (claude → `.claude/skills/`; cursor → `.cursor/rules/`\n// as `.mdc`); gemini/agents-md/opencode have no skill concept and the step is\n// skipped. `scaffold({mode:'sync'})` throws when the project isn't initialized\n// (no .noir/project.id), matching the predecessor's `loadProjectInfo` gate.\n\nimport { existsSync } from 'node:fs';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, paths } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { reportPlannedWrites } from './init.js';\nimport { resolveInteractive } from './output.js';\n\nexport interface SyncOptions {\n /** S10 `--host <id>` override. When set, takes precedence over the\n * `.noir/config.yml` `host:` field. Useful for re-emitting under a\n * different host without re-init (advanced — the canonical host stays\n * whatever init wrote). */\n host?: HostId;\n /** SP-C: overwrite differing regenerated files without prompting (bypasses\n * the conflict menu). */\n force?: boolean;\n /** SP-D: three-way merge managed regions (preserve hand-edits inside\n * `<!-- noir:* -->` markers across a template update). DEFAULT TRUE;\n * `--merge` is now a no-op (kept for backward compatibility). */\n merge?: boolean;\n /** Opt OUT of managed-region merge (restore strip-replace). Exposed on\n * the bin as `--no-merge-regions`. When explicitly `false`, hand-edits\n * inside `<!-- noir:* -->` markers are discarded on a template upgrade. */\n mergeManagedRegions?: boolean;\n /** F1: `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk. sync still requires a valid `.noir/project.id`\n * (the engine's \"not initialized\" gate fires regardless). */\n dryRun?: boolean;\n /** F1: alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\nexport async function sync(root: string, opts: SyncOptions = {}): Promise<ScaffoldResult> {\n const host = resolveSyncHost(root, opts);\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n\n // Load project info once for both the dedup hook (embedder config)\n // and the existing config-host fallback. Best-effort: a missing/corrupt file\n // is left to scaffold's \"not initialized\" gate below.\n let projectInfo: ReturnType<typeof loadProjectInfo> | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n\n // F1: --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'sync',\n host,\n interactive,\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n // Merge defaults TRUE inside scaffold; only forward an explicit opt-out.\n ...(opts.mergeManagedRegions === false ? { mergeManagedRegions: false } : {}),\n });\n\n // F1: dry-run reports the planned writes and stops BEFORE skills emission +\n // dedup (both would touch disk / load the embedder). The engine still throws\n // its \"not initialized\" gate when `.noir/project.id` is missing, so a dry-run\n // sync on an uninitialized project is a clean error (not a misleading no-op).\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n\n // Surface the no-op so users see sync was a true no-op on disk (the\n // scaffold wrote nothing — every runtime file was content-hash identical).\n // Skills emission below still runs (it has its own dedup).\n if (res.written.length === 0 && res.identical.length > 0) {\n process.stderr.write('Nothing to sync (Noir-managed files are up to date).\\n');\n }\n\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 sync phrased this as \"nothing to sync\".)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so the skills-emit conflict flow is\n // live in interactive mode (mirrors init.ts). --json/--no-input stays\n // prompt-free via the `interactive: false` guard.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Synced ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // Surface stale-dir pruning so a user can see when a previous Noir\n // version's builtin was removed (the dir was deleted from .claude/skills/).\n // Pure hygiene; never affects correctness of the freshly-emitted pack.\n const pruned = summary.pruned ?? [];\n if (pruned.length > 0) {\n process.stderr.write(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}\\n`,\n );\n }\n }\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n return res;\n}\n\n/** Resolve the sync host: explicit `--host` override > `.noir/config.yml`\n * `host:` field > `'claude'` (default — preserves the v1.1 regression anchor\n * for projects initialized before S10). The config read is best-effort: if\n * `.noir/config.yml` is absent OR fails to parse, scaffold's own\n * \"not initialized\" gate (no `.noir/project.id`) fires below with the locked\n * error string. */\nfunction resolveSyncHost(root: string, opts: SyncOptions): HostId {\n if (opts.host !== undefined) return opts.host;\n if (!existsSync(paths.config(root))) return 'claude';\n try {\n return loadProjectInfo(root).config.host;\n } catch {\n return 'claude';\n }\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAS,kBAAkB;AAC3B,SAAsB,sBAAsB;AAC5C,SAAS,iBAAiB,aAAa;AACvC,SAA8B,gBAAgB;AAC9C,SAA6B,uBAAuB;AAgCpD,eAAsB,KAAK,MAAc,OAAoB,CAAC,GAA4B;AACxF,QAAM,OAAO,gBAAgB,MAAM,IAAI;AAGvC,QAAM,cAAc,mBAAmB;AACvC,QAAM,eAAe,kBAAkB,EAAE,OAAO,KAAK,OAAO,YAAY,CAAC;AAKzE,MAAI;AACJ,MAAI;AACF,kBAAc,gBAAgB,IAAI;AAAA,EACpC,QAAQ;AACN,kBAAc;AAAA,EAChB;AAGA,QAAM,SAAS,KAAK,WAAW,QAAQ,KAAK,YAAY;AAExD,QAAM,MAAM,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,GAAI,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,IACjC,GAAG;AAAA;AAAA,IAEH,GAAI,KAAK,wBAAwB,QAAQ,EAAE,qBAAqB,MAAM,IAAI,CAAC;AAAA,EAC7E,CAAC;AAMD,MAAI,QAAQ;AACV,wBAAoB,GAAG;AACvB,WAAO;AAAA,EACT;AAKA,MAAI,IAAI,QAAQ,WAAW,KAAK,IAAI,UAAU,SAAS,GAAG;AACxD,YAAQ,OAAO,MAAM,wDAAwD;AAAA,EAC/E;AAEA,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;AAK9B,UAAM,YAA2B;AAAA,MAC/B,qBAAqB;AAAA,MACrB;AAAA,MACA,gBAAgB,aAAa;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,aAAa,eAAe,QAAW;AACzC,gBAAU,aAAa,aAAa;AAAA,IACtC;AACA,UAAM,UAAU,MAAM,gBAAgB,WAAW,SAAS;AAC1D,UAAM,SAAS,UAAU,QAAQ,GAAG,IAAI,KAAK,EAAE;AAC/C,YAAQ,OAAO;AAAA,MACb,UAAU,QAAQ,QAAQ,MAAM,mBAAmB,MAAM,cAAc,MAAM;AAAA;AAAA,IAC/E;AAIA,UAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,OAAO;AAAA,QACb,UAAU,OAAO,MAAM,0BAA0B,OAAO,WAAW,IAAI,KAAK,GAAG,KAAK,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA,MACvG;AAAA,IACF;AAAA,EACF;AAKA,QAAM,QAAQ,MAAM,oBAAoB,MAAM,KAAK,EAAE,aAAa,SAAS,YAAY,CAAC;AACxF,MAAI,MAAM,UAAU,SAAS,EAAG,KAAI,UAAU,KAAK,GAAG,MAAM,SAAS;AAErE,SAAO;AACT;AAQA,SAAS,gBAAgB,MAAc,MAA2B;AAChE,MAAI,KAAK,SAAS,OAAW,QAAO,KAAK;AACzC,MAAI,CAAC,WAAW,MAAM,OAAO,IAAI,CAAC,EAAG,QAAO;AAC5C,MAAI;AACF,WAAO,gBAAgB,IAAI,EAAE,OAAO;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -5,9 +5,8 @@ import {
5
5
  runAsyncUpdateCheck,
6
6
  update,
7
7
  updateAdvice
8
- } from "./chunk-NLK6UNIA.js";
9
- import "./chunk-QF6777NJ.js";
10
- import "./chunk-R5YX44UL.js";
8
+ } from "./chunk-2RK6AFZQ.js";
9
+ import "./chunk-ESZTOQEC.js";
11
10
  export {
12
11
  DEFAULT_UPDATE_CONFIG,
13
12
  buildUpdateNotice,
@@ -16,4 +15,4 @@ export {
16
15
  update,
17
16
  updateAdvice
18
17
  };
19
- //# sourceMappingURL=update-PGAM6OVL.js.map
18
+ //# sourceMappingURL=update-LET5BWOU.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noir-ai/cli",
3
- "version": "1.11.2-beta.1",
3
+ "version": "1.12.0-beta.1",
4
4
  "description": "Noir CLI — the `noir` command tree (commander + Ink dashboard + @clack home menu), the shell entry point to the Noir AI toolkit.",
5
5
  "license": "MIT",
6
6
  "author": "agaaaptr",
@@ -55,19 +55,19 @@
55
55
  "ora": "^8.1.0",
56
56
  "picocolors": "^1.1.1",
57
57
  "react": "^19.2.0",
58
- "@noir-ai/context": "1.11.2-beta.1",
59
- "@noir-ai/adapters": "1.11.2-beta.1",
60
- "@noir-ai/core": "1.11.2-beta.1",
61
- "@noir-ai/daemon": "1.11.2-beta.1",
62
- "@noir-ai/create": "1.11.2-beta.1",
63
- "@noir-ai/memory": "1.11.2-beta.1",
64
- "@noir-ai/skills": "1.11.2-beta.1",
65
- "@noir-ai/model": "1.11.2-beta.1",
66
- "@noir-ai/workflow": "1.11.2-beta.1",
67
- "@noir-ai/store": "1.11.2-beta.1"
58
+ "@noir-ai/adapters": "1.12.0-beta.1",
59
+ "@noir-ai/context": "1.12.0-beta.1",
60
+ "@noir-ai/create": "1.12.0-beta.1",
61
+ "@noir-ai/core": "1.12.0-beta.1",
62
+ "@noir-ai/model": "1.12.0-beta.1",
63
+ "@noir-ai/memory": "1.12.0-beta.1",
64
+ "@noir-ai/skills": "1.12.0-beta.1",
65
+ "@noir-ai/workflow": "1.12.0-beta.1",
66
+ "@noir-ai/store": "1.12.0-beta.1",
67
+ "@noir-ai/daemon": "1.12.0-beta.1"
68
68
  },
69
69
  "devDependencies": {
70
- "@types/node": "^26.1.1",
70
+ "@types/node": "^22.17.0",
71
71
  "@types/react": "^19.2.0",
72
72
  "ink-testing-library": "^4.0.0"
73
73
  },
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/serve.ts
4
- import { loadProjectInfo } from "@noir-ai/core";
5
- import { ensureDaemonRunning, startStdioServer } from "@noir-ai/daemon";
6
- async function serve(opts) {
7
- const project = loadProjectInfo(process.cwd());
8
- if (opts.stdio) {
9
- await startStdioServer({ project, transport: "stdio", daemon: false });
10
- return;
11
- }
12
- try {
13
- const { url } = await ensureDaemonRunning({
14
- project,
15
- idleTimeoutSec: project.config.daemon.idleTimeoutSec
16
- });
17
- process.stderr.write(
18
- `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.)
19
- `
20
- );
21
- } catch (err) {
22
- process.stderr.write(
23
- `Noir daemon unavailable (${err instanceof Error ? err.message : String(err)}); falling back to stdio.
24
- `
25
- );
26
- await startStdioServer({ project, transport: "stdio", daemon: false });
27
- }
28
- }
29
-
30
- export {
31
- serve
32
- };
33
- //# sourceMappingURL=chunk-A5W2E3X2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/serve.ts"],"sourcesContent":["import { loadProjectInfo } from '@noir-ai/core';\nimport { ensureDaemonRunning, startStdioServer } from '@noir-ai/daemon';\n\nexport async function serve(opts: { stdio: boolean }): Promise<void> {\n const project = loadProjectInfo(process.cwd());\n if (opts.stdio) {\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n return; // stdio server runs until stdin closes\n }\n // Prefer the shared daemon; on failure fall back to in-process stdio so Noir\n // keeps working even when the daemon cannot start (FS-degradation path).\n try {\n const { url } = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n });\n process.stderr.write(\n `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.)\\n`,\n );\n } catch (err) {\n process.stderr.write(\n `Noir daemon unavailable (${err instanceof Error ? err.message : String(err)}); falling back to stdio.\\n`,\n );\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n }\n}\n"],"mappings":";;;AAAA,SAAS,uBAAuB;AAChC,SAAS,qBAAqB,wBAAwB;AAEtD,eAAsB,MAAM,MAAyC;AACnE,QAAM,UAAU,gBAAgB,QAAQ,IAAI,CAAC;AAC7C,MAAI,KAAK,OAAO;AACd,UAAM,iBAAiB,EAAE,SAAS,WAAW,SAAS,QAAQ,MAAM,CAAC;AACrE;AAAA,EACF;AAGA,MAAI;AACF,UAAM,EAAE,IAAI,IAAI,MAAM,oBAAoB;AAAA,MACxC;AAAA,MACA,gBAAgB,QAAQ,OAAO,OAAO;AAAA,IACxC,CAAC;AACD,YAAQ,OAAO;AAAA,MACb,4BAA4B,GAAG;AAAA;AAAA,IACjC;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,OAAO;AAAA,MACb,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,IAC9E;AACA,UAAM,iBAAiB,EAAE,SAAS,WAAW,SAAS,QAAQ,MAAM,CAAC;AAAA,EACvE;AACF;","names":[]}