@noir-ai/cli 1.17.0-beta.1 → 1.18.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.
@@ -1135,6 +1135,14 @@ var HYGIENE_MARKDOWN_EXTENSIONS = /\.(?:md|mdx)$/i;
1135
1135
  var HYGIENE_SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
1136
1136
  var HYGIENE_EXCLUDED_DIRS = /* @__PURE__ */ new Set(["docs/internal", "docs/decisions", "docs/roadmap"]);
1137
1137
  var HYGIENE_EXCLUDED_FILES = /* @__PURE__ */ new Set(["CHANGELOG.md"]);
1138
+ var HYGIENE_EXCLUDED_PATHS = /* @__PURE__ */ new Set([
1139
+ "docs/README.md",
1140
+ "docs/reference/cli.md",
1141
+ "docs/reference/config.md",
1142
+ "docs/reference/mcp-tools.md",
1143
+ "docs/reference/packages.md",
1144
+ "docs/reference/skills.md"
1145
+ ]);
1138
1146
  function readDirEntries(dir) {
1139
1147
  try {
1140
1148
  return readdirSync2(dir, { withFileTypes: true });
@@ -1165,7 +1173,8 @@ function hasScannableLayout(root) {
1165
1173
  }
1166
1174
  function addHygieneFile(rel, walk) {
1167
1175
  const name = rel.slice(rel.lastIndexOf("/") + 1);
1168
- if (name.startsWith(".") || HYGIENE_EXCLUDED_FILES.has(name)) return;
1176
+ if (name.startsWith(".") || HYGIENE_EXCLUDED_FILES.has(name) || HYGIENE_EXCLUDED_PATHS.has(rel))
1177
+ return;
1169
1178
  const kind = hygieneKindOf(rel);
1170
1179
  if (kind === null) return;
1171
1180
  if (walk.files.length >= HYGIENE_MAX_FILES) {
@@ -2921,11 +2930,13 @@ function requiredArgLabel(cmd) {
2921
2930
  const required = cmd.registeredArguments.find((arg) => arg.required);
2922
2931
  return required?.name();
2923
2932
  }
2933
+ var INTERNAL_LEAF_COMMANDS = /* @__PURE__ */ new Set(["hook"]);
2924
2934
  function collectLeaves(cmd, path, out) {
2925
2935
  const children = cmd.commands;
2926
2936
  const isLeaf = hasAction(cmd) && children.length === 0;
2927
2937
  if (isLeaf) {
2928
2938
  const argv = path;
2939
+ if (INTERNAL_LEAF_COMMANDS.has(argv.join(" "))) return;
2929
2940
  const id = argv.join(" ");
2930
2941
  const description = cmd.description() || argv.join(" ");
2931
2942
  out.push({
@@ -3379,8 +3390,50 @@ ${COMMANDS_HINT}
3379
3390
  await runGroupedMenu(clack, opts, deps, sections);
3380
3391
  }
3381
3392
 
3393
+ // src/commands/hook.ts
3394
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
3395
+ import { join as join4 } from "path";
3396
+ function hook() {
3397
+ let eventName = "SessionStart";
3398
+ let root = process.cwd();
3399
+ try {
3400
+ const input = JSON.parse(readFileSync4(0, "utf8"));
3401
+ if (typeof input.hook_event_name === "string" && input.hook_event_name !== "") {
3402
+ eventName = input.hook_event_name;
3403
+ }
3404
+ if (typeof input.cwd === "string" && input.cwd !== "") root = input.cwd;
3405
+ } catch {
3406
+ }
3407
+ process.stdout.write(JSON.stringify(hookOutput(eventName, root)));
3408
+ }
3409
+ function hookOutput(eventName, root) {
3410
+ let routerPath = null;
3411
+ let rulesPath = null;
3412
+ for (let dir = root; ; dir = join4(dir, "..")) {
3413
+ const router = join4(dir, ".noir", "router.md");
3414
+ const rules = join4(dir, ".noir", "rules", "anti-slop.md");
3415
+ if (existsSync3(router) || existsSync3(rules)) {
3416
+ routerPath = router;
3417
+ rulesPath = rules;
3418
+ break;
3419
+ }
3420
+ const parent = join4(dir, "..");
3421
+ if (parent === dir) break;
3422
+ }
3423
+ const parts = [];
3424
+ for (const path of [routerPath, rulesPath]) {
3425
+ if (path === null) continue;
3426
+ try {
3427
+ const text = readFileSync4(path, "utf8").trim();
3428
+ if (text !== "") parts.push(text);
3429
+ } catch {
3430
+ }
3431
+ }
3432
+ return parts.length === 0 ? { hookSpecificOutput: { hookEventName: eventName } } : { hookSpecificOutput: { hookEventName: eventName, additionalContext: parts.join("\n\n") } };
3433
+ }
3434
+
3382
3435
  // src/commands/memory.ts
3383
- import { readFileSync as readFileSync4 } from "fs";
3436
+ import { readFileSync as readFileSync5 } from "fs";
3384
3437
  import { loadProjectInfo as loadProjectInfo9 } from "@noir-ai/core";
3385
3438
  function resolveWorkspace() {
3386
3439
  const routing = workspaceRoutingTarget(process.cwd());
@@ -3598,14 +3651,14 @@ async function resolveCaptureContent(opts) {
3598
3651
  if (typeof opts.content === "string" && opts.content.length > 0) return opts.content;
3599
3652
  if (typeof opts.file === "string" && opts.file.length > 0) {
3600
3653
  try {
3601
- return readFileSync4(opts.file, "utf8");
3654
+ return readFileSync5(opts.file, "utf8");
3602
3655
  } catch {
3603
3656
  fail(EXIT.USAGE, `memory capture could not read --file ${opts.file}`, opts);
3604
3657
  }
3605
3658
  }
3606
3659
  if (!process.stdin.isTTY) {
3607
3660
  try {
3608
- return readFileSync4(0, "utf8");
3661
+ return readFileSync5(0, "utf8");
3609
3662
  } catch {
3610
3663
  fail(
3611
3664
  EXIT.USAGE,
@@ -3885,8 +3938,8 @@ async function release(opts) {
3885
3938
  }
3886
3939
 
3887
3940
  // src/commands/run.ts
3888
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
3889
- import { join as join4 } from "path";
3941
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
3942
+ import { join as join5 } from "path";
3890
3943
  import { SUPPORTED_HOSTS } from "@noir-ai/adapters";
3891
3944
  import { isDeniedEnvKey as isDeniedEnvKey2, loadNoirEnv as loadNoirEnv3, NOIR_DIR as NOIR_DIR2, parseConfig, paths as paths2 } from "@noir-ai/core";
3892
3945
 
@@ -4991,7 +5044,7 @@ async function runOnce(prompt, opts, resumeSessionId) {
4991
5044
  const customBinary = opts.command ?? profile.binary;
4992
5045
  const extraArgs = resumeSessionId === void 0 ? profile.args : [...profile.args ?? [], "--resume", resumeSessionId];
4993
5046
  const env2 = profile.env ? mergeEnv(process.env, profile.env) : void 0;
4994
- if (!existsSync3(join4(root, NOIR_DIR2, ".env"))) {
5047
+ if (!existsSync4(join5(root, NOIR_DIR2, ".env"))) {
4995
5048
  log(
4996
5049
  `This project has no ${NOIR_DIR2}/.env \u2014 that is where Noir reads project credentials and run config from. Run \`noir init\` to scaffold one (\`noir run\` works without it).`,
4997
5050
  opts
@@ -5154,7 +5207,7 @@ function safeTranscript(host, lines) {
5154
5207
  function writeTranscript(host, lines, root = process.cwd()) {
5155
5208
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5156
5209
  const dir = paths2.transcriptsDir(root);
5157
- const file = join4(dir, `${host}-${ts}.jsonl`);
5210
+ const file = join5(dir, `${host}-${ts}.jsonl`);
5158
5211
  try {
5159
5212
  mkdirSync2(dir, { recursive: true, mode: 448 });
5160
5213
  writeFileSync4(file, `${lines.join("\n")}${lines.length > 0 ? "\n" : ""}`, { mode: 384 });
@@ -5175,7 +5228,7 @@ import {
5175
5228
  } from "@noir-ai/skills";
5176
5229
 
5177
5230
  // src/init.ts
5178
- import { existsSync as existsSync4 } from "fs";
5231
+ import { existsSync as existsSync5 } from "fs";
5179
5232
  import { resolveAdapter as resolveAdapter3 } from "@noir-ai/adapters";
5180
5233
  import { loadProjectInfo as loadProjectInfo11, paths as paths3 } from "@noir-ai/core";
5181
5234
  import { scaffold } from "@noir-ai/create";
@@ -5285,8 +5338,8 @@ function labelFor(resolution) {
5285
5338
  }
5286
5339
 
5287
5340
  // src/dedup-write.ts
5288
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync5 } from "fs";
5289
- import { join as join5 } from "path";
5341
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync5 } from "fs";
5342
+ import { join as join6 } from "path";
5290
5343
  import { sha256Hex as sha256Hex2, sha256Hex12 } from "@noir-ai/core";
5291
5344
  var HOST_CONTEXT_RELS = /* @__PURE__ */ new Set(["CLAUDE.md", "AGENTS.md", "GEMINI.md", ".noir/rules/RULES.md"]);
5292
5345
  var ACTION_THRESHOLD = 0.95;
@@ -5302,10 +5355,10 @@ async function checkWritePathDedup(root, res, opts) {
5302
5355
  if (embed === void 0) return result;
5303
5356
  const cachedEmbed = makeCachedEmbed(cache, embed);
5304
5357
  for (const proposedRel of writtenHostFiles) {
5305
- const proposedAbs = join5(root, proposedRel);
5358
+ const proposedAbs = join6(root, proposedRel);
5306
5359
  let proposedText;
5307
5360
  try {
5308
- proposedText = readFileSync5(proposedAbs, "utf8");
5361
+ proposedText = readFileSync6(proposedAbs, "utf8");
5309
5362
  } catch {
5310
5363
  continue;
5311
5364
  }
@@ -5349,7 +5402,7 @@ async function checkWritePathDedup(root, res, opts) {
5349
5402
  `
5350
5403
  );
5351
5404
  }
5352
- const matchedText = safeRead(join5(root, matchedRel));
5405
+ const matchedText = safeRead(join6(root, matchedRel));
5353
5406
  result.conflicts.push({
5354
5407
  path: proposedRel,
5355
5408
  mode: "artifact",
@@ -5433,21 +5486,21 @@ async function promptDedupAction(root, proposedRel, matchedRel, proposedText, si
5433
5486
  if (clack.isCancel(choice)) return "preserve";
5434
5487
  if (choice === "skip") {
5435
5488
  try {
5436
- rmSync(join5(root, proposedRel), { force: true });
5489
+ rmSync(join6(root, proposedRel), { force: true });
5437
5490
  } catch {
5438
5491
  }
5439
5492
  return "preserve";
5440
5493
  } else if (choice === "replace") {
5441
5494
  try {
5442
- writeFileSync5(join5(root, matchedRel), proposedText, "utf8");
5495
+ writeFileSync5(join6(root, matchedRel), proposedText, "utf8");
5443
5496
  } catch {
5444
5497
  }
5445
5498
  return "replace";
5446
5499
  } else if (choice === "mirror") {
5447
5500
  try {
5448
- const existing = readFileSync5(join5(root, matchedRel), "utf8");
5501
+ const existing = readFileSync6(join6(root, matchedRel), "utf8");
5449
5502
  writeFileSync5(
5450
- join5(root, matchedRel),
5503
+ join6(root, matchedRel),
5451
5504
  `${existing}
5452
5505
 
5453
5506
  <!-- mirrored from ${proposedRel} -->
@@ -5465,11 +5518,11 @@ function emptyCache() {
5465
5518
  return { version: DEDUP_CACHE_VERSION, model: "", entries: {} };
5466
5519
  }
5467
5520
  function cachePath(root) {
5468
- return join5(root, ".noir", "dedup-cache.json");
5521
+ return join6(root, ".noir", "dedup-cache.json");
5469
5522
  }
5470
5523
  function loadCache(root) {
5471
5524
  try {
5472
- const raw = readFileSync5(cachePath(root), "utf8");
5525
+ const raw = readFileSync6(cachePath(root), "utf8");
5473
5526
  const parsed = JSON.parse(raw);
5474
5527
  if (parsed.version !== DEDUP_CACHE_VERSION) return emptyCache();
5475
5528
  if (typeof parsed.entries !== "object" || parsed.entries === null) return emptyCache();
@@ -5484,7 +5537,7 @@ function loadCache(root) {
5484
5537
  }
5485
5538
  function saveCache(root, cache) {
5486
5539
  try {
5487
- mkdirSync3(join5(root, ".noir"), { recursive: true });
5540
+ mkdirSync3(join6(root, ".noir"), { recursive: true });
5488
5541
  writeFileSync5(cachePath(root), JSON.stringify(cache), "utf8");
5489
5542
  } catch {
5490
5543
  }
@@ -5501,7 +5554,7 @@ function makeCachedEmbed(cache, embed) {
5501
5554
  }
5502
5555
  function safeRead(abs) {
5503
5556
  try {
5504
- return readFileSync5(abs, "utf8");
5557
+ return readFileSync6(abs, "utf8");
5505
5558
  } catch {
5506
5559
  return void 0;
5507
5560
  }
@@ -5584,7 +5637,7 @@ async function init(root, opts) {
5584
5637
  }
5585
5638
  function resolveInitHost(root, opts) {
5586
5639
  if (opts.host !== void 0) return opts.host;
5587
- if (!existsSync4(paths3.config(root))) return "claude";
5640
+ if (!existsSync5(paths3.config(root))) return "claude";
5588
5641
  try {
5589
5642
  return loadProjectInfo11(root).config.host;
5590
5643
  } catch {
@@ -6092,7 +6145,7 @@ function createProgram() {
6092
6145
  const force = opts.force === true;
6093
6146
  const host = parseHost(opts.host);
6094
6147
  const dryRun = opts.dryRun === true || opts.preview === true;
6095
- const { create } = await import("./create-4XGNRRP7.js");
6148
+ const { create } = await import("./create-GUXMIKZB.js");
6096
6149
  const result = await create(dir, {
6097
6150
  transport,
6098
6151
  url: opts.url,
@@ -6131,7 +6184,7 @@ function createProgram() {
6131
6184
  ).choices(SUPPORTED_HOSTS2)
6132
6185
  ).action(
6133
6186
  async (opts, cmd) => {
6134
- const { sync } = await import("./sync-Q3KXM5RQ.js");
6187
+ const { sync } = await import("./sync-PXMB4SJT.js");
6135
6188
  const host = parseHost(opts.host);
6136
6189
  const force = opts.force === true;
6137
6190
  const merge = opts.merge === true;
@@ -6278,6 +6331,9 @@ function createProgram() {
6278
6331
  program2.command("env").description("which configuration is in effect and where each value comes from").action(async (...args) => {
6279
6332
  await env(toCliOptions(actionGlobals(args)));
6280
6333
  });
6334
+ program2.command("hook", { hidden: true }).description("host hook runner (SessionStart / SubagentStart context injection)").action(async () => {
6335
+ await hook();
6336
+ });
6281
6337
  const contextGrp = program2.command("context").description("context engine");
6282
6338
  contextGrp.command("search").description("hybrid search over the indexed context").argument("<query>", "search query").option("--limit <n>", "max results", "10").action(async (...args) => {
6283
6339
  const cmd = trailingCmd(args);
@@ -7257,4 +7313,4 @@ export {
7257
7313
  assertTransportUrl,
7258
7314
  buildPaletteCommandsForTui
7259
7315
  };
7260
- //# sourceMappingURL=chunk-NZMM6YXE.js.map
7316
+ //# sourceMappingURL=chunk-A5XEQWRN.js.map