@craftsamo/git-tools 0.2.1 → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-tools",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Git tools for coding agents: state, commits, rewrites and stacked branches, worktrees and pushing.",
5
5
  "author": {
6
6
  "name": "craftsamo",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-tools",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Git tools for coding agents: state, commits, rewrites and stacked branches, worktrees and pushing.",
5
5
  "author": {
6
6
  "name": "craftsamo",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-tools",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Git tools for coding agents: state, commits, rewrites and stacked branches, worktrees and pushing.",
5
5
  "author": {
6
6
  "name": "craftsamo",
package/README.md CHANGED
@@ -303,22 +303,26 @@ Where a write is asked about, and where it is not:
303
303
  Every entry point takes the same settings. A flag wins over its environment
304
304
  variable, and an option or setting wins over the variable too.
305
305
 
306
- | Setting | Flag | Variable | Meaning |
307
- | ---------------------- | ------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
308
- | `toolsets` | `--toolsets` | `GIT_TOOLS_TOOLSETS` | the toolsets that are on, or `all`; default those `src/project.ts` names |
309
- | `read_only` | `--read-only` | `GIT_TOOLS_READ_ONLY` | registers no tool that writes; wins over every other setting |
310
- | `tools` | `--tools` | `GIT_TOOLS_TOOLS` | single tools to add by ID, whatever their toolset |
311
- | `exclude_tools` | `--exclude-tools` | `GIT_TOOLS_EXCLUDE_TOOLS` | single tools to remove by ID; wins over `tools` and `toolsets` |
312
- | `confirm` | `--confirm` | `GIT_TOOLS_CONFIRM` | MCP server only: `harness` (default) or `elicitation`, as the conventions say |
313
- | `language` | `--language` | `GIT_TOOLS_LANGUAGE` | native plugins only: the approval prompt's language, such as `ja` |
314
- | `emoji` | — | — | Hermes only: the emoji shown beside each toolset, by toolset name (`""` for none) |
315
- | `published_text_rules` | `--published-text-rules` | `GIT_TOOLS_PUBLISHED_TEXT_RULES` | the absolute path (`~` allowed) of a JSON file of published-text rules (see the push toolset); unset, nothing is checked |
316
- | `worktree_root` | `--worktree-root` | `GIT_TOOLS_WORKTREE_ROOT` | the absolute path where task worktrees go (`~` allowed); unset, `~/worktrees` |
306
+ | Setting | Flag | Variable | Meaning |
307
+ | ---------------------- | ------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------- |
308
+ | `toolsets` | `--toolsets` | `GIT_TOOLS_TOOLSETS` | the toolsets that are on, or `all`; default those `src/project.ts` names |
309
+ | `read_only` | `--read-only` | `GIT_TOOLS_READ_ONLY` | registers no tool that writes; wins over every other setting |
310
+ | `tools` | `--tools` | `GIT_TOOLS_TOOLS` | single tools to add by ID, whatever their toolset |
311
+ | `exclude_tools` | `--exclude-tools` | `GIT_TOOLS_EXCLUDE_TOOLS` | single tools to remove by ID; wins over `tools` and `toolsets` |
312
+ | `confirm` | `--confirm` | `GIT_TOOLS_CONFIRM` | MCP server only: `harness` (default) or `elicitation`, as the conventions say |
313
+ | `language` | `--language` | `GIT_TOOLS_LANGUAGE` | native plugins only: the approval prompt's language, such as `ja` |
314
+ | `emoji` | — | — | Hermes only: the emoji shown beside each toolset, by toolset name (`""` for none) |
315
+ | `published_text_rules` | `--published-text-rules` | `GIT_TOOLS_PUBLISHED_TEXT_RULES` | the path of a JSON file of published-text rules (see the push toolset); unset, nothing is checked |
316
+ | `worktree_root` | `--worktree-root` | `GIT_TOOLS_WORKTREE_ROOT` | the path where task worktrees go; unset, `~/worktrees` |
317
317
 
318
318
  Lists are comma-separated in a flag or a variable. A package's own settings,
319
319
  listed in `options` in `src/project.ts`, take a row each here and are read the
320
- same way: `published_text_rules` and `worktree_root` hold text, trimmed and
321
- blank meaning unset, and the tools read them themselves. The layout is
320
+ same way. A `path` is trimmed; `~` and `~/…` (also with the platform separator)
321
+ stand for the home directory (`HOME` when it is absolute, else the account's).
322
+ It must then be absolute and reaches the tool normalized, without a filesystem
323
+ check. Blank paths count as unset. Refused values are `invalid_argument`, naming
324
+ the flag, variable or option and what it must be. Hermes passes paths to the
325
+ command line, which checks them. The layout is
322
326
  `<root>/<repository>/<branch>`, with each `/` of the branch written `-`; the
323
327
  repository is the last part of `origin`'s URL without `.git`, else the folder
324
328
  name of the main checkout. Where each harness reads them:
@@ -339,7 +343,7 @@ name of the main checkout. Where each harness reads them:
339
343
  settings:
340
344
  read_only: false
341
345
  exclude_tools: []
342
- published_text_rules: ~/.config/git-tools/published-text.json
346
+ published_text_rules: /home/me/.config/git-tools/published-text.json
343
347
  worktree_root: ~/worktrees
344
348
  ```
345
349
 
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ var PROJECT = {
27
27
  /** The toolsets on when a setting names none. */
28
28
  defaultToolsets: ["repo", "commit", "rebase"],
29
29
  /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
30
- options: { published_text_rules: "text", worktree_root: "text" },
30
+ options: { published_text_rules: "path", worktree_root: "path" },
31
31
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
32
32
  toolVariables: [
33
33
  "LANGUAGE",
@@ -5135,7 +5135,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5135
5135
  var NONE = 0;
5136
5136
  var ASSUMED = 1;
5137
5137
  var PROVEN = 2;
5138
- function isRecursive(inst, stack, resolve5) {
5138
+ function isRecursive(inst, stack, resolve3) {
5139
5139
  const cached2 = recursive.get(inst);
5140
5140
  if (cached2 !== void 0)
5141
5141
  return cached2 ? PROVEN : NONE;
@@ -5145,7 +5145,7 @@ function isRecursive(inst, stack, resolve5) {
5145
5145
  let result = NONE;
5146
5146
  const check2 = (child) => {
5147
5147
  if (result !== PROVEN && child?._zod) {
5148
- const answer = isRecursive(child, stack, resolve5);
5148
+ const answer = isRecursive(child, stack, resolve3);
5149
5149
  if (answer > result)
5150
5150
  result = answer;
5151
5151
  }
@@ -5156,7 +5156,7 @@ function isRecursive(inst, stack, resolve5) {
5156
5156
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5157
5157
  if (spread && !desc.enumerable)
5158
5158
  continue;
5159
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
5159
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
5160
5160
  if (child > answer)
5161
5161
  answer = child;
5162
5162
  }
@@ -5220,7 +5220,7 @@ function isRecursive(inst, stack, resolve5) {
5220
5220
  break;
5221
5221
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5222
5222
  case "lazy": {
5223
- const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
5223
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
5224
5224
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5225
5225
  break;
5226
5226
  }
@@ -19829,7 +19829,7 @@ function abortError(signal) {
19829
19829
  }
19830
19830
  function exec(argv, options) {
19831
19831
  const { signal } = options;
19832
- return new Promise((resolve5, reject) => {
19832
+ return new Promise((resolve3, reject) => {
19833
19833
  if (signal?.aborted) {
19834
19834
  reject(abortError(signal));
19835
19835
  return;
@@ -19888,7 +19888,7 @@ function exec(argv, options) {
19888
19888
  }
19889
19889
  const finish = () => settle2(() => {
19890
19890
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
19891
- resolve5({
19891
+ resolve3({
19892
19892
  code: code ?? -1,
19893
19893
  stdout,
19894
19894
  stderr,
@@ -22788,7 +22788,7 @@ async function compareBranch(root, remote, selected, expect, signal) {
22788
22788
 
22789
22789
  // src/core/push/published-text.ts
22790
22790
  import { readFile as readFile2, stat as stat6 } from "node:fs/promises";
22791
- import { isAbsolute as isAbsolute4, resolve as resolve2 } from "node:path";
22791
+ import { isAbsolute as isAbsolute4 } from "node:path";
22792
22792
 
22793
22793
  // src/core/describe.ts
22794
22794
  var MAX_FACTS = 8;
@@ -22925,19 +22925,6 @@ async function wholeCallDescription(spec, args, ctx) {
22925
22925
  return { ...headline2 && { headline: headline2 }, facts, ...text && { text }, ...kept2 };
22926
22926
  }
22927
22927
 
22928
- // src/core/home.ts
22929
- import { homedir } from "node:os";
22930
- import { join as join5 } from "node:path";
22931
- var HOME_MARK = "~";
22932
- var HOME_PREFIX = "~/";
22933
- function homeDirectory() {
22934
- return process.env.HOME?.trim() || homedir();
22935
- }
22936
- function expandHome(value) {
22937
- if (value === HOME_MARK) return homeDirectory();
22938
- return value.startsWith(HOME_PREFIX) ? join5(homeDirectory(), value.slice(HOME_PREFIX.length)) : value;
22939
- }
22940
-
22941
22928
  // src/core/push/published-text.ts
22942
22929
  var PUBLISHED_TEXT_RULES_OPTION = "published_text_rules";
22943
22930
  var MAX_FILE_BYTES = 64 * 1024;
@@ -23010,11 +22997,8 @@ async function loadPublishedTextRules(options, signal) {
23010
22997
  const option = options?.[PUBLISHED_TEXT_RULES_OPTION];
23011
22998
  const path = typeof option === "string" ? option.trim() : void 0;
23012
22999
  if (!path) return [];
23013
- const expanded = expandHome(path);
23014
- if (!isAbsolute4(expanded))
23015
- throw invalidRules(path, `the value must be an absolute path, or start with ${HOME_PREFIX}`);
23016
- const normalized = resolve2(expanded);
23017
- return compileRules(normalized, parseRulesJson(normalized, await readRulesFile(normalized, signal)));
23000
+ if (!isAbsolute4(path)) throw invalidRules(path, "the value must be an absolute path");
23001
+ return compileRules(path, parseRulesJson(path, await readRulesFile(path, signal)));
23018
23002
  }
23019
23003
  var publishedTextFindingSchema = external_exports.object({
23020
23004
  rule: external_exports.string().describe("The name of the rule that matched."),
@@ -23968,11 +23952,11 @@ var rebaseRead = defineTool({
23968
23952
  // src/core/rebase/resume.ts
23969
23953
  import { createHash as createHash3 } from "node:crypto";
23970
23954
  import { readFile as readFile5 } from "node:fs/promises";
23971
- import { join as join8 } from "node:path";
23955
+ import { join as join7 } from "node:path";
23972
23956
 
23973
23957
  // src/core/conflicts.ts
23974
23958
  import { readFile as readFile3 } from "node:fs/promises";
23975
- import { basename as basename2, join as join6 } from "node:path";
23959
+ import { basename as basename2, join as join5 } from "node:path";
23976
23960
  var REGION_MAX = 20;
23977
23961
  var SIDE_LINES_MAX = 40;
23978
23962
  var LOCKFILES = {
@@ -24084,7 +24068,7 @@ async function readConflictFile(root, path, xy, operation) {
24084
24068
  }
24085
24069
  let text;
24086
24070
  try {
24087
- text = await readFile3(join6(root, path), "utf8");
24071
+ text = await readFile3(join5(root, path), "utf8");
24088
24072
  } catch {
24089
24073
  return { ...file2, region_count: 0, regions: [] };
24090
24074
  }
@@ -24167,13 +24151,13 @@ var rebaseOutcomeSchema = external_exports.object({
24167
24151
 
24168
24152
  // src/core/rebase/rebase-state.ts
24169
24153
  import { readFile as readFile4 } from "node:fs/promises";
24170
- import { join as join7 } from "node:path";
24154
+ import { join as join6 } from "node:path";
24171
24155
  var HEADS_PREFIX = "refs/heads/";
24172
24156
  var STATE_DIRS = ["rebase-merge", "rebase-apply"];
24173
24157
  var UPDATE_REFS_LINES = 3;
24174
24158
  async function readStateFile(dir, name) {
24175
24159
  try {
24176
- return (await readFile4(join7(dir, name), "utf8")).trim();
24160
+ return (await readFile4(join6(dir, name), "utf8")).trim();
24177
24161
  } catch {
24178
24162
  return void 0;
24179
24163
  }
@@ -24348,7 +24332,7 @@ async function conflictedFiles(root, signal) {
24348
24332
  for (const path of await unmergedPaths(root, signal)) {
24349
24333
  let content;
24350
24334
  try {
24351
- content = await readFile5(join8(root, path));
24335
+ content = await readFile5(join7(root, path));
24352
24336
  } catch {
24353
24337
  content = null;
24354
24338
  }
@@ -25347,7 +25331,8 @@ var repoRead = defineTool({
25347
25331
  import { lstat } from "node:fs/promises";
25348
25332
 
25349
25333
  // src/core/worktree/layout.ts
25350
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute5, join as join9, resolve as resolve3 } from "node:path";
25334
+ import { homedir } from "node:os";
25335
+ import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute5, join as join8 } from "node:path";
25351
25336
  var WORKTREE_ROOT_OPTION = "worktree_root";
25352
25337
  var DEFAULT_ROOT_FOLDER = "worktrees";
25353
25338
  var GIT_DIRECTORY = ".git";
@@ -25358,15 +25343,14 @@ var NOT_A_NAME = [".", ".."];
25358
25343
  function worktreeRoot(ctx) {
25359
25344
  const option = ctx.options?.[WORKTREE_ROOT_OPTION];
25360
25345
  const value = typeof option === "string" ? option.trim() : void 0;
25361
- if (!value) return join9(homeDirectory(), DEFAULT_ROOT_FOLDER);
25362
- const expanded = expandHome(value);
25363
- if (!isAbsolute5(expanded)) {
25346
+ if (!value) return join8(process.env.HOME?.trim() || homedir(), DEFAULT_ROOT_FOLDER);
25347
+ if (!isAbsolute5(value)) {
25364
25348
  throw new ToolError(
25365
25349
  "invalid_argument",
25366
- `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path, or start with ${HOME_PREFIX}.`
25350
+ `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path.`
25367
25351
  );
25368
25352
  }
25369
- return resolve3(expanded);
25353
+ return value;
25370
25354
  }
25371
25355
  async function mainCheckout(root, signal) {
25372
25356
  const common = (await git(["rev-parse", "--path-format=absolute", "--git-common-dir"], { cwd: root, signal })).trim();
@@ -25382,7 +25366,7 @@ async function repositoryName(root, signal) {
25382
25366
  return fromUrl || basename3(await mainCheckout(root, signal)).replace(GIT_SUFFIX, "");
25383
25367
  }
25384
25368
  function worktreePath(root, repository, branch) {
25385
- return join9(root, repository, branch.replaceAll("/", "-"));
25369
+ return join8(root, repository, branch.replaceAll("/", "-"));
25386
25370
  }
25387
25371
 
25388
25372
  // src/core/worktree/add-plan.ts
@@ -25645,7 +25629,7 @@ var worktreeAdd = defineTool({
25645
25629
  });
25646
25630
 
25647
25631
  // src/core/worktree/list.ts
25648
- import { dirname as dirname5, join as join10 } from "node:path";
25632
+ import { dirname as dirname5, join as join9 } from "node:path";
25649
25633
  var worktreeSchema = external_exports.object({
25650
25634
  directory: external_exports.string().describe("The worktree's directory: its real path when it exists, else as git prints it."),
25651
25635
  branch: external_exports.string().nullable().describe("The checked-out branch's name; null when HEAD is detached or bare."),
@@ -25680,7 +25664,7 @@ async function readWorktreeList(callerRoot, root, signal) {
25680
25664
  const repository = await repositoryName(callerRoot, signal);
25681
25665
  const [caller, layoutFolder, entries] = await Promise.all([
25682
25666
  realpathOrSelf(callerRoot),
25683
- realpathOfExisting(join10(root, repository)),
25667
+ realpathOfExisting(join9(root, repository)),
25684
25668
  listWorktrees(callerRoot, signal)
25685
25669
  ]);
25686
25670
  return {
@@ -25742,7 +25726,7 @@ async function removeDescription(plan, signal) {
25742
25726
  }
25743
25727
 
25744
25728
  // src/core/worktree/remove-plan.ts
25745
- import { resolve as resolve4 } from "node:path";
25729
+ import { resolve as resolve2 } from "node:path";
25746
25730
 
25747
25731
  // src/core/worktree/remove-checks.ts
25748
25732
  var MAX_LISTED_FILES = 50;
@@ -25816,7 +25800,7 @@ async function targetOf2(request) {
25816
25800
  if (request.directory.trim() === "") {
25817
25801
  throw invalid3("directory is blank; pass the directory of the worktree to remove.");
25818
25802
  }
25819
- return realpathOfExisting(resolve4(request.callerRoot, request.directory));
25803
+ return realpathOfExisting(resolve2(request.callerRoot, request.directory));
25820
25804
  }
25821
25805
  async function findWorktree(request, target) {
25822
25806
  for (const entry of await listWorktrees(request.callerRoot, request.signal)) {
@@ -25859,7 +25843,7 @@ async function planRemove(request) {
25859
25843
 
25860
25844
  // src/core/worktree/remove-run.ts
25861
25845
  import { rmdir } from "node:fs/promises";
25862
- import { dirname as dirname6, join as join11 } from "node:path";
25846
+ import { dirname as dirname6, join as join10 } from "node:path";
25863
25847
 
25864
25848
  // src/core/worktree/landed.ts
25865
25849
  var LANDED_READ_TIMEOUT_MS = PROTECTION_READ_TIMEOUT_MS;
@@ -25941,7 +25925,7 @@ async function removeWorktreeRecord(request, plan) {
25941
25925
  }
25942
25926
  async function removeEmptyLayoutFolder(request, plan) {
25943
25927
  const { callerRoot, worktreeRoot: worktreeRoot2, signal } = request;
25944
- const folder = await realpathOfExisting(join11(worktreeRoot2, await repositoryName(callerRoot, signal)));
25928
+ const folder = await realpathOfExisting(join10(worktreeRoot2, await repositoryName(callerRoot, signal)));
25945
25929
  const parent = dirname6(plan.directory);
25946
25930
  if (await realpathOfExisting(parent) !== folder) return;
25947
25931
  try {
@@ -26404,9 +26388,9 @@ async function timedCallDescription(spec, args, ctx, timeoutMs = DESCRIBE_TIMEOU
26404
26388
  let timer;
26405
26389
  let onAbort;
26406
26390
  try {
26407
- const timedOut = new Promise((resolve5) => {
26391
+ const timedOut = new Promise((resolve3) => {
26408
26392
  timer = setTimeout(() => {
26409
- resolve5(void 0);
26393
+ resolve3(void 0);
26410
26394
  controller.abort();
26411
26395
  }, timeoutMs);
26412
26396
  });
@@ -26461,7 +26445,7 @@ var PROJECT_PROFILE = {
26461
26445
 
26462
26446
  // src/entry/settings.ts
26463
26447
  import { homedir as homedir2 } from "node:os";
26464
- import { isAbsolute as isAbsolute6, join as join12, resolve as resolvePath, sep as sep3 } from "node:path";
26448
+ import { isAbsolute as isAbsolute6, join as join11, resolve as resolvePath, sep as sep3 } from "node:path";
26465
26449
  var CONFIRM_MODES = ["harness", "elicitation"];
26466
26450
  var ALL_TOOLSETS = "all";
26467
26451
  var TRUE_VALUES = ["1", "true"];
@@ -26555,7 +26539,7 @@ function parseNumber(value, source) {
26555
26539
  }
26556
26540
  function absolutePath(text, source, env) {
26557
26541
  const home = env.HOME !== void 0 && isAbsolute6(env.HOME) ? env.HOME : homedir2();
26558
- const expanded = text === "~" || text.startsWith("~/") || text.startsWith(`~${sep3}`) ? join12(home, text.slice(1)) : text;
26542
+ const expanded = text === "~" || text.startsWith("~/") || text.startsWith(`~${sep3}`) ? join11(home, text.slice(1)) : text;
26559
26543
  if (!isAbsolute6(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
26560
26544
  return resolvePath(expanded);
26561
26545
  }
package/dist/mcp.js CHANGED
@@ -2984,7 +2984,7 @@ var require_compile = __commonJS({
2984
2984
  const schOrFunc = root.refs[ref];
2985
2985
  if (schOrFunc)
2986
2986
  return schOrFunc;
2987
- let _sch = resolve5.call(this, root, ref);
2987
+ let _sch = resolve3.call(this, root, ref);
2988
2988
  if (_sch === void 0) {
2989
2989
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
2990
2990
  const { schemaId } = this.opts;
@@ -3011,7 +3011,7 @@ var require_compile = __commonJS({
3011
3011
  function sameSchemaEnv(s1, s2) {
3012
3012
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3013
3013
  }
3014
- function resolve5(root, ref) {
3014
+ function resolve3(root, ref) {
3015
3015
  let sch;
3016
3016
  while (typeof (sch = this.refs[ref]) == "string")
3017
3017
  ref = sch;
@@ -3875,7 +3875,7 @@ var require_fast_uri = __commonJS({
3875
3875
  }
3876
3876
  return uri;
3877
3877
  }
3878
- function resolve5(baseURI, relativeURI, options) {
3878
+ function resolve3(baseURI, relativeURI, options) {
3879
3879
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3880
3880
  const {
3881
3881
  parsed: baseParsed,
@@ -4266,7 +4266,7 @@ var require_fast_uri = __commonJS({
4266
4266
  var fastUri = {
4267
4267
  SCHEMES,
4268
4268
  normalize,
4269
- resolve: resolve5,
4269
+ resolve: resolve3,
4270
4270
  resolveComponent,
4271
4271
  equal,
4272
4272
  serialize,
@@ -12325,7 +12325,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
12325
12325
  var NONE = 0;
12326
12326
  var ASSUMED = 1;
12327
12327
  var PROVEN = 2;
12328
- function isRecursive(inst, stack, resolve5) {
12328
+ function isRecursive(inst, stack, resolve3) {
12329
12329
  const cached2 = recursive.get(inst);
12330
12330
  if (cached2 !== void 0)
12331
12331
  return cached2 ? PROVEN : NONE;
@@ -12335,7 +12335,7 @@ function isRecursive(inst, stack, resolve5) {
12335
12335
  let result = NONE;
12336
12336
  const check2 = (child) => {
12337
12337
  if (result !== PROVEN && child?._zod) {
12338
- const answer = isRecursive(child, stack, resolve5);
12338
+ const answer = isRecursive(child, stack, resolve3);
12339
12339
  if (answer > result)
12340
12340
  result = answer;
12341
12341
  }
@@ -12346,7 +12346,7 @@ function isRecursive(inst, stack, resolve5) {
12346
12346
  const desc = Object.getOwnPropertyDescriptor(sh, key);
12347
12347
  if (spread && !desc.enumerable)
12348
12348
  continue;
12349
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
12349
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
12350
12350
  if (child > answer)
12351
12351
  answer = child;
12352
12352
  }
@@ -12410,7 +12410,7 @@ function isRecursive(inst, stack, resolve5) {
12410
12410
  break;
12411
12411
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
12412
12412
  case "lazy": {
12413
- const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
12413
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
12414
12414
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
12415
12415
  break;
12416
12416
  }
@@ -28587,12 +28587,12 @@ var StdioServerTransport = class {
28587
28587
  this.onclose?.();
28588
28588
  }
28589
28589
  send(message) {
28590
- return new Promise((resolve5) => {
28590
+ return new Promise((resolve3) => {
28591
28591
  const json2 = serializeMessage(message);
28592
28592
  if (this._stdout.write(json2)) {
28593
- resolve5();
28593
+ resolve3();
28594
28594
  } else {
28595
- this._stdout.once("drain", resolve5);
28595
+ this._stdout.once("drain", resolve3);
28596
28596
  }
28597
28597
  });
28598
28598
  }
@@ -28639,7 +28639,7 @@ function abortError(signal) {
28639
28639
  }
28640
28640
  function exec(argv, options) {
28641
28641
  const { signal } = options;
28642
- return new Promise((resolve5, reject) => {
28642
+ return new Promise((resolve3, reject) => {
28643
28643
  if (signal?.aborted) {
28644
28644
  reject(abortError(signal));
28645
28645
  return;
@@ -28698,7 +28698,7 @@ function exec(argv, options) {
28698
28698
  }
28699
28699
  const finish = () => settle2(() => {
28700
28700
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
28701
- resolve5({
28701
+ resolve3({
28702
28702
  code: code ?? -1,
28703
28703
  stdout,
28704
28704
  stderr,
@@ -29113,7 +29113,7 @@ var PROJECT = {
29113
29113
  /** The toolsets on when a setting names none. */
29114
29114
  defaultToolsets: ["repo", "commit", "rebase"],
29115
29115
  /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
29116
- options: { published_text_rules: "text", worktree_root: "text" },
29116
+ options: { published_text_rules: "path", worktree_root: "path" },
29117
29117
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
29118
29118
  toolVariables: [
29119
29119
  "LANGUAGE",
@@ -31857,22 +31857,7 @@ async function compareBranch(root, remote, selected, expect, signal) {
31857
31857
 
31858
31858
  // src/core/push/published-text.ts
31859
31859
  import { readFile as readFile2, stat as stat6 } from "node:fs/promises";
31860
- import { isAbsolute as isAbsolute5, resolve as resolve2 } from "node:path";
31861
-
31862
- // src/core/home.ts
31863
- import { homedir as homedir2 } from "node:os";
31864
- import { join as join6 } from "node:path";
31865
- var HOME_MARK = "~";
31866
- var HOME_PREFIX = "~/";
31867
- function homeDirectory() {
31868
- return process.env.HOME?.trim() || homedir2();
31869
- }
31870
- function expandHome(value) {
31871
- if (value === HOME_MARK) return homeDirectory();
31872
- return value.startsWith(HOME_PREFIX) ? join6(homeDirectory(), value.slice(HOME_PREFIX.length)) : value;
31873
- }
31874
-
31875
- // src/core/push/published-text.ts
31860
+ import { isAbsolute as isAbsolute5 } from "node:path";
31876
31861
  var PUBLISHED_TEXT_RULES_OPTION = "published_text_rules";
31877
31862
  var MAX_FILE_BYTES = 64 * 1024;
31878
31863
  var MAX_RULES = 100;
@@ -31944,11 +31929,8 @@ async function loadPublishedTextRules(options, signal) {
31944
31929
  const option = options?.[PUBLISHED_TEXT_RULES_OPTION];
31945
31930
  const path = typeof option === "string" ? option.trim() : void 0;
31946
31931
  if (!path) return [];
31947
- const expanded = expandHome(path);
31948
- if (!isAbsolute5(expanded))
31949
- throw invalidRules(path, `the value must be an absolute path, or start with ${HOME_PREFIX}`);
31950
- const normalized = resolve2(expanded);
31951
- return compileRules(normalized, parseRulesJson(normalized, await readRulesFile(normalized, signal)));
31932
+ if (!isAbsolute5(path)) throw invalidRules(path, "the value must be an absolute path");
31933
+ return compileRules(path, parseRulesJson(path, await readRulesFile(path, signal)));
31952
31934
  }
31953
31935
  var publishedTextFindingSchema = external_exports.object({
31954
31936
  rule: external_exports.string().describe("The name of the rule that matched."),
@@ -32902,11 +32884,11 @@ var rebaseRead = defineTool({
32902
32884
  // src/core/rebase/resume.ts
32903
32885
  import { createHash as createHash3 } from "node:crypto";
32904
32886
  import { readFile as readFile5 } from "node:fs/promises";
32905
- import { join as join9 } from "node:path";
32887
+ import { join as join8 } from "node:path";
32906
32888
 
32907
32889
  // src/core/conflicts.ts
32908
32890
  import { readFile as readFile3 } from "node:fs/promises";
32909
- import { basename as basename2, join as join7 } from "node:path";
32891
+ import { basename as basename2, join as join6 } from "node:path";
32910
32892
  var REGION_MAX = 20;
32911
32893
  var SIDE_LINES_MAX = 40;
32912
32894
  var LOCKFILES = {
@@ -33018,7 +33000,7 @@ async function readConflictFile(root, path, xy, operation) {
33018
33000
  }
33019
33001
  let text;
33020
33002
  try {
33021
- text = await readFile3(join7(root, path), "utf8");
33003
+ text = await readFile3(join6(root, path), "utf8");
33022
33004
  } catch {
33023
33005
  return { ...file2, region_count: 0, regions: [] };
33024
33006
  }
@@ -33101,13 +33083,13 @@ var rebaseOutcomeSchema = external_exports.object({
33101
33083
 
33102
33084
  // src/core/rebase/rebase-state.ts
33103
33085
  import { readFile as readFile4 } from "node:fs/promises";
33104
- import { join as join8 } from "node:path";
33086
+ import { join as join7 } from "node:path";
33105
33087
  var HEADS_PREFIX = "refs/heads/";
33106
33088
  var STATE_DIRS = ["rebase-merge", "rebase-apply"];
33107
33089
  var UPDATE_REFS_LINES = 3;
33108
33090
  async function readStateFile(dir, name) {
33109
33091
  try {
33110
- return (await readFile4(join8(dir, name), "utf8")).trim();
33092
+ return (await readFile4(join7(dir, name), "utf8")).trim();
33111
33093
  } catch {
33112
33094
  return void 0;
33113
33095
  }
@@ -33282,7 +33264,7 @@ async function conflictedFiles(root, signal) {
33282
33264
  for (const path of await unmergedPaths(root, signal)) {
33283
33265
  let content;
33284
33266
  try {
33285
- content = await readFile5(join9(root, path));
33267
+ content = await readFile5(join8(root, path));
33286
33268
  } catch {
33287
33269
  content = null;
33288
33270
  }
@@ -34281,7 +34263,8 @@ var repoRead = defineTool({
34281
34263
  import { lstat } from "node:fs/promises";
34282
34264
 
34283
34265
  // src/core/worktree/layout.ts
34284
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute6, join as join10, resolve as resolve3 } from "node:path";
34266
+ import { homedir as homedir2 } from "node:os";
34267
+ import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute6, join as join9 } from "node:path";
34285
34268
  var WORKTREE_ROOT_OPTION = "worktree_root";
34286
34269
  var DEFAULT_ROOT_FOLDER = "worktrees";
34287
34270
  var GIT_DIRECTORY = ".git";
@@ -34292,15 +34275,14 @@ var NOT_A_NAME = [".", ".."];
34292
34275
  function worktreeRoot(ctx) {
34293
34276
  const option = ctx.options?.[WORKTREE_ROOT_OPTION];
34294
34277
  const value = typeof option === "string" ? option.trim() : void 0;
34295
- if (!value) return join10(homeDirectory(), DEFAULT_ROOT_FOLDER);
34296
- const expanded = expandHome(value);
34297
- if (!isAbsolute6(expanded)) {
34278
+ if (!value) return join9(process.env.HOME?.trim() || homedir2(), DEFAULT_ROOT_FOLDER);
34279
+ if (!isAbsolute6(value)) {
34298
34280
  throw new ToolError(
34299
34281
  "invalid_argument",
34300
- `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path, or start with ${HOME_PREFIX}.`
34282
+ `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path.`
34301
34283
  );
34302
34284
  }
34303
- return resolve3(expanded);
34285
+ return value;
34304
34286
  }
34305
34287
  async function mainCheckout(root, signal) {
34306
34288
  const common = (await git(["rev-parse", "--path-format=absolute", "--git-common-dir"], { cwd: root, signal })).trim();
@@ -34316,7 +34298,7 @@ async function repositoryName(root, signal) {
34316
34298
  return fromUrl || basename3(await mainCheckout(root, signal)).replace(GIT_SUFFIX, "");
34317
34299
  }
34318
34300
  function worktreePath(root, repository, branch) {
34319
- return join10(root, repository, branch.replaceAll("/", "-"));
34301
+ return join9(root, repository, branch.replaceAll("/", "-"));
34320
34302
  }
34321
34303
 
34322
34304
  // src/core/worktree/add-plan.ts
@@ -34579,7 +34561,7 @@ var worktreeAdd = defineTool({
34579
34561
  });
34580
34562
 
34581
34563
  // src/core/worktree/list.ts
34582
- import { dirname as dirname5, join as join11 } from "node:path";
34564
+ import { dirname as dirname5, join as join10 } from "node:path";
34583
34565
  var worktreeSchema = external_exports.object({
34584
34566
  directory: external_exports.string().describe("The worktree's directory: its real path when it exists, else as git prints it."),
34585
34567
  branch: external_exports.string().nullable().describe("The checked-out branch's name; null when HEAD is detached or bare."),
@@ -34614,7 +34596,7 @@ async function readWorktreeList(callerRoot, root, signal) {
34614
34596
  const repository = await repositoryName(callerRoot, signal);
34615
34597
  const [caller, layoutFolder, entries] = await Promise.all([
34616
34598
  realpathOrSelf(callerRoot),
34617
- realpathOfExisting(join11(root, repository)),
34599
+ realpathOfExisting(join10(root, repository)),
34618
34600
  listWorktrees(callerRoot, signal)
34619
34601
  ]);
34620
34602
  return {
@@ -34676,7 +34658,7 @@ async function removeDescription(plan, signal) {
34676
34658
  }
34677
34659
 
34678
34660
  // src/core/worktree/remove-plan.ts
34679
- import { resolve as resolve4 } from "node:path";
34661
+ import { resolve as resolve2 } from "node:path";
34680
34662
 
34681
34663
  // src/core/worktree/remove-checks.ts
34682
34664
  var MAX_LISTED_FILES = 50;
@@ -34750,7 +34732,7 @@ async function targetOf2(request) {
34750
34732
  if (request.directory.trim() === "") {
34751
34733
  throw invalid3("directory is blank; pass the directory of the worktree to remove.");
34752
34734
  }
34753
- return realpathOfExisting(resolve4(request.callerRoot, request.directory));
34735
+ return realpathOfExisting(resolve2(request.callerRoot, request.directory));
34754
34736
  }
34755
34737
  async function findWorktree(request, target) {
34756
34738
  for (const entry of await listWorktrees(request.callerRoot, request.signal)) {
@@ -34793,7 +34775,7 @@ async function planRemove(request) {
34793
34775
 
34794
34776
  // src/core/worktree/remove-run.ts
34795
34777
  import { rmdir } from "node:fs/promises";
34796
- import { dirname as dirname6, join as join12 } from "node:path";
34778
+ import { dirname as dirname6, join as join11 } from "node:path";
34797
34779
 
34798
34780
  // src/core/worktree/landed.ts
34799
34781
  var LANDED_READ_TIMEOUT_MS = PROTECTION_READ_TIMEOUT_MS;
@@ -34875,7 +34857,7 @@ async function removeWorktreeRecord(request, plan) {
34875
34857
  }
34876
34858
  async function removeEmptyLayoutFolder(request, plan) {
34877
34859
  const { callerRoot, worktreeRoot: worktreeRoot2, signal } = request;
34878
- const folder = await realpathOfExisting(join12(worktreeRoot2, await repositoryName(callerRoot, signal)));
34860
+ const folder = await realpathOfExisting(join11(worktreeRoot2, await repositoryName(callerRoot, signal)));
34879
34861
  const parent = dirname6(plan.directory);
34880
34862
  if (await realpathOfExisting(parent) !== folder) return;
34881
34863
  try {
@@ -35630,7 +35612,7 @@ var Protocol = class {
35630
35612
  return;
35631
35613
  }
35632
35614
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
35633
- await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
35615
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
35634
35616
  options?.signal?.throwIfAborted();
35635
35617
  }
35636
35618
  } catch (error62) {
@@ -35647,7 +35629,7 @@ var Protocol = class {
35647
35629
  */
35648
35630
  request(request, resultSchema, options) {
35649
35631
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
35650
- return new Promise((resolve5, reject) => {
35632
+ return new Promise((resolve3, reject) => {
35651
35633
  const earlyReject = (error62) => {
35652
35634
  reject(error62);
35653
35635
  };
@@ -35725,7 +35707,7 @@ var Protocol = class {
35725
35707
  if (!parseResult.success) {
35726
35708
  reject(parseResult.error);
35727
35709
  } else {
35728
- resolve5(parseResult.data);
35710
+ resolve3(parseResult.data);
35729
35711
  }
35730
35712
  } catch (error62) {
35731
35713
  reject(error62);
@@ -35987,12 +35969,12 @@ var Protocol = class {
35987
35969
  }
35988
35970
  } catch {
35989
35971
  }
35990
- return new Promise((resolve5, reject) => {
35972
+ return new Promise((resolve3, reject) => {
35991
35973
  if (signal.aborted) {
35992
35974
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
35993
35975
  return;
35994
35976
  }
35995
- const timeoutId = setTimeout(resolve5, interval);
35977
+ const timeoutId = setTimeout(resolve3, interval);
35996
35978
  signal.addEventListener("abort", () => {
35997
35979
  clearTimeout(timeoutId);
35998
35980
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -36948,7 +36930,7 @@ async function main() {
36948
36930
  const settings = parseSettings(process.argv.slice(2), process.env);
36949
36931
  server = createServer(ALL_TOOLS, settings, {
36950
36932
  ownDir: packageRoot(import.meta.url),
36951
- version: true ? "0.2.1" : void 0
36933
+ version: true ? "0.2.2" : void 0
36952
36934
  });
36953
36935
  } catch (error62) {
36954
36936
  if (!isToolError(error62)) throw error62;
package/dist/opencode.js CHANGED
@@ -26,7 +26,7 @@ var PROJECT = {
26
26
  /** The toolsets on when a setting names none. */
27
27
  defaultToolsets: ["repo", "commit", "rebase"],
28
28
  /** The package's own settings, which the tools themselves read: each snake_case name and its type, with bounds when it needs them. */
29
- options: { published_text_rules: "text", worktree_root: "text" },
29
+ options: { published_text_rules: "path", worktree_root: "path" },
30
30
  /** The environment variables the tools read besides the settings, which Codex forwards to the server after them. */
31
31
  toolVariables: [
32
32
  "LANGUAGE",
@@ -5134,7 +5134,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5134
5134
  var NONE = 0;
5135
5135
  var ASSUMED = 1;
5136
5136
  var PROVEN = 2;
5137
- function isRecursive(inst, stack, resolve6) {
5137
+ function isRecursive(inst, stack, resolve4) {
5138
5138
  const cached2 = recursive.get(inst);
5139
5139
  if (cached2 !== void 0)
5140
5140
  return cached2 ? PROVEN : NONE;
@@ -5144,7 +5144,7 @@ function isRecursive(inst, stack, resolve6) {
5144
5144
  let result = NONE;
5145
5145
  const check2 = (child) => {
5146
5146
  if (result !== PROVEN && child?._zod) {
5147
- const answer = isRecursive(child, stack, resolve6);
5147
+ const answer = isRecursive(child, stack, resolve4);
5148
5148
  if (answer > result)
5149
5149
  result = answer;
5150
5150
  }
@@ -5155,7 +5155,7 @@ function isRecursive(inst, stack, resolve6) {
5155
5155
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5156
5156
  if (spread && !desc.enumerable)
5157
5157
  continue;
5158
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
5158
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve4) : NONE;
5159
5159
  if (child > answer)
5160
5160
  answer = child;
5161
5161
  }
@@ -5219,7 +5219,7 @@ function isRecursive(inst, stack, resolve6) {
5219
5219
  break;
5220
5220
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5221
5221
  case "lazy": {
5222
- const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
5222
+ const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
5223
5223
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5224
5224
  break;
5225
5225
  }
@@ -19828,7 +19828,7 @@ function abortError(signal) {
19828
19828
  }
19829
19829
  function exec(argv, options) {
19830
19830
  const { signal } = options;
19831
- return new Promise((resolve6, reject) => {
19831
+ return new Promise((resolve4, reject) => {
19832
19832
  if (signal?.aborted) {
19833
19833
  reject(abortError(signal));
19834
19834
  return;
@@ -19887,7 +19887,7 @@ function exec(argv, options) {
19887
19887
  }
19888
19888
  const finish = () => settle2(() => {
19889
19889
  const { code, killedBy } = exitedWith ?? { code: null, killedBy: null };
19890
- resolve6({
19890
+ resolve4({
19891
19891
  code: code ?? -1,
19892
19892
  stdout,
19893
19893
  stderr,
@@ -22774,7 +22774,7 @@ async function compareBranch(root, remote, selected, expect, signal) {
22774
22774
 
22775
22775
  // src/core/push/published-text.ts
22776
22776
  import { readFile as readFile2, stat as stat6 } from "node:fs/promises";
22777
- import { isAbsolute as isAbsolute4, resolve as resolve2 } from "node:path";
22777
+ import { isAbsolute as isAbsolute4 } from "node:path";
22778
22778
 
22779
22779
  // src/core/describe.ts
22780
22780
  var MAX_FACTS = 8;
@@ -22911,19 +22911,6 @@ async function wholeCallDescription(spec, args, ctx) {
22911
22911
  return { ...headline2 && { headline: headline2 }, facts, ...text && { text }, ...kept2 };
22912
22912
  }
22913
22913
 
22914
- // src/core/home.ts
22915
- import { homedir } from "node:os";
22916
- import { join as join5 } from "node:path";
22917
- var HOME_MARK = "~";
22918
- var HOME_PREFIX = "~/";
22919
- function homeDirectory() {
22920
- return process.env.HOME?.trim() || homedir();
22921
- }
22922
- function expandHome(value) {
22923
- if (value === HOME_MARK) return homeDirectory();
22924
- return value.startsWith(HOME_PREFIX) ? join5(homeDirectory(), value.slice(HOME_PREFIX.length)) : value;
22925
- }
22926
-
22927
22914
  // src/core/push/published-text.ts
22928
22915
  var PUBLISHED_TEXT_RULES_OPTION = "published_text_rules";
22929
22916
  var MAX_FILE_BYTES = 64 * 1024;
@@ -22996,11 +22983,8 @@ async function loadPublishedTextRules(options, signal) {
22996
22983
  const option = options?.[PUBLISHED_TEXT_RULES_OPTION];
22997
22984
  const path = typeof option === "string" ? option.trim() : void 0;
22998
22985
  if (!path) return [];
22999
- const expanded = expandHome(path);
23000
- if (!isAbsolute4(expanded))
23001
- throw invalidRules(path, `the value must be an absolute path, or start with ${HOME_PREFIX}`);
23002
- const normalized = resolve2(expanded);
23003
- return compileRules(normalized, parseRulesJson(normalized, await readRulesFile(normalized, signal)));
22986
+ if (!isAbsolute4(path)) throw invalidRules(path, "the value must be an absolute path");
22987
+ return compileRules(path, parseRulesJson(path, await readRulesFile(path, signal)));
23004
22988
  }
23005
22989
  var publishedTextFindingSchema = external_exports.object({
23006
22990
  rule: external_exports.string().describe("The name of the rule that matched."),
@@ -23954,11 +23938,11 @@ var rebaseRead = defineTool({
23954
23938
  // src/core/rebase/resume.ts
23955
23939
  import { createHash as createHash3 } from "node:crypto";
23956
23940
  import { readFile as readFile5 } from "node:fs/promises";
23957
- import { join as join8 } from "node:path";
23941
+ import { join as join7 } from "node:path";
23958
23942
 
23959
23943
  // src/core/conflicts.ts
23960
23944
  import { readFile as readFile3 } from "node:fs/promises";
23961
- import { basename as basename2, join as join6 } from "node:path";
23945
+ import { basename as basename2, join as join5 } from "node:path";
23962
23946
  var REGION_MAX = 20;
23963
23947
  var SIDE_LINES_MAX = 40;
23964
23948
  var LOCKFILES = {
@@ -24070,7 +24054,7 @@ async function readConflictFile(root, path, xy, operation) {
24070
24054
  }
24071
24055
  let text;
24072
24056
  try {
24073
- text = await readFile3(join6(root, path), "utf8");
24057
+ text = await readFile3(join5(root, path), "utf8");
24074
24058
  } catch {
24075
24059
  return { ...file2, region_count: 0, regions: [] };
24076
24060
  }
@@ -24153,13 +24137,13 @@ var rebaseOutcomeSchema = external_exports.object({
24153
24137
 
24154
24138
  // src/core/rebase/rebase-state.ts
24155
24139
  import { readFile as readFile4 } from "node:fs/promises";
24156
- import { join as join7 } from "node:path";
24140
+ import { join as join6 } from "node:path";
24157
24141
  var HEADS_PREFIX = "refs/heads/";
24158
24142
  var STATE_DIRS = ["rebase-merge", "rebase-apply"];
24159
24143
  var UPDATE_REFS_LINES = 3;
24160
24144
  async function readStateFile(dir, name) {
24161
24145
  try {
24162
- return (await readFile4(join7(dir, name), "utf8")).trim();
24146
+ return (await readFile4(join6(dir, name), "utf8")).trim();
24163
24147
  } catch {
24164
24148
  return void 0;
24165
24149
  }
@@ -24334,7 +24318,7 @@ async function conflictedFiles(root, signal) {
24334
24318
  for (const path of await unmergedPaths(root, signal)) {
24335
24319
  let content;
24336
24320
  try {
24337
- content = await readFile5(join8(root, path));
24321
+ content = await readFile5(join7(root, path));
24338
24322
  } catch {
24339
24323
  content = null;
24340
24324
  }
@@ -25333,7 +25317,8 @@ var repoRead = defineTool({
25333
25317
  import { lstat } from "node:fs/promises";
25334
25318
 
25335
25319
  // src/core/worktree/layout.ts
25336
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute5, join as join9, resolve as resolve3 } from "node:path";
25320
+ import { homedir } from "node:os";
25321
+ import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute5, join as join8 } from "node:path";
25337
25322
  var WORKTREE_ROOT_OPTION = "worktree_root";
25338
25323
  var DEFAULT_ROOT_FOLDER = "worktrees";
25339
25324
  var GIT_DIRECTORY = ".git";
@@ -25344,15 +25329,14 @@ var NOT_A_NAME = [".", ".."];
25344
25329
  function worktreeRoot(ctx) {
25345
25330
  const option = ctx.options?.[WORKTREE_ROOT_OPTION];
25346
25331
  const value = typeof option === "string" ? option.trim() : void 0;
25347
- if (!value) return join9(homeDirectory(), DEFAULT_ROOT_FOLDER);
25348
- const expanded = expandHome(value);
25349
- if (!isAbsolute5(expanded)) {
25332
+ if (!value) return join8(process.env.HOME?.trim() || homedir(), DEFAULT_ROOT_FOLDER);
25333
+ if (!isAbsolute5(value)) {
25350
25334
  throw new ToolError(
25351
25335
  "invalid_argument",
25352
- `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path, or start with ${HOME_PREFIX}.`
25336
+ `The ${WORKTREE_ROOT_OPTION} setting (${value}): the value must be an absolute path.`
25353
25337
  );
25354
25338
  }
25355
- return resolve3(expanded);
25339
+ return value;
25356
25340
  }
25357
25341
  async function mainCheckout(root, signal) {
25358
25342
  const common = (await git(["rev-parse", "--path-format=absolute", "--git-common-dir"], { cwd: root, signal })).trim();
@@ -25368,7 +25352,7 @@ async function repositoryName(root, signal) {
25368
25352
  return fromUrl || basename3(await mainCheckout(root, signal)).replace(GIT_SUFFIX, "");
25369
25353
  }
25370
25354
  function worktreePath(root, repository, branch) {
25371
- return join9(root, repository, branch.replaceAll("/", "-"));
25355
+ return join8(root, repository, branch.replaceAll("/", "-"));
25372
25356
  }
25373
25357
 
25374
25358
  // src/core/worktree/add-plan.ts
@@ -25631,7 +25615,7 @@ var worktreeAdd = defineTool({
25631
25615
  });
25632
25616
 
25633
25617
  // src/core/worktree/list.ts
25634
- import { dirname as dirname5, join as join10 } from "node:path";
25618
+ import { dirname as dirname5, join as join9 } from "node:path";
25635
25619
  var worktreeSchema = external_exports.object({
25636
25620
  directory: external_exports.string().describe("The worktree's directory: its real path when it exists, else as git prints it."),
25637
25621
  branch: external_exports.string().nullable().describe("The checked-out branch's name; null when HEAD is detached or bare."),
@@ -25666,7 +25650,7 @@ async function readWorktreeList(callerRoot, root, signal) {
25666
25650
  const repository = await repositoryName(callerRoot, signal);
25667
25651
  const [caller, layoutFolder, entries] = await Promise.all([
25668
25652
  realpathOrSelf(callerRoot),
25669
- realpathOfExisting(join10(root, repository)),
25653
+ realpathOfExisting(join9(root, repository)),
25670
25654
  listWorktrees(callerRoot, signal)
25671
25655
  ]);
25672
25656
  return {
@@ -25728,7 +25712,7 @@ async function removeDescription(plan, signal) {
25728
25712
  }
25729
25713
 
25730
25714
  // src/core/worktree/remove-plan.ts
25731
- import { resolve as resolve4 } from "node:path";
25715
+ import { resolve as resolve2 } from "node:path";
25732
25716
 
25733
25717
  // src/core/worktree/remove-checks.ts
25734
25718
  var MAX_LISTED_FILES = 50;
@@ -25802,7 +25786,7 @@ async function targetOf2(request) {
25802
25786
  if (request.directory.trim() === "") {
25803
25787
  throw invalid3("directory is blank; pass the directory of the worktree to remove.");
25804
25788
  }
25805
- return realpathOfExisting(resolve4(request.callerRoot, request.directory));
25789
+ return realpathOfExisting(resolve2(request.callerRoot, request.directory));
25806
25790
  }
25807
25791
  async function findWorktree(request, target) {
25808
25792
  for (const entry of await listWorktrees(request.callerRoot, request.signal)) {
@@ -25845,7 +25829,7 @@ async function planRemove(request) {
25845
25829
 
25846
25830
  // src/core/worktree/remove-run.ts
25847
25831
  import { rmdir } from "node:fs/promises";
25848
- import { dirname as dirname6, join as join11 } from "node:path";
25832
+ import { dirname as dirname6, join as join10 } from "node:path";
25849
25833
 
25850
25834
  // src/core/worktree/landed.ts
25851
25835
  var LANDED_READ_TIMEOUT_MS = PROTECTION_READ_TIMEOUT_MS;
@@ -25927,7 +25911,7 @@ async function removeWorktreeRecord(request, plan) {
25927
25911
  }
25928
25912
  async function removeEmptyLayoutFolder(request, plan) {
25929
25913
  const { callerRoot, worktreeRoot: worktreeRoot2, signal } = request;
25930
- const folder = await realpathOfExisting(join11(worktreeRoot2, await repositoryName(callerRoot, signal)));
25914
+ const folder = await realpathOfExisting(join10(worktreeRoot2, await repositoryName(callerRoot, signal)));
25931
25915
  const parent = dirname6(plan.directory);
25932
25916
  if (await realpathOfExisting(parent) !== folder) return;
25933
25917
  try {
@@ -26392,9 +26376,9 @@ async function timedCallDescription(spec, args, ctx, timeoutMs = DESCRIBE_TIMEOU
26392
26376
  let timer;
26393
26377
  let onAbort;
26394
26378
  try {
26395
- const timedOut = new Promise((resolve6) => {
26379
+ const timedOut = new Promise((resolve4) => {
26396
26380
  timer = setTimeout(() => {
26397
- resolve6(void 0);
26381
+ resolve4(void 0);
26398
26382
  controller.abort();
26399
26383
  }, timeoutMs);
26400
26384
  });
@@ -26475,7 +26459,7 @@ var PROJECT_PROFILE = {
26475
26459
 
26476
26460
  // src/entry/settings.ts
26477
26461
  import { homedir as homedir2 } from "node:os";
26478
- import { isAbsolute as isAbsolute6, join as join12, resolve as resolvePath, sep as sep3 } from "node:path";
26462
+ import { isAbsolute as isAbsolute6, join as join11, resolve as resolvePath, sep as sep3 } from "node:path";
26479
26463
  var ALL_TOOLSETS = "all";
26480
26464
  var TRUE_VALUES = ["1", "true"];
26481
26465
  var FALSE_VALUES = ["0", "false"];
@@ -26534,7 +26518,7 @@ function parseNumber(value, source) {
26534
26518
  }
26535
26519
  function absolutePath(text, source, env) {
26536
26520
  const home = env.HOME !== void 0 && isAbsolute6(env.HOME) ? env.HOME : homedir2();
26537
- const expanded = text === "~" || text.startsWith("~/") || text.startsWith(`~${sep3}`) ? join12(home, text.slice(1)) : text;
26521
+ const expanded = text === "~" || text.startsWith("~/") || text.startsWith(`~${sep3}`) ? join11(home, text.slice(1)) : text;
26538
26522
  if (!isAbsolute6(expanded)) throw new ToolError("invalid_argument", `${source} must be an absolute path.`);
26539
26523
  return resolvePath(expanded);
26540
26524
  }
@@ -26681,7 +26665,7 @@ function settingsFromOptions(options, env, profile = PROJECT_PROFILE) {
26681
26665
  if (unknown2 !== void 0) {
26682
26666
  throw new ToolError("invalid_argument", `Unknown option "${unknown2}"; use one of ${names.join(", ")}.`);
26683
26667
  }
26684
- const resolve6 = (name, fromOption, fromText) => {
26668
+ const resolve4 = (name, fromOption, fromText) => {
26685
26669
  if (given[name] !== void 0) return fromOption(given[name]);
26686
26670
  const text = envText(name, env, profile);
26687
26671
  return text !== void 0 ? fromText(text) : void 0;
@@ -26698,17 +26682,17 @@ function settingsFromOptions(options, env, profile = PROJECT_PROFILE) {
26698
26682
  if (parsed !== void 0) parsedOptions[name] = parsed;
26699
26683
  }
26700
26684
  const settings = { options: parsedOptions };
26701
- const toolsets = resolve6(
26685
+ const toolsets = resolve4(
26702
26686
  "toolsets",
26703
26687
  (value) => toolsetsOption(value, profile.toolsets),
26704
26688
  ({ text, source }) => checkToolsets(splitList(text), source, profile.toolsets)
26705
26689
  );
26706
26690
  if (toolsets !== void 0) settings.toolsets = toolsets;
26707
- const readOnly = resolve6("read_only", booleanOption, ({ text, source }) => parseBoolean(text, source));
26691
+ const readOnly = resolve4("read_only", booleanOption, ({ text, source }) => parseBoolean(text, source));
26708
26692
  if (readOnly !== void 0) settings.read_only = readOnly;
26709
- const tools = resolve6("tools", listOption("tools"), ({ text }) => splitList(text));
26693
+ const tools = resolve4("tools", listOption("tools"), ({ text }) => splitList(text));
26710
26694
  if (tools !== void 0) settings.tools = tools;
26711
- const excluded = resolve6("exclude_tools", listOption("exclude_tools"), ({ text }) => splitList(text));
26695
+ const excluded = resolve4("exclude_tools", listOption("exclude_tools"), ({ text }) => splitList(text));
26712
26696
  if (excluded !== void 0) settings.exclude_tools = excluded;
26713
26697
  const language = given[LANGUAGE_OPTION] !== void 0 ? languageOption(given[LANGUAGE_OPTION]) : promptLanguage(void 0, env, void 0, profile);
26714
26698
  if (language !== void 0) settings.language = language;
@@ -26803,7 +26787,7 @@ function createV1Server(specs, deps = {}) {
26803
26787
 
26804
26788
  // src/opencode/skills.ts
26805
26789
  import { readFile as readFile6, readdir } from "node:fs/promises";
26806
- import { join as join13, resolve as resolve5 } from "node:path";
26790
+ import { join as join12, resolve as resolve3 } from "node:path";
26807
26791
  import { fileURLToPath as fileURLToPath2 } from "node:url";
26808
26792
  var SKILL_FILE = "SKILL.md";
26809
26793
  var FRONTMATTER = /^---[ \t]*\r?\n([\s\S]*?\r?\n)?---[ \t]*(?:\r?\n|$)/;
@@ -26894,7 +26878,7 @@ async function readSkillFile(path) {
26894
26878
  }
26895
26879
  }
26896
26880
  async function readSkills(dir) {
26897
- const root = resolve5(typeof dir === "string" ? dir : fileURLToPath2(dir));
26881
+ const root = resolve3(typeof dir === "string" ? dir : fileURLToPath2(dir));
26898
26882
  let entries;
26899
26883
  try {
26900
26884
  entries = (await readdir(root, { withFileTypes: true })).filter((entry) => !entry.isFile()).map((entry) => entry.name);
@@ -26904,7 +26888,7 @@ async function readSkills(dir) {
26904
26888
  }
26905
26889
  const skills = /* @__PURE__ */ new Map();
26906
26890
  for (const directory of entries.sort()) {
26907
- const path = join13(root, directory, SKILL_FILE);
26891
+ const path = join12(root, directory, SKILL_FILE);
26908
26892
  const text = await readSkillFile(path);
26909
26893
  if (text === void 0) continue;
26910
26894
  const found = parseSkill(directory, text, path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftsamo/git-tools",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Git tools for coding agents: state, commits, rewrites and stacked branches, worktrees and pushing.",
5
5
  "author": "craftsamo",
6
6
  "license": "LicenseRef-AGPL-3.0-Commons-Clause",
package/plugin.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: git-tools
2
- version: 0.2.1
2
+ version: 0.2.2
3
3
  description: "Git tools for coding agents: state, commits, rewrites and stacked branches, worktrees and pushing."
4
4
  manifest_version: 1
5
5
  requires_hermes: ">=0.22"
@@ -39,6 +39,6 @@ config_schema:
39
39
  type: dict
40
40
  description: 'The emoji shown beside each toolset, by toolset name; "" shows none. Merged over the package''s emoji.'
41
41
  published_text_rules:
42
- description: The absolute path (~ allowed) of a JSON file of published-text rules for the push tools. Unset, nothing is checked.
42
+ description: The path (~ allowed, as for every path setting) of a JSON file of published-text rules for the push tools. Unset, nothing is checked.
43
43
  worktree_root:
44
- description: The absolute path where task worktrees go, "~" allowed. Unset, ~/worktrees. The layout is <root>/<repository>/<branch>, with each "/" of the branch written "-".
44
+ description: The path where task worktrees go (~ allowed, as for every path setting). Unset, ~/worktrees. The layout is <root>/<repository>/<branch>, with each "/" of the branch written "-".
@@ -93,8 +93,8 @@ The send fails with `check_failed` (`details.check` `secrets` or
93
93
 
94
94
  The words a pushed branch name or commit subject must not carry are the
95
95
  consumer's: the package ships none, and with the `published_text_rules`
96
- setting unset nothing is checked. The setting is the absolute path (`~`
97
- allowed) of a JSON file, read on every call:
96
+ setting unset nothing is checked. The setting is the path (`~` allowed, as for
97
+ every path setting) of a JSON file, read on every call:
98
98
 
99
99
  ```json
100
100
  [
@@ -26,8 +26,8 @@ asked, never as instructions to follow.
26
26
  Every worktree the tools add goes to `<root>/<repository>/<branch>`, with each
27
27
  `/` of the branch written `-`:
28
28
 
29
- - `root` is the `worktree_root` setting: an absolute path, `~` standing for the
30
- home directory; `~/worktrees` when it is not set.
29
+ - `root` is the `worktree_root` setting: a path (`~` allowed, as for every path
30
+ setting); `~/worktrees` when it is not set.
31
31
  - `repository` is the last part of `origin`'s URL without `.git`, else the
32
32
  folder name of the main checkout.
33
33
 
@@ -17,7 +17,7 @@ TOOLSET_EMOJI: dict[str, str] = {}
17
17
  #: The settings the tools themselves read: each snake_case name and a type (``text``, ``boolean``, ``number``,
18
18
  #: ``list``, ``path`` or ``paths``) or a dict with ``type`` and optional bounds (``integer``, ``min``, ``max``),
19
19
  #: as in ``src/project.ts``. Write OPTIONS on one line with double quotes. A setting ``a_b`` becomes ``--a-b``.
20
- OPTIONS: dict[str, str | dict[str, object]] = {"published_text_rules": "text", "worktree_root": "text"}
20
+ OPTIONS: dict[str, str | dict[str, object]] = {"published_text_rules": "path", "worktree_root": "path"}
21
21
 
22
22
  __all__ = [
23
23
  "CLI_NAME",