@open-tomato/rafa 0.11.0 → 0.12.0

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.
@@ -4,7 +4,7 @@ import {
4
4
  openNdjsonStore,
5
5
  openSqliteStore,
6
6
  selectEffortStore
7
- } from "../../index-srdtw7w1.js";
7
+ } from "../../index-4g6vyznd.js";
8
8
  import {
9
9
  STORE_BACKENDS
10
10
  } from "../../index-96pz9023.js";
@@ -1006,16 +1006,17 @@ async function teeToOperator(stream) {
1006
1006
  }
1007
1007
  return text + tail;
1008
1008
  }
1009
- async function spawnClaudeCaptured(args, prompt) {
1009
+ async function spawnClaudeCaptured(args, prompt, options = {}) {
1010
1010
  guardSpend();
1011
- return spawnCaptured(args, prompt);
1011
+ return spawnCaptured(args, prompt, options);
1012
1012
  }
1013
- async function spawnCaptured(args, prompt) {
1013
+ async function spawnCaptured(args, prompt, options = {}) {
1014
1014
  const proc = Bun.spawn([CLAUDE_BIN, ...args], {
1015
1015
  stdin: new TextEncoder().encode(prompt),
1016
1016
  stdout: "pipe",
1017
1017
  stderr: "inherit",
1018
- env: claudeSessionEnv()
1018
+ env: claudeSessionEnv(),
1019
+ ...options.cwd === undefined ? {} : { cwd: options.cwd }
1019
1020
  });
1020
1021
  liveSessions.add(proc);
1021
1022
  let stdout;
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ import {
50
50
  sqliteStorePath,
51
51
  withSqliteStore,
52
52
  writeSqliteStore
53
- } from "./index-srdtw7w1.js";
53
+ } from "./index-4g6vyznd.js";
54
54
  import {
55
55
  BUDGET_FLAG,
56
56
  CLAUDE_SETTING_SOURCES,
@@ -337,6 +337,13 @@ var PROMPT_SHAPES = [
337
337
  prefix: "The pull request for branch ",
338
338
  firstLineInfix: " is not mergeable: ",
339
339
  source: "src/start/pr-lifecycle.ts"
340
+ },
341
+ {
342
+ kind: "search",
343
+ label: "search",
344
+ prefix: "# Inventory search instructions",
345
+ firstLineInfix: null,
346
+ source: "src/inventory/search/prompt.ts"
340
347
  }
341
348
  ];
342
349
  var ENQUEUE_SCAN_WINDOW = 10;
@@ -1895,7 +1902,7 @@ async function report(args, repoRoot) {
1895
1902
  }
1896
1903
  }
1897
1904
  // package.json
1898
- var version = "0.11.0";
1905
+ var version = "0.12.0";
1899
1906
 
1900
1907
  // src/modules/manifest.ts
1901
1908
  var MANIFEST_VERSION = 1;
@@ -2378,8 +2385,7 @@ function definitionName(text) {
2378
2385
  const name = frontmatter["name"];
2379
2386
  return typeof name === "string" && name.length > 0 ? name : null;
2380
2387
  }
2381
- function readAgentDefinitions(root) {
2382
- const dir = join3(root, AGENT_DEFINITION_DIR);
2388
+ function readAgentDirectory(dir) {
2383
2389
  const found = [];
2384
2390
  for (const file of markdownFiles(dir)) {
2385
2391
  const path = join3(dir, file);
@@ -2393,6 +2399,9 @@ function readAgentDefinitions(root) {
2393
2399
  }
2394
2400
  return found.sort((a, b) => a.name.localeCompare(b.name));
2395
2401
  }
2402
+ function readAgentDefinitions(root) {
2403
+ return readAgentDirectory(join3(root, AGENT_DEFINITION_DIR));
2404
+ }
2396
2405
  function firstByName(files) {
2397
2406
  const byName = new Map;
2398
2407
  for (const file of files) {
@@ -4726,112 +4735,8 @@ async function resolvePlanSpec(options) {
4726
4735
  });
4727
4736
  }
4728
4737
 
4729
- // src/project/root-choice.ts
4738
+ // src/cli/prompt/confirm.ts
4730
4739
  import { createInterface as createInterface2 } from "readline";
4731
-
4732
- // src/project/roots.ts
4733
- import { accessSync, constants, readFileSync as readFileSync8, statSync as statSync7 } from "fs";
4734
-
4735
- // src/project/scope.ts
4736
- import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
4737
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
4738
- var SCOPE_DIR = dirname3(CONFIG_FILE);
4739
- var INIT_COMMAND = "rafa init";
4740
- var DISK_FILE_SYSTEM = Object.freeze({
4741
- exists: (path3) => existsSync9(path3),
4742
- realpath: (path3) => realpathSync2(path3)
4743
- });
4744
-
4745
- class ScopeError extends Error {
4746
- constructor(message, options) {
4747
- super(`rafa scope: ${message}`, options);
4748
- this.name = "ScopeError";
4749
- }
4750
- }
4751
- function scopeAt(base) {
4752
- return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
4753
- }
4754
- function initHint(start, passedUserConfig = null) {
4755
- const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
4756
- return [
4757
- `not inside a rafa project: no ${CONFIG_FILE} in ${start} or any directory above it`,
4758
- ...passed2,
4759
- `run \`${INIT_COMMAND}\` to set one up`
4760
- ].join(`
4761
- `);
4762
- }
4763
- function requireAbsolute(label, path3) {
4764
- if (!isAbsolute2(path3)) {
4765
- throw new ScopeError(`${label} is ${describeValue(path3)}, expected an absolute path`);
4766
- }
4767
- }
4768
- function realStartOf(start, fs2) {
4769
- try {
4770
- return fs2.realpath(start);
4771
- } catch (error) {
4772
- const message = `start directory ${start} does not resolve (${messageOf(error)})`;
4773
- throw new ScopeError(message, { cause: error });
4774
- }
4775
- }
4776
- function realHomeOf(home, fs2) {
4777
- try {
4778
- return fs2.realpath(home);
4779
- } catch {
4780
- return null;
4781
- }
4782
- }
4783
- function selfAndAncestors(path3) {
4784
- const parent = dirname3(path3);
4785
- return parent === path3 ? [path3] : [path3, ...selfAndAncestors(parent)];
4786
- }
4787
- function walkUp(realStart, realHome, fs2) {
4788
- let passedUserConfig = false;
4789
- for (const dir of selfAndAncestors(realStart)) {
4790
- if (!fs2.exists(configFilePath(dir)))
4791
- continue;
4792
- if (dir !== realHome)
4793
- return { root: dir, passedUserConfig };
4794
- passedUserConfig = true;
4795
- }
4796
- return { root: null, passedUserConfig };
4797
- }
4798
- function resolveScope(start, seams) {
4799
- const { home, fs: fs2 = DISK_FILE_SYSTEM } = seams;
4800
- requireAbsolute("start directory", start);
4801
- requireAbsolute("home directory", home);
4802
- const user = scopeAt(home);
4803
- const walk = walkUp(realStartOf(start, fs2), realHomeOf(home, fs2), fs2);
4804
- if (walk.root === null) {
4805
- const passed2 = walk.passedUserConfig ? user.configFile : null;
4806
- return { found: false, start, home, user, hint: initHint(start, passed2) };
4807
- }
4808
- return { found: true, root: walk.root, home, project: scopeAt(walk.root), user };
4809
- }
4810
-
4811
- // src/project/roots.ts
4812
- var REFUSED_ROOTS = Object.freeze([
4813
- { path: "/", why: "is the filesystem root" },
4814
- { path: "/var", why: "holds system state" },
4815
- { path: "/etc", why: "holds system configuration" },
4816
- { path: "/usr", why: "holds system programs" },
4817
- { path: "/home", why: "holds the home directories of every user" },
4818
- { path: "/Users", why: "holds the home directories of every user" }
4819
- ]);
4820
- var HOME_WHY = `is the home directory, whose ${SCOPE_DIR}/ is the user scope`;
4821
- var MONOREPO_MARKER_FILES = Object.freeze([
4822
- "turbo.json",
4823
- "pnpm-workspace.yaml",
4824
- "nx.json",
4825
- "lerna.json"
4826
- ]);
4827
- var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
4828
- ...DISK_FILE_SYSTEM,
4829
- readFile: (path3) => readFileSync8(path3, "utf8"),
4830
- isDirectory: (path3) => statSync7(path3).isDirectory(),
4831
- assertWritable: (path3) => accessSync(path3, constants.W_OK)
4832
- });
4833
-
4834
- // src/project/root-choice.ts
4835
4740
  function createLinePrompter(input, output) {
4836
4741
  const buffered = [];
4837
4742
  const waiting = [];
@@ -5639,12 +5544,90 @@ import {
5639
5544
  linkSync as linkSync2,
5640
5545
  mkdirSync as mkdirSync4,
5641
5546
  readdirSync as readdirSync4,
5642
- readFileSync as readFileSync9,
5547
+ readFileSync as readFileSync8,
5643
5548
  renameSync as renameSync2,
5644
5549
  rmSync,
5645
5550
  writeFileSync as writeFileSync3
5646
5551
  } from "fs";
5647
5552
  import { basename as basename4, dirname as dirname4, join as join7 } from "path";
5553
+
5554
+ // src/project/scope.ts
5555
+ import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
5556
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
5557
+ var SCOPE_DIR = dirname3(CONFIG_FILE);
5558
+ var INIT_COMMAND = "rafa init";
5559
+ var DISK_FILE_SYSTEM = Object.freeze({
5560
+ exists: (path3) => existsSync9(path3),
5561
+ realpath: (path3) => realpathSync2(path3)
5562
+ });
5563
+
5564
+ class ScopeError extends Error {
5565
+ constructor(message, options) {
5566
+ super(`rafa scope: ${message}`, options);
5567
+ this.name = "ScopeError";
5568
+ }
5569
+ }
5570
+ function scopeAt(base) {
5571
+ return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
5572
+ }
5573
+ function initHint(start, passedUserConfig = null) {
5574
+ const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
5575
+ return [
5576
+ `not inside a rafa project: no ${CONFIG_FILE} in ${start} or any directory above it`,
5577
+ ...passed2,
5578
+ `run \`${INIT_COMMAND}\` to set one up`
5579
+ ].join(`
5580
+ `);
5581
+ }
5582
+ function requireAbsolute(label, path3) {
5583
+ if (!isAbsolute2(path3)) {
5584
+ throw new ScopeError(`${label} is ${describeValue(path3)}, expected an absolute path`);
5585
+ }
5586
+ }
5587
+ function realStartOf(start, fs2) {
5588
+ try {
5589
+ return fs2.realpath(start);
5590
+ } catch (error) {
5591
+ const message = `start directory ${start} does not resolve (${messageOf(error)})`;
5592
+ throw new ScopeError(message, { cause: error });
5593
+ }
5594
+ }
5595
+ function realHomeOf(home, fs2) {
5596
+ try {
5597
+ return fs2.realpath(home);
5598
+ } catch {
5599
+ return null;
5600
+ }
5601
+ }
5602
+ function selfAndAncestors(path3) {
5603
+ const parent = dirname3(path3);
5604
+ return parent === path3 ? [path3] : [path3, ...selfAndAncestors(parent)];
5605
+ }
5606
+ function walkUp(realStart, realHome, fs2) {
5607
+ let passedUserConfig = false;
5608
+ for (const dir of selfAndAncestors(realStart)) {
5609
+ if (!fs2.exists(configFilePath(dir)))
5610
+ continue;
5611
+ if (dir !== realHome)
5612
+ return { root: dir, passedUserConfig };
5613
+ passedUserConfig = true;
5614
+ }
5615
+ return { root: null, passedUserConfig };
5616
+ }
5617
+ function resolveScope(start, seams) {
5618
+ const { home, fs: fs2 = DISK_FILE_SYSTEM } = seams;
5619
+ requireAbsolute("start directory", start);
5620
+ requireAbsolute("home directory", home);
5621
+ const user = scopeAt(home);
5622
+ const walk = walkUp(realStartOf(start, fs2), realHomeOf(home, fs2), fs2);
5623
+ if (walk.root === null) {
5624
+ const passed2 = walk.passedUserConfig ? user.configFile : null;
5625
+ return { found: false, start, home, user, hint: initHint(start, passed2) };
5626
+ }
5627
+ return { found: true, root: walk.root, home, project: scopeAt(walk.root), user };
5628
+ }
5629
+
5630
+ // src/loop/sessions.ts
5648
5631
  var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
5649
5632
  var RUNS_DIR = "runs";
5650
5633
  var RECORD_EXTENSION = ".json";
@@ -5792,7 +5775,7 @@ function readState2(record, isAlive = isPidAlive) {
5792
5775
  function readRecordFile(file) {
5793
5776
  let text;
5794
5777
  try {
5795
- text = readFileSync9(file, "utf8");
5778
+ text = readFileSync8(file, "utf8");
5796
5779
  } catch (error) {
5797
5780
  throw new SessionRecordError(file, `cannot be read: ${messageOf(error)}`);
5798
5781
  }
@@ -6005,15 +5988,15 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS) {
6005
5988
  }
6006
5989
 
6007
5990
  // src/commands/plan/list.ts
6008
- import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
5991
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
6009
5992
  import { join as join9 } from "path";
6010
5993
  var USAGE2 = "rafa plan list";
6011
5994
  function listing(dir, stub) {
6012
5995
  const plan = join9(dir, planFileName(stub, false));
6013
5996
  const trackerPath = join9(dir, planFileName(stub, true));
6014
5997
  const tracker = isFile(trackerPath) ? trackerPath : null;
6015
- const planModel = parsePlan(readFileSync10(plan, "utf8"));
6016
- const counted2 = tracker === null ? planModel : parsePlan(readFileSync10(tracker, "utf8"));
5998
+ const planModel = parsePlan(readFileSync9(plan, "utf8"));
5999
+ const counted2 = tracker === null ? planModel : parsePlan(readFileSync9(tracker, "utf8"));
6017
6000
  return { stub, plan, tracker, tasks: countTasks(counted2.tasks), issues: planModel.issues.length };
6018
6001
  }
6019
6002
  function listPlans(plans) {
@@ -7029,7 +7012,7 @@ import { homedir as homedir3 } from "os";
7029
7012
  var RAFA_VERSION = version;
7030
7013
 
7031
7014
  // src/notices/notices.ts
7032
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
7015
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
7033
7016
  import { dirname as dirname5, join as join10 } from "path";
7034
7017
  var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
7035
7018
  var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
@@ -7043,7 +7026,7 @@ function isNoticeId(value) {
7043
7026
  function readDismissed(home) {
7044
7027
  let raw;
7045
7028
  try {
7046
- raw = readFileSync11(noticesPath(home), "utf8");
7029
+ raw = readFileSync10(noticesPath(home), "utf8");
7047
7030
  } catch {
7048
7031
  return [];
7049
7032
  }
@@ -8260,16 +8243,16 @@ function writeProgress(repoRoot, planStub2) {
8260
8243
 
8261
8244
  // src/start/preflight.ts
8262
8245
  import { randomUUID as randomUUID6 } from "crypto";
8263
- import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
8246
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
8264
8247
  import { homedir as homedir5 } from "os";
8265
8248
  import { basename as basename5 } from "path";
8266
8249
 
8267
8250
  // src/preflight/first-dispatch.ts
8268
- import { readFileSync as readFileSync12 } from "fs";
8251
+ import { readFileSync as readFileSync11 } from "fs";
8269
8252
  var TICKED_TASK = /^- \[[xX]\] (.+)/;
8270
8253
  function readIfReadable(path5) {
8271
8254
  try {
8272
- return readFileSync12(path5, "utf8");
8255
+ return readFileSync11(path5, "utf8");
8273
8256
  } catch {
8274
8257
  return null;
8275
8258
  }
@@ -8306,7 +8289,7 @@ function agentSourcePath(planPath) {
8306
8289
  }
8307
8290
  function readIfReadable2(path5) {
8308
8291
  try {
8309
- return readFileSync13(path5, "utf8");
8292
+ return readFileSync12(path5, "utf8");
8310
8293
  } catch {
8311
8294
  return null;
8312
8295
  }
@@ -8800,7 +8783,7 @@ async function handleRedChecks(io, branch, prNumber, rows) {
8800
8783
  }
8801
8784
 
8802
8785
  // src/release/prepare.ts
8803
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
8786
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
8804
8787
 
8805
8788
  // src/release/changelog.ts
8806
8789
  var RELEASE_HEADING_FIELDS = ["version", "date", "title"];
@@ -8985,11 +8968,11 @@ function changelogDate(now) {
8985
8968
  }
8986
8969
 
8987
8970
  // src/release/enabled.ts
8988
- import { statSync as statSync8 } from "fs";
8971
+ import { statSync as statSync7 } from "fs";
8989
8972
  import { resolve as resolve9 } from "path";
8990
8973
  function isRegularFile(full) {
8991
8974
  try {
8992
- return statSync8(full).isFile();
8975
+ return statSync7(full).isFile();
8993
8976
  } catch {
8994
8977
  return false;
8995
8978
  }
@@ -9032,7 +9015,7 @@ function resolveReleaseLevel(declared, notes) {
9032
9015
  }
9033
9016
 
9034
9017
  // src/release/version.ts
9035
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
9018
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
9036
9019
  var RELEASE_REMOTE = "origin";
9037
9020
  var RELEASE_BASE_BRANCH = "main";
9038
9021
  var VERSION_SHAPE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
@@ -9124,7 +9107,7 @@ function writeManifestVersion(path5, next) {
9124
9107
  const unwritten = { path: path5, written: false, previous: null, version: next };
9125
9108
  let text;
9126
9109
  try {
9127
- text = readFileSync14(path5, "utf8");
9110
+ text = readFileSync13(path5, "utf8");
9128
9111
  } catch (error) {
9129
9112
  return { ...unwritten, problem: `${path5} could not be read: ${messageOf(error)}` };
9130
9113
  }
@@ -9227,7 +9210,7 @@ function planVersion(input, versionFile, level) {
9227
9210
  }
9228
9211
  function textOf(path5) {
9229
9212
  try {
9230
- return readFileSync15(path5, "utf8");
9213
+ return readFileSync14(path5, "utf8");
9231
9214
  } catch {
9232
9215
  return null;
9233
9216
  }
@@ -9340,7 +9323,7 @@ function changelogFailure(failed3, versionEdit, restored) {
9340
9323
  }
9341
9324
 
9342
9325
  // src/release/verify.ts
9343
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
9326
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
9344
9327
  function changelogInsertionSpan(before, after) {
9345
9328
  const beforeLines = before.split(`
9346
9329
  `);
@@ -9357,7 +9340,7 @@ function changelogInsertionSpan(before, after) {
9357
9340
  }
9358
9341
  function textOf2(path5) {
9359
9342
  try {
9360
- return readFileSync16(path5, "utf8");
9343
+ return readFileSync15(path5, "utf8");
9361
9344
  } catch {
9362
9345
  return null;
9363
9346
  }
@@ -9692,10 +9675,10 @@ async function finishRelease(input, seams = {}) {
9692
9675
  }
9693
9676
 
9694
9677
  // src/start/risk-total.ts
9695
- import { readFileSync as readFileSync18 } from "fs";
9678
+ import { readFileSync as readFileSync17 } from "fs";
9696
9679
 
9697
9680
  // src/plan/risk.ts
9698
- import { readFileSync as readFileSync17 } from "fs";
9681
+ import { readFileSync as readFileSync16 } from "fs";
9699
9682
  import { isAbsolute as isAbsolute3, join as join13, relative as relative2 } from "path";
9700
9683
 
9701
9684
  // src/schema/tiers.ts
@@ -10349,7 +10332,7 @@ function located(finding2, file, task) {
10349
10332
  }
10350
10333
  function readText4(path5) {
10351
10334
  try {
10352
- return readFileSync17(path5, "utf8");
10335
+ return readFileSync16(path5, "utf8");
10353
10336
  } catch {
10354
10337
  return null;
10355
10338
  }
@@ -10423,7 +10406,7 @@ function spawningRunners(root) {
10423
10406
  }
10424
10407
  async function readRiskTotalLine(input, seams = {}) {
10425
10408
  const makeRunners = seams.runners ?? spawningRunners;
10426
- const report2 = await assessPlanRisk({ path: input.planPath, text: readFileSync18(input.planPath, "utf8") }, {
10409
+ const report2 = await assessPlanRisk({ path: input.planPath, text: readFileSync17(input.planPath, "utf8") }, {
10427
10410
  repoRoot: input.repoRoot,
10428
10411
  home: input.home,
10429
10412
  settingSources: input.config.settingSources,
@@ -10595,7 +10578,7 @@ function announcePlanIssues(planContent) {
10595
10578
  }
10596
10579
 
10597
10580
  // src/start/runtime.ts
10598
- import { existsSync as existsSync17, readdirSync as readdirSync6, realpathSync as realpathSync4, statSync as statSync9 } from "fs";
10581
+ import { existsSync as existsSync17, readdirSync as readdirSync6, realpathSync as realpathSync4, statSync as statSync8 } from "fs";
10599
10582
  import { homedir as homedir6 } from "os";
10600
10583
  import { isAbsolute as isAbsolute4, join as join14, relative as relative4, resolve as resolve10, sep } from "path";
10601
10584
  var RUNTIME_FLAG = "--runtime";
@@ -10671,14 +10654,14 @@ function installedVersions(root) {
10671
10654
  }
10672
10655
  function isFile2(path5) {
10673
10656
  try {
10674
- return statSync9(path5).isFile();
10657
+ return statSync8(path5).isFile();
10675
10658
  } catch {
10676
10659
  return false;
10677
10660
  }
10678
10661
  }
10679
10662
  function isDirectory(path5) {
10680
10663
  try {
10681
- return statSync9(path5).isDirectory();
10664
+ return statSync8(path5).isDirectory();
10682
10665
  } catch {
10683
10666
  return false;
10684
10667
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-tomato/rafa",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Alpha. Runs a plan through the ralph loop: one Claude Code session per task, one commit per task, then a pull request and a CI wait.",
5
5
  "private": false,
6
6
  "publishConfig": {