@rness/cli 0.6.1 → 0.6.2

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/README.md CHANGED
@@ -147,6 +147,24 @@ its content does.
147
147
  Exit codes: 0 success, 1 failure, 2 usage — or a refusal without a TTY.
148
148
  `RNESS_DEBUG=1` adds stack traces; `RNESS_NO_DELEGATE=1` skips the delegation.
149
149
 
150
+ ## 0.6.2 — no dialog for the root CLAUDE.md
151
+
152
+ - Opening Claude Code in a repository of the workspace no longer asks "Allow
153
+ external CLAUDE.md file imports?". The workspace root `CLAUDE.md` carried
154
+ `@AGENTS.md`; Claude Code loads that file from every repository below it,
155
+ where the import points outside the working directory. `rness sync` now
156
+ writes the global block into the root `CLAUDE.md` itself; a line that is
157
+ exactly `@AGENTS.md` is dropped there, the rest of the file is kept. The
158
+ `CLAUDE.md` of each repository keeps its `@AGENTS.md`.
159
+ - The root files are not in any repository: run `rness sync` once after the
160
+ upgrade, on every machine. The blocks in the repositories are unchanged.
161
+ - `sync --check` and `validate` cover the root `CLAUDE.md`. At the root, a
162
+ symlink between `CLAUDE.md` and `AGENTS.md` is left as it is and the other
163
+ file is still written.
164
+ - The scaffold's `validate.yml` uses `actions/checkout@v7` and
165
+ `actions/setup-node@v7`, as the rness repositories do. A workspace created
166
+ before keeps its own file.
167
+
150
168
  ## 0.6.1 — hints you can type
151
169
 
152
170
  - Every hint that names a command to run (`not cloned: …`, `run … sync`,
package/dist/bin/rness.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  findDelegate,
7
7
  reportError,
8
8
  run
9
- } from "../chunk-YAIZOFYV.js";
9
+ } from "../chunk-7TOHFPW6.js";
10
10
 
11
11
  // src/bin/rness.ts
12
12
  import { pathToFileURL } from "url";
@@ -1637,6 +1637,17 @@ function mergeBlock(existing, block) {
1637
1637
  }
1638
1638
  return { ok: true, text, changed: text !== existing };
1639
1639
  }
1640
+ var POINTER = "@AGENTS.md";
1641
+ function claudeMdAtRoot(existing, block) {
1642
+ if (existing === null) return mergeBlock(null, block);
1643
+ const lines = existing.split(/\r?\n/);
1644
+ if (!lines.some((l) => l.trim() === POINTER))
1645
+ return mergeBlock(existing, block);
1646
+ const eol = existing.includes("\r\n") ? "\r\n" : "\n";
1647
+ const rest = lines.filter((l) => l.trim() !== POINTER).join(eol);
1648
+ const merged = mergeBlock(rest.trim() === "" ? null : rest, block);
1649
+ return merged.ok ? { ...merged, changed: merged.text !== existing } : merged;
1650
+ }
1640
1651
  async function ensureClaudeMd(dir) {
1641
1652
  const file = join8(dir, "CLAUDE.md");
1642
1653
  if (await isSymlink(file)) return "unchanged";
@@ -1849,6 +1860,18 @@ function targetsOf(manifest, root, only) {
1849
1860
  }
1850
1861
  return targets;
1851
1862
  }
1863
+ async function syncBlockFile(file, block, check, rootClaudeMd = false) {
1864
+ const existing = await readOrNull(file);
1865
+ const existingLines = existing === null ? [] : existing.split(/\r?\n/);
1866
+ const span = findBlock(existingLines);
1867
+ const current = span.kind === "one" && isCurrentBlock(existingLines.slice(span.begin, span.end + 1), block.hash) && !(rootClaudeMd && existingLines.some((l) => l.trim() === "@AGENTS.md"));
1868
+ const merged = current ? { ok: true, text: existing ?? "", changed: false } : rootClaudeMd ? claudeMdAtRoot(existing, block.text) : mergeBlock(existing, block.text);
1869
+ if (!merged.ok) return { error: merged.error };
1870
+ if (!merged.changed) return "unchanged";
1871
+ if (check) return "stale";
1872
+ await writeFileAtomic(file, merged.text);
1873
+ return "updated";
1874
+ }
1852
1875
  var SELECT_ALL = "*";
1853
1876
  async function askWhatToSync(input) {
1854
1877
  if (!input.terminal.isTty()) {
@@ -2008,10 +2031,11 @@ async function syncCommand(opts, deps = {}) {
2008
2031
  lines.push(["skipped", `${t.label} (directory not present)`]);
2009
2032
  continue;
2010
2033
  }
2011
- const linked = await Promise.all(
2034
+ const [agentsLinked, claudeLinked] = await Promise.all(
2012
2035
  MANAGED_FILES.map((f) => isSymlink(join9(t.dir, f)))
2013
2036
  );
2014
- if (linked.includes(true)) {
2037
+ const atRoot = t.scope === null;
2038
+ if (!atRoot && (agentsLinked === true || claudeLinked === true)) {
2015
2039
  lines.push(["skipped", `${t.label} (symlink)`]);
2016
2040
  continue;
2017
2041
  }
@@ -2031,32 +2055,32 @@ async function syncCommand(opts, deps = {}) {
2031
2055
  `${t.label}: block is ${Math.round(block.bytes / 1024)} KB (over 32 KB)`
2032
2056
  );
2033
2057
  }
2034
- const file = join9(t.dir, "AGENTS.md");
2035
- const existing = await readOrNull(file);
2036
- const existingLines = existing === null ? [] : existing.split(/\r?\n/);
2037
- const span = findBlock(existingLines);
2038
- const current = span.kind === "one" && isCurrentBlock(
2039
- existingLines.slice(span.begin, span.end + 1),
2040
- block.hash
2041
- );
2042
- const merged = current ? { ok: true, text: existing ?? "", changed: false } : mergeBlock(existing, block.text);
2043
- if (!merged.ok) {
2044
- problems.push(`${t.label}: ${merged.error}`);
2058
+ const agents = agentsLinked === true ? "skipped" : await syncBlockFile(join9(t.dir, "AGENTS.md"), block, check);
2059
+ if (typeof agents === "object") {
2060
+ problems.push(`${t.label}: ${agents.error}`);
2045
2061
  continue;
2046
2062
  }
2047
- if (!merged.changed) {
2048
- lines.push(["unchanged", `${t.label}`]);
2063
+ lines.push(
2064
+ agents === "skipped" ? ["skipped", `${t.label} (symlink)`] : [agents, t.label]
2065
+ );
2066
+ if (agents === "stale") differences += 1;
2067
+ if (!atRoot) {
2049
2068
  if (!check) await ensureClaudeMd(t.dir);
2050
2069
  continue;
2051
2070
  }
2052
- if (check) {
2053
- lines.push(["stale", `${t.label}`]);
2054
- differences += 1;
2071
+ if (claudeLinked === true) continue;
2072
+ const claude = await syncBlockFile(
2073
+ join9(t.dir, "CLAUDE.md"),
2074
+ block,
2075
+ check,
2076
+ true
2077
+ );
2078
+ if (typeof claude === "object") {
2079
+ problems.push(`CLAUDE.md: ${claude.error}`);
2055
2080
  continue;
2056
2081
  }
2057
- await writeFileAtomic(file, merged.text);
2058
- await ensureClaudeMd(t.dir);
2059
- lines.push(["updated", `${t.label}`]);
2082
+ if (claude === "stale") differences += 1;
2083
+ if (claude !== agents) lines.push([claude, "CLAUDE.md"]);
2060
2084
  }
2061
2085
  if (check && differences > 0)
2062
2086
  problems.push(
@@ -2301,7 +2325,7 @@ async function contextCommand(opts) {
2301
2325
  }
2302
2326
 
2303
2327
  // src/version.ts
2304
- var VERSION = "0.6.1";
2328
+ var VERSION = "0.6.2";
2305
2329
 
2306
2330
  // src/core/delegate.ts
2307
2331
  import { readFile as readFile6 } from "fs/promises";
@@ -3872,13 +3896,17 @@ async function checkBlocks({
3872
3896
  checks
3873
3897
  });
3874
3898
  if (!await exists(join19(root, "org"))) return derive();
3875
- const targets = [{ scope: null, dir: root, depth: 0, label: "AGENTS.md" }];
3899
+ const targets = [
3900
+ { scope: null, dir: root, depth: 0, file: "AGENTS.md", label: "AGENTS.md" },
3901
+ { scope: null, dir: root, depth: 0, file: "CLAUDE.md", label: "CLAUDE.md" }
3902
+ ];
3876
3903
  for (const [name, entry] of Object.entries(manifest.scopes)) {
3877
3904
  const segments = entry.path.split("/");
3878
3905
  targets.push({
3879
3906
  scope: name,
3880
3907
  dir: join19(root, ...segments),
3881
3908
  depth: segments.length,
3909
+ file: "AGENTS.md",
3882
3910
  label: `${entry.path}/AGENTS.md`
3883
3911
  });
3884
3912
  }
@@ -3891,7 +3919,7 @@ async function checkBlocks({
3891
3919
  );
3892
3920
  continue;
3893
3921
  }
3894
- const text = await readOrNull(join19(t.dir, "AGENTS.md"));
3922
+ const text = await readOrNull(join19(t.dir, t.file));
3895
3923
  const lines = text === null ? [] : text.split(/\r?\n/);
3896
3924
  const found = findBlock(lines);
3897
3925
  if (found.kind === "none") {
@@ -3917,7 +3945,8 @@ async function checkBlocks({
3917
3945
  depth: t.depth,
3918
3946
  context
3919
3947
  });
3920
- if (isCurrentBlock(lines.slice(found.begin, found.end + 1), fresh2.hash))
3948
+ const pointerLeft = t.file === "CLAUDE.md" && lines.some((l) => l.trim() === "@AGENTS.md");
3949
+ if (isCurrentBlock(lines.slice(found.begin, found.end + 1), fresh2.hash) && !pointerLeft)
3921
3950
  note(t.label, "current");
3922
3951
  else
3923
3952
  note(
@@ -4012,7 +4041,13 @@ async function validateCommand(opts, deps = {}) {
4012
4041
  return 0;
4013
4042
  }
4014
4043
  ui.intro(`Validate ${org}`);
4015
- for (const c of blocks.checks)
4044
+ const rootStatus = blocks.checks.find(
4045
+ (c) => c.label === "AGENTS.md"
4046
+ )?.status;
4047
+ const shown = blocks.checks.filter(
4048
+ (c) => !(c.label === "CLAUDE.md" && c.status === rootStatus)
4049
+ );
4050
+ for (const c of shown)
4016
4051
  if (c.message === null) ui.line("current", c.label);
4017
4052
  else if (c.status === "stale" || c.status === "malformed")
4018
4053
  ui.error(c.message);
@@ -4292,4 +4327,4 @@ export {
4292
4327
  OWN_COMMANDS,
4293
4328
  catchUp
4294
4329
  };
4295
- //# sourceMappingURL=chunk-YAIZOFYV.js.map
4330
+ //# sourceMappingURL=chunk-7TOHFPW6.js.map