@technomoron/agent-run 0.99.32 → 0.99.33

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/dist/guards.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderGuardShims = renderGuardShims;
4
4
  const path = require("path");
5
5
  const constants_1 = require("./constants");
6
+ const GIT_TEST_REPO_MARKER = '.agent-run-test-repo';
6
7
  function renderGuardShims(context) {
7
8
  const names = [];
8
9
  if (context.guardrails.blockGitWrite) {
@@ -34,16 +35,46 @@ function posixGitShim() {
34
35
  return [
35
36
  '#!/usr/bin/env bash',
36
37
  'set -euo pipefail',
38
+ 'agent_run_test_repo() {',
39
+ ' local dir="$PWD"',
40
+ ' while [ "$#" -gt 0 ]; do',
41
+ ' case "$1" in',
42
+ ' -C)',
43
+ ' [ "$#" -ge 2 ] || return 1',
44
+ ' dir="$(cd -- "$dir" 2>/dev/null && cd -- "$2" 2>/dev/null && pwd -P)" || return 1',
45
+ ' shift 2 ;;',
46
+ ' -c)',
47
+ ' [ "$#" -ge 2 ] || return 1',
48
+ ' shift 2 ;;',
49
+ ' --git-dir*|--work-tree*) return 1 ;;',
50
+ ' -*) shift ;;',
51
+ ' *) break ;;',
52
+ ' esac',
53
+ ' done',
54
+ ' dir="$(cd -- "$dir" 2>/dev/null && pwd -P)" || return 1',
55
+ ' while :; do',
56
+ ` if [ -e "$dir/${GIT_TEST_REPO_MARKER}" ]; then return 0; fi`,
57
+ ' [ "$dir" != "/" ] || return 1',
58
+ ' dir="${dir%/*}"',
59
+ ' [ -n "$dir" ] || dir="/"',
60
+ ' done',
61
+ '}',
62
+ 'blocked=""',
63
+ 'skip_value=0',
37
64
  'for arg in "$@"; do',
65
+ ' if [ "$skip_value" = 1 ]; then skip_value=0; continue; fi',
38
66
  ' case "$arg" in',
39
- ' commit|tag|push)',
40
- ' if [ "${AGENT_RUN_ALLOW_GIT_WRITE:-}" != "1" ]; then',
41
- ' echo "agent-run: blocked git $arg. Review and run it manually, or set AGENT_RUN_ALLOW_GIT_WRITE=1 for this invocation." >&2',
42
- ' exit 42',
43
- ' fi',
44
- ' ;;',
67
+ ' -C|-c|--git-dir|--work-tree|--namespace|--super-prefix|--config-env) skip_value=1; continue ;;',
68
+ ' --help|-h|--version|-v|--html-path|--man-path|--info-path|--) break ;;',
69
+ ' -*) continue ;;',
70
+ ' commit|tag|push) blocked="$arg"; break ;;',
71
+ ' *) break ;;',
45
72
  ' esac',
46
73
  'done',
74
+ 'if [ -n "$blocked" ] && [ "${AGENT_RUN_ALLOW_GIT_WRITE:-}" != "1" ] && ! agent_run_test_repo "$@"; then',
75
+ ' echo "agent-run: blocked git $blocked. Review and run it manually, or set AGENT_RUN_ALLOW_GIT_WRITE=1 for this invocation." >&2',
76
+ ' exit 42',
77
+ 'fi',
47
78
  ...posixRunRealCommand('git'),
48
79
  ''
49
80
  ].join('\n');
@@ -105,16 +136,31 @@ function posixRunRealCommand(tool) {
105
136
  function windowsShim(name) {
106
137
  const executable = name === 'npm' || name === 'pnpm' ? `${name}.cmd` : `${name}.exe`;
107
138
  const guard = name === 'git' ? windowsGitGuard() : name === 'gh' ? windowsGhGuard() : windowsPublishGuard(name);
139
+ const helpers = name === 'git' ? windowsGitTestRepo() : [];
108
140
  return [
109
141
  '@echo off',
142
+ 'setlocal DisableDelayedExpansion',
110
143
  ...guard,
111
144
  ':run',
145
+ 'for %%D in ("%~dp0.") do set "AGENT_RUN_GUARD_DIR=%%~fD"',
112
146
  'if defined AGENT_RUN_REAL_PATH (',
113
147
  ' set "PATH=%AGENT_RUN_REAL_PATH%"',
114
148
  ') else (',
115
- ' for /f "tokens=1,* delims=;" %%A in ("%PATH%") do set "PATH=%%B"',
149
+ ' for /f "tokens=1,* delims=;" %%A in ("%PATH%") do for %%D in ("%%~A\\.") do if /I "%%~fD"=="%AGENT_RUN_GUARD_DIR%" set "PATH=%%B"',
150
+ ')',
151
+ 'set "AGENT_RUN_COMMAND="',
152
+ `for %%I in (${executable}) do set "AGENT_RUN_COMMAND=%%~$PATH:I"`,
153
+ 'if not defined AGENT_RUN_COMMAND (',
154
+ ` echo agent-run: cannot find the real ${name} command outside the guard directory. 1>&2`,
155
+ ' exit /b 127',
156
+ ')',
157
+ 'if /I "%AGENT_RUN_COMMAND%"=="%~f0" (',
158
+ ` echo agent-run: refused recursive ${name} guard resolution. 1>&2`,
159
+ ' exit /b 127',
116
160
  ')',
117
- `${executable} %*`,
161
+ '"%AGENT_RUN_COMMAND%" %*',
162
+ 'exit /b %ERRORLEVEL%',
163
+ ...helpers,
118
164
  ''
119
165
  ].join('\n');
120
166
  }
@@ -122,17 +168,77 @@ function windowsGitGuard() {
122
168
  return [
123
169
  ':scan',
124
170
  'if "%~1"=="" goto run',
171
+ 'if "%~1"=="-C" goto skip_git_value',
172
+ 'if "%~1"=="-c" goto skip_git_value',
173
+ 'if "%~1"=="--git-dir" goto skip_git_value',
174
+ 'if "%~1"=="--work-tree" goto skip_git_value',
175
+ 'if "%~1"=="--namespace" goto skip_git_value',
176
+ 'if "%~1"=="--super-prefix" goto skip_git_value',
177
+ 'if "%~1"=="--config-env" goto skip_git_value',
178
+ 'if "%~1"=="--help" goto run',
179
+ 'if "%~1"=="-h" goto run',
180
+ 'if "%~1"=="--version" goto run',
181
+ 'if "%~1"=="-v" goto run',
182
+ 'if "%~1"=="--html-path" goto run',
183
+ 'if "%~1"=="--man-path" goto run',
184
+ 'if "%~1"=="--info-path" goto run',
185
+ 'if "%~1"=="--" goto run',
125
186
  'if /I "%~1"=="commit" goto block_git',
126
187
  'if /I "%~1"=="tag" goto block_git',
127
188
  'if /I "%~1"=="push" goto block_git',
189
+ 'set "ARG=%~1"',
190
+ 'if not "%ARG:~0,1%"=="-" goto run',
191
+ 'shift',
192
+ 'goto scan',
193
+ ':skip_git_value',
194
+ 'shift',
195
+ 'if "%~1"=="" goto run',
128
196
  'shift',
129
197
  'goto scan',
130
198
  ':block_git',
131
199
  'if "%AGENT_RUN_ALLOW_GIT_WRITE%"=="1" goto run',
200
+ 'call :agent_run_test_repo %*',
201
+ 'if not errorlevel 1 goto run',
132
202
  'echo agent-run: blocked git write. Review and run it manually, or set AGENT_RUN_ALLOW_GIT_WRITE=1 for this invocation. 1>&2',
133
203
  'exit /b 42'
134
204
  ];
135
205
  }
206
+ function windowsGitTestRepo() {
207
+ return [
208
+ ':agent_run_test_repo',
209
+ 'setlocal enabledelayedexpansion',
210
+ 'set "TARGET=%CD%"',
211
+ ':agent_run_scan',
212
+ 'if "%~1"=="" goto agent_run_walk',
213
+ 'if "%~1"=="-C" goto agent_run_chdir',
214
+ 'if "%~1"=="-c" (shift & shift & goto agent_run_scan)',
215
+ 'set "ARG=%~1"',
216
+ 'if /I "!ARG:~0,9!"=="--git-dir" goto agent_run_deny',
217
+ 'if /I "!ARG:~0,11!"=="--work-tree" goto agent_run_deny',
218
+ 'if "!ARG:~0,1!"=="-" (shift & goto agent_run_scan)',
219
+ 'goto agent_run_walk',
220
+ ':agent_run_chdir',
221
+ 'if "%~2"=="" goto agent_run_deny',
222
+ 'pushd "!TARGET!" 2>nul',
223
+ 'if errorlevel 1 goto agent_run_deny',
224
+ 'pushd "%~2" 2>nul',
225
+ 'if errorlevel 1 (popd & goto agent_run_deny)',
226
+ 'set "TARGET=!CD!"',
227
+ 'popd',
228
+ 'popd',
229
+ 'shift',
230
+ 'shift',
231
+ 'goto agent_run_scan',
232
+ ':agent_run_walk',
233
+ `if exist "!TARGET!\\${GIT_TEST_REPO_MARKER}" (endlocal & exit /b 0)`,
234
+ 'for %%D in ("!TARGET!\\..") do set "PARENT=%%~fD"',
235
+ 'if /I "!PARENT!"=="!TARGET!" goto agent_run_deny',
236
+ 'set "TARGET=!PARENT!"',
237
+ 'goto agent_run_walk',
238
+ ':agent_run_deny',
239
+ 'endlocal & exit /b 1'
240
+ ];
241
+ }
136
242
  function windowsGhGuard() {
137
243
  return [
138
244
  'set "SAW_RELEASE=0"',
package/dist/process.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findRealBinary = findRealBinary;
4
- exports.execTool = execTool;
5
4
  exports.execCommand = execCommand;
6
5
  exports.openEditor = openEditor;
7
6
  exports.parseEditorCommand = parseEditorCommand;
@@ -73,10 +72,6 @@ function isAgentRunRedirectShim(filePath) {
73
72
  return false;
74
73
  }
75
74
  }
76
- function execTool(command, args) {
77
- (0, utils_1.verbose)(`exec tool: ${(0, utils_1.formatCommand)(command, args)}`);
78
- execCommand(command, args);
79
- }
80
75
  function execCommand(command, args, env, cwd, onExit) {
81
76
  (0, utils_1.verbose)(`spawn: ${(0, utils_1.formatCommand)(command, args)} cwd=${cwd ?? process.cwd()}`);
82
77
  const child = crossSpawn(command, args, { cwd, env, stdio: 'inherit' });
package/dist/project.js CHANGED
@@ -12,7 +12,6 @@ exports.resolveProfileResult = resolveProfileResult;
12
12
  exports.parseProfile = parseProfile;
13
13
  exports.projectRootForProfile = projectRootForProfile;
14
14
  exports.defaultConfigRoot = defaultConfigRoot;
15
- exports.defaultConfigRootSearchCandidates = defaultConfigRootSearchCandidates;
16
15
  const childProcess = require("child_process");
17
16
  const fs = require("fs");
18
17
  const os = require("os");
@@ -163,7 +162,7 @@ function resolveProfileResult(projectRoot, configRoot = defaultConfigRoot(projec
163
162
  const named = resolved.profile ? (0, config_1.namedBrainProfile)(configRoot, resolved.profile) : null;
164
163
  if (named)
165
164
  return { profile: named, reason: '' };
166
- if (useDefault && (0, config_1.readBrainConfig)(configRoot)?.enabled && !readAgentRunEnv(projectRoot).AGENT_RUN_PROFILE?.trim()) {
165
+ if (useDefault && (0, config_1.readBrainConfig)(configRoot)?.enabled) {
167
166
  const configured = resolved.profile !== null && ['agent-run.jsonc', 'local.md.njk', 'AGENTS-MODS.md']
168
167
  .some((name) => fs.existsSync(path.join(configRoot, resolved.profile, name)));
169
168
  if (!configured)
@@ -172,11 +171,6 @@ function resolveProfileResult(projectRoot, configRoot = defaultConfigRoot(projec
172
171
  return resolved;
173
172
  }
174
173
  function resolveLegacyProfileResult(projectRoot) {
175
- const envProfile = readAgentRunEnv(projectRoot).AGENT_RUN_PROFILE?.trim();
176
- if (envProfile) {
177
- (0, utils_1.verbose)(`using ${constants_1.ENV_FILE_NAME} AGENT_RUN_PROFILE=${envProfile}`);
178
- return parseProfile(envProfile, `${constants_1.ENV_FILE_NAME} AGENT_RUN_PROFILE`);
179
- }
180
174
  const githubProfile = resolveGitHubProfile(projectRoot);
181
175
  if (githubProfile !== null) {
182
176
  (0, utils_1.verbose)(`using GitHub origin profile=${githubProfile}`);
@@ -321,12 +315,11 @@ function parseEnvFile(content) {
321
315
  }
322
316
  return env;
323
317
  }
324
- function defaultConfigRoot(projectRoot, options = {}) {
318
+ function defaultConfigRoot(projectRoot) {
325
319
  const explicit = explicitConfigRoot(projectRoot);
326
320
  if (explicit !== null) {
327
321
  return explicit;
328
322
  }
329
- void options;
330
323
  return path.join(os.homedir(), '.agent-run');
331
324
  }
332
325
  function explicitConfigRoot(projectRoot) {
@@ -342,17 +335,3 @@ function explicitConfigRoot(projectRoot) {
342
335
  const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim();
343
336
  return localValue ? path.resolve(projectRoot, localValue) : null;
344
337
  }
345
- function defaultConfigRootSearchCandidates(_projectRoot, options = {}) {
346
- const platform = options.platform ?? process.platform;
347
- const pathApi = platform === 'win32' ? path.win32 : path.posix;
348
- const homeDir = options.homeDir ?? os.homedir();
349
- const names = ['agent-config', 'agent-configs'];
350
- if (platform === 'win32') {
351
- return [pathApi.join(homeDir, 'Documents', 'code'), pathApi.join(homeDir, 'Desktop', 'code'), 'C:\\code'].flatMap((root) => names.map((name) => pathApi.join(root, name)));
352
- }
353
- return [
354
- ...names.map((name) => pathApi.join(homeDir, 'code', name)),
355
- ...names.map((name) => pathApi.join(homeDir, name)),
356
- ...names.map((name) => pathApi.join(homeDir, `.${name}`))
357
- ];
358
- }
package/dist/renderer.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderProfile = renderProfile;
4
4
  exports.syncAgentProfile = syncAgentProfile;
5
+ exports.syncRenderedProfile = syncRenderedProfile;
5
6
  exports.checkRenderedProfile = checkRenderedProfile;
6
7
  const fs = require("fs");
7
8
  const path = require("path");
@@ -30,8 +31,8 @@ function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = nu
30
31
  const brainEnabled = (0, config_1.readBrainConfig)(configRoot)?.enabled === true;
31
32
  if (brainEnabled && !context.mcpServers['agent-brain']) {
32
33
  context.mcpServers['agent-brain'] = {
33
- transport: 'stdio', enabled: true, command: process.execPath,
34
- args: [path.join(__dirname, 'agent-brain.js'), 'mcp', '--configdir', configRoot, '--cwd', projectRoot, '--socket', (0, config_1.defaultSocketPath)(configRoot)],
34
+ transport: 'stdio', enabled: true, command: 'agent-brain',
35
+ args: ['mcp', '--configdir', configRoot, '--cwd', projectRoot],
35
36
  env: {}, headers: {}
36
37
  };
37
38
  }
@@ -87,6 +88,10 @@ function validateManifestProfile(manifestProfile, inferredProfile, agentDir) {
87
88
  }
88
89
  function syncAgentProfile(projectRoot, agentDir, options) {
89
90
  const rendered = renderProfile(projectRoot, agentDir, false, null, undefined, options);
91
+ return syncRenderedProfile(rendered);
92
+ }
93
+ function syncRenderedProfile(rendered) {
94
+ (0, config_tree_1.ensureConfigRootGitignore)(rendered.configRoot);
90
95
  syncRuntimeDirs(rendered);
91
96
  removeStaleGeneratedEntries(rendered);
92
97
  for (const file of rendered.files) {
package/dist/templates.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createNunjucksEnv = createNunjucksEnv;
4
+ exports.createSkillNunjucksEnv = createSkillNunjucksEnv;
4
5
  exports.buildRenderContext = buildRenderContext;
5
6
  exports.resolveConfigPath = resolveConfigPath;
6
7
  exports.renderTemplateFile = renderTemplateFile;
@@ -19,6 +20,33 @@ function createNunjucksEnv(configRoot) {
19
20
  throwOnUndefined: true
20
21
  });
21
22
  }
23
+ function createSkillNunjucksEnv(configRoot, profileDir, trace) {
24
+ const roots = [path.join(configRoot, 'global', 'skills'), path.join(profileDir, 'skills')];
25
+ class SkillLoader extends nunjucks.Loader {
26
+ getSource(name) {
27
+ const target = path.resolve(configRoot, name);
28
+ if (!roots.some((root) => (0, utils_1.isSamePathOrDescendant)(target, root))) {
29
+ throw new Error(`Skill template must be inside an active skills directory: ${name}`);
30
+ }
31
+ let current = configRoot;
32
+ for (const part of path.relative(configRoot, target).split(path.sep).filter(Boolean)) {
33
+ current = path.join(current, part);
34
+ if (fs.lstatSync(current).isSymbolicLink()) {
35
+ throw new Error(`Symlinks are not supported in skill templates: ${current}`);
36
+ }
37
+ }
38
+ if (fs.statSync(target).size > 1024 * 1024) {
39
+ throw new Error(`Skill template exceeds 1 MiB: ${target}`);
40
+ }
41
+ const src = fs.readFileSync(target, 'utf8');
42
+ trace?.sourceFiles.add(target);
43
+ return { src, path: target, noCache: true };
44
+ }
45
+ }
46
+ return new nunjucks.Environment(new SkillLoader(), {
47
+ autoescape: false, trimBlocks: true, lstripBlocks: true, throwOnUndefined: true
48
+ });
49
+ }
22
50
  function buildRenderContext(projectRoot, profileDir, configRoot, manifest, env) {
23
51
  const date = (0, utils_1.localDateString)();
24
52
  const liveDir = path.join(profileDir, constants_1.LIVE_DIR_NAME);
@@ -84,8 +112,8 @@ function buildPermissionsAllow(manifest) {
84
112
  }
85
113
  return [...allow];
86
114
  }
87
- function resolveConfigPath(configRoot, relativePath, context) {
88
- const rendered = createNunjucksEnv(configRoot).renderString(relativePath, context);
115
+ function resolveConfigPath(configRoot, relativePath, context, env) {
116
+ const rendered = env.renderString(relativePath, context);
89
117
  const resolved = path.resolve(configRoot, rendered);
90
118
  if (!(0, utils_1.isSamePathOrDescendant)(resolved, path.resolve(configRoot))) {
91
119
  throw new Error(`config path escapes config root: ${relativePath}`);
@@ -97,20 +125,20 @@ function resolveRuntimePath(configRoot, pathTemplate, env, context) {
97
125
  return path.isAbsolute(rendered) ? path.resolve(rendered) : path.resolve(configRoot, rendered);
98
126
  }
99
127
  function renderTemplateFile(env, configRoot, templatePath, context, trace) {
100
- const resolvedPath = resolveConfigPath(configRoot, templatePath, context);
128
+ const resolvedPath = resolveConfigPath(configRoot, templatePath, context, env);
101
129
  if (!fs.existsSync(resolvedPath)) {
102
130
  throw new Error(`missing template: ${resolvedPath}`);
103
131
  }
104
132
  const relativePath = path.relative(configRoot, resolvedPath).replace(/\\/g, '/');
105
133
  let content = fs.readFileSync(resolvedPath, 'utf8');
106
- traceTemplateSource(configRoot, resolvedPath, content, context, trace);
134
+ traceTemplateSource(env, configRoot, resolvedPath, content, context, trace);
107
135
  if (path.basename(resolvedPath) === 'AGENTS-MODS.md') {
108
136
  content = renderLegacyAgentsMods(resolvedPath, [], trace);
109
137
  return env.renderString(content, context);
110
138
  }
111
139
  return env.render(relativePath, context);
112
140
  }
113
- function traceTemplateSource(configRoot, sourcePath, content, context, trace, stack = []) {
141
+ function traceTemplateSource(env, configRoot, sourcePath, content, context, trace, stack = []) {
114
142
  if (trace === undefined) {
115
143
  return;
116
144
  }
@@ -126,9 +154,9 @@ function traceTemplateSource(configRoot, sourcePath, content, context, trace, st
126
154
  if (!includePath) {
127
155
  continue;
128
156
  }
129
- const includedPath = resolveConfigPath(configRoot, includePath, context);
157
+ const includedPath = resolveConfigPath(configRoot, includePath, context, env);
130
158
  if (fs.existsSync(includedPath)) {
131
- traceTemplateSource(configRoot, includedPath, fs.readFileSync(includedPath, 'utf8'), context, trace, nextStack);
159
+ traceTemplateSource(env, configRoot, includedPath, fs.readFileSync(includedPath, 'utf8'), context, trace, nextStack);
132
160
  }
133
161
  }
134
162
  }
@@ -14,7 +14,7 @@
14
14
  "triage"
15
15
  ],
16
16
  "overrides": {
17
- "triage": "{{ profile }}/overrides/triage.SKILL.md.njk"
17
+ "triage": "{{ profile }}/skills/triage/SKILL.md.njk"
18
18
  }
19
19
  },
20
20
  "mcp": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.32",
3
+ "version": "0.99.33",
4
4
  "packageManager": "pnpm@10.34.5",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js",
@@ -36,8 +36,7 @@
36
36
  "test:integration": "bash ./scripts/test.sh",
37
37
  "test:release": "bash ./scripts/test-release.sh",
38
38
  "release": "bash ./scripts/release.sh",
39
- "release:check": "bash ./scripts/release-check.sh",
40
- "release:preflight": "pnpm cleanbuild && pnpm test && pnpm release:check -- --local",
39
+ "release:preflight": "pnpm cleanbuild && pnpm test",
41
40
  "release:verify": "bash ./scripts/release-verify.sh"
42
41
  },
43
42
  "author": "Bjorn Erik Jacobsen",
@@ -19,7 +19,6 @@ USAGE
19
19
  }
20
20
 
21
21
  ROOT="$(cd "$(dirname "$0")/.." && pwd)"
22
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
23
22
  INSTALL_LIBEXEC_DIR="${INSTALL_LIBEXEC_DIR:-${INSTALL_LIB_DIR:-/usr/local/libexec/agent-run}}"
24
23
  SYSTEMD_DIR="${SYSTEMD_DIR:-/etc/systemd/system}"
25
24
  SYSTEMD_USER_DIR="${SYSTEMD_USER_DIR:-/etc/systemd/user}"
@@ -30,13 +29,7 @@ SERVICE_FILE="$ROOT/ops/systemd/ai-tools-update.service"
30
29
  TIMER_FILE="$ROOT/ops/systemd/ai-tools-update.timer"
31
30
  BRAIN_HELPER="$ROOT/scripts/ensure-agent-brain.sh"
32
31
  BRAIN_SERVICE="$ROOT/ops/systemd/agent-brain.service"
33
- if [ ! -f "$UPDATE_SCRIPT" ]; then
34
- UPDATE_SCRIPT="$SCRIPT_DIR/update-ai-tools.sh"
35
- SERVICE_FILE="$SCRIPT_DIR/ai-tools-update.service"
36
- TIMER_FILE="$SCRIPT_DIR/ai-tools-update.timer"
37
- BRAIN_HELPER="$SCRIPT_DIR/ensure-agent-brain.sh"
38
- BRAIN_SERVICE="$SCRIPT_DIR/agent-brain.service"
39
- fi
32
+
40
33
 
41
34
  INSTALL_AI_TOOLS=0
42
35
  DRY_RUN=0