agent-sh 0.12.12 → 0.12.13

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.
@@ -16,6 +16,7 @@ import { createToolProtocol } from "./tool-protocol.js";
16
16
  // Core tool factories
17
17
  import { createBashTool } from "./tools/bash.js";
18
18
  import { createPwshTool } from "./tools/pwsh.js";
19
+ import { findBash } from "../executor.js";
19
20
  import { createReadFileTool } from "./tools/read-file.js";
20
21
  import { createWriteFileTool } from "./tools/write-file.js";
21
22
  import { createEditFileTool } from "./tools/edit-file.js";
@@ -606,7 +607,9 @@ export class AgentLoop {
606
607
  }
607
608
  return env;
608
609
  };
609
- this.toolRegistry.register(createBashTool({ getCwd, getEnv, bus: this.bus }));
610
+ if (findBash() !== null) {
611
+ this.toolRegistry.register(createBashTool({ getCwd, getEnv, bus: this.bus }));
612
+ }
610
613
  if (process.platform === "win32") {
611
614
  this.toolRegistry.register(createPwshTool({ getCwd, getEnv, bus: this.bus }));
612
615
  }
@@ -9,7 +9,9 @@ export declare function discoverGlobalSkills(): Skill[];
9
9
  export declare function invalidateGlobalSkillsCache(): void;
10
10
  /**
11
11
  * Discover project-level skills from .agents/skills/ in cwd hierarchy.
12
- * Scans from cwd up to git root.
12
+ * Walks from cwd up to $HOME (or filesystem root if cwd is outside HOME).
13
+ * Git boundaries are ignored — nested repos under a skills-bearing parent
14
+ * would otherwise hide the parent's skills.
13
15
  */
14
16
  export declare function discoverProjectSkills(cwd: string): Skill[];
15
17
  /**
@@ -93,22 +93,6 @@ function scanDir(dir) {
93
93
  }
94
94
  return skills;
95
95
  }
96
- /** Find the git root from a directory. */
97
- function findGitRoot(dir) {
98
- let current = path.resolve(dir);
99
- while (true) {
100
- try {
101
- fs.accessSync(path.join(current, ".git"));
102
- return current;
103
- }
104
- catch {
105
- const parent = path.dirname(current);
106
- if (parent === current)
107
- return null;
108
- current = parent;
109
- }
110
- }
111
- }
112
96
  /** Expand ~ to home directory. */
113
97
  function expandHome(p) {
114
98
  if (p.startsWith("~/") || p === "~") {
@@ -146,16 +130,18 @@ export function invalidateGlobalSkillsCache() {
146
130
  }
147
131
  /**
148
132
  * Discover project-level skills from .agents/skills/ in cwd hierarchy.
149
- * Scans from cwd up to git root.
133
+ * Walks from cwd up to $HOME (or filesystem root if cwd is outside HOME).
134
+ * Git boundaries are ignored — nested repos under a skills-bearing parent
135
+ * would otherwise hide the parent's skills.
150
136
  */
151
137
  export function discoverProjectSkills(cwd) {
152
138
  const seen = new Set();
153
139
  const skills = [];
154
- const gitRoot = findGitRoot(cwd);
140
+ const home = path.resolve(os.homedir());
155
141
  let current = path.resolve(cwd);
156
142
  while (true) {
157
143
  addUnique(skills, scanDir(path.join(current, ".agents", "skills")), seen);
158
- if (gitRoot && current === gitRoot)
144
+ if (current === home)
159
145
  break;
160
146
  const parent = path.dirname(current);
161
147
  if (parent === current)
@@ -1,4 +1,8 @@
1
1
  import { type ChildProcess } from "node:child_process";
2
+ /** Resolve a usable bash binary, or null if none is on PATH.
3
+ * Unix: `/bin/bash` (canonical, present on every Linux/macOS install).
4
+ * Windows: probe via `where bash` so Git Bash users keep working. */
5
+ export declare function findBash(): string | null;
2
6
  export interface ExecutorSession {
3
7
  id: string;
4
8
  command: string;
package/dist/executor.js CHANGED
@@ -1,5 +1,20 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawn, spawnSync } from "node:child_process";
2
2
  import { stripAnsi } from "./utils/ansi.js";
3
+ let cachedBashPath;
4
+ /** Resolve a usable bash binary, or null if none is on PATH.
5
+ * Unix: `/bin/bash` (canonical, present on every Linux/macOS install).
6
+ * Windows: probe via `where bash` so Git Bash users keep working. */
7
+ export function findBash() {
8
+ if (cachedBashPath !== undefined)
9
+ return cachedBashPath;
10
+ if (process.platform !== "win32") {
11
+ cachedBashPath = "/bin/bash";
12
+ return cachedBashPath;
13
+ }
14
+ const r = spawnSync("where", ["bash"], { encoding: "utf-8" });
15
+ cachedBashPath = r.status === 0 ? r.stdout.split(/\r?\n/)[0].trim() || null : null;
16
+ return cachedBashPath;
17
+ }
3
18
  const DEFAULT_TIMEOUT = 60_000;
4
19
  const DEFAULT_MAX_OUTPUT = 256 * 1024; // 256KB
5
20
  /**
@@ -29,9 +44,12 @@ export function executeCommand(opts) {
29
44
  if (v !== undefined)
30
45
  env[k] = v;
31
46
  }
47
+ const bashPath = findBash();
32
48
  let child;
33
49
  try {
34
- child = spawn("/bin/bash", ["-c", opts.command], {
50
+ if (!bashPath)
51
+ throw new Error("bash not found on PATH");
52
+ child = spawn(bashPath, ["-c", opts.command], {
35
53
  stdio: ["ignore", "pipe", "pipe"],
36
54
  cwd: opts.cwd,
37
55
  env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-sh",
3
- "version": "0.12.12",
3
+ "version": "0.12.13",
4
4
  "description": "A shell-first terminal where AI is one keystroke away",
5
5
  "type": "module",
6
6
  "main": "dist/core.js",