@technomoron/agent-run 0.99.27 → 0.99.28

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/CHANGES CHANGED
@@ -1,6 +1,15 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ Version 0.99.28 (2026-08-30)
5
+ ----------------------------
6
+ * Add interactive `agent-run setup [org/repo]`, make `agent-run generate` the
7
+ profile-initialization command, and retain `init` as a deprecated alias.
8
+ * Use `~/.agent-run` as the single default config directory, overridable with
9
+ `--configdir`, `AGENT_CONFIG_DIR`, or a project-local `.agent-run.env`.
10
+ * Resolve profiles from `package.json.repository` and avoid rewriting unchanged
11
+ generated files.
12
+
4
13
  Version 0.99.27 (2026-08-11)
5
14
  ----------------------------
6
15
  * Rename the unattended-run option from `--danger` to `--yolo` for Codex and
package/README.md CHANGED
@@ -3,8 +3,7 @@
3
3
  Small wrapper for AI coding CLIs like Codex and Claude.
4
4
 
5
5
  It keeps agent files out of normal repos and stores them in a separate
6
- agent config tree. By default, that tree lives beside owner/repo checkouts
7
- under the same code root.
6
+ agent config tree. By default, that tree lives at `~/.agent-run`.
8
7
 
9
8
  ## Model
10
9
 
@@ -17,31 +16,29 @@ There are three things:
17
16
  Example:
18
17
 
19
18
  ```text
20
- ~/source/
21
- agent-config/
22
- agent-run.defaults.jsonc
23
- org/
24
- my-api/
25
- agent-run.jsonc
26
- local.md.njk # optional
27
- overrides/ # optional
28
- live/
29
- CLAUDE.md
30
- .claude/
31
- bin/
32
- memories/
33
- codex-home/
34
- AGENTS.md
35
- config.toml
36
- skills/
19
+ ~/.agent-run/
20
+ agent-run.defaults.jsonc
37
21
  org/
38
22
  my-api/
23
+ agent-run.jsonc
24
+ local.md.njk # optional
25
+ overrides/ # optional
26
+ live/
27
+ CLAUDE.md
28
+ .claude/
29
+ bin/
30
+ memories/
31
+ codex-home/
32
+ AGENTS.md
33
+ config.toml
34
+ skills/
35
+ ~/source/org/my-api/
39
36
  ```
40
37
 
41
38
  If your source repo is `~/source/org/my-api`, `agent-run` maps it to:
42
39
 
43
40
  ```text
44
- ~/source/agent-config/org/my-api
41
+ ~/.agent-run/org/my-api
45
42
  ```
46
43
 
47
44
  The source repo stays clean. The agent files live in the matching path under
@@ -96,13 +93,15 @@ The mapped path is:
96
93
 
97
94
  1. `AGENT_RUN_PROFILE` in `.agent-run.env`
98
95
  2. GitHub `origin` remote
99
- 3. `package.json.name`
100
- 4. the project path as `[parent]/[current]`
96
+ 3. `package.json.repository` (string or object `url`)
97
+ 4. `package.json.name`
98
+ 5. the project path as `[parent]/[current]`
101
99
 
102
100
  Examples:
103
101
 
104
102
  - `AGENT_RUN_PROFILE=org/my-api` -> `org/my-api`
105
103
  - `AGENT_RUN_PROFILE=unrelated/hello` -> `unrelated/hello`
104
+ - `package.json.repository = "github:org/my-api"` -> `org/my-api`
106
105
  - `package.json.name = "@org/my-api"` -> `org/my-api`
107
106
  - `package.json.name = "my-api"` -> `my-api`
108
107
  - `/work/org/my-api` with none of the above -> `org/my-api`
@@ -124,20 +123,10 @@ AGENT_RUN_PROFILE=org/my-api
124
123
 
125
124
  Config root:
126
125
 
127
- - default for a project at `[code]/owner/repo`: `[code]/agent-config`
128
- - override with `--config-root /path/to/agent-configs`
126
+ - default: `~/.agent-run`
127
+ - override with `--configdir=/path/to/agent-configs`
129
128
  - override with `AGENT_CONFIG_DIR=/path/to/agent-configs`
130
- - override with `AGENT_CONFIG_ROOT=/path/to/agent-configs`
131
- - or set `AGENT_CONFIG_DIR=/path/to/agent-configs` or `AGENT_CONFIG_ROOT=/path/to/agent-configs` in `.agent-run.env`
132
-
133
- For commands without a project root, `agent-run` looks for an existing config
134
- root in these locations:
135
-
136
- - Windows: `~/Documents/code/agent-config`, `~/Documents/code/agent-configs`,
137
- `~/Desktop/code/agent-config`, `~/Desktop/code/agent-configs`,
138
- `C:\code\agent-config`, `C:\code\agent-configs`
139
- - Unix: `~/code/agent-config`, `~/code/agent-configs`, `~/agent-config`,
140
- `~/agent-configs`, `~/.agent-config`, `~/.agent-configs`
129
+ - or set `AGENT_CONFIG_DIR=/path/to/agent-configs` in `.agent-run.env`
141
130
 
142
131
  ## Commands
143
132
 
@@ -186,12 +175,12 @@ to warn and continue for a specific invocation.
186
175
  Initialize mapped files for the current repo:
187
176
 
188
177
  ```sh
189
- agent-run init
178
+ agent-run generate
190
179
  ```
191
180
 
192
181
  This creates `<config-root>/agent-run.defaults.jsonc` when needed and a minimal
193
182
  `agent-run.jsonc` marker for the mapped profile. It does not create empty local
194
- instruction or override files.
183
+ instruction or override files. `agent-run init` remains as a deprecated alias.
195
184
 
196
185
  Edit the source file for the current repo:
197
186
 
@@ -209,19 +198,18 @@ agent-run update
209
198
  ```
210
199
 
211
200
  `update` requires an existing profile marker or local/legacy instruction file;
212
- it does not silently scaffold an unconfigured project. Use `agent-run init` or
201
+ it does not silently scaffold an unconfigured project. Use `agent-run generate` or
213
202
  the tool command's `--create` option first.
214
203
 
215
204
  Regenerate every profile under the config root without requiring matching code
216
205
  checkouts:
217
206
 
218
207
  ```sh
219
- agent-run update --all ~/.agent-config
208
+ agent-run update --all ~/.agent-run
220
209
  ```
221
210
 
222
211
  If the config root argument is omitted, `update --all` uses the normal configured
223
- root. Without a project path, that falls back to `$HOME/.agent-config`; for
224
- project commands, the default is `[code]/agent-config`.
212
+ root. Without an override, the root is `$HOME/.agent-run`.
225
213
 
226
214
  You can also generate from a tool command and stop before launch:
227
215
 
@@ -245,27 +233,28 @@ Per-profile override templates can be placed in:
245
233
  - `overrides/codex-config.toml.njk`
246
234
  - `overrides/claude-settings.json.njk`
247
235
 
248
- ## Starter Config
236
+ ## Default Config Tree
249
237
 
250
- A complete starter lives in `examples/basic-config`. It includes a minimal
238
+ A complete default tree lives in `examples/basic-config`. It includes a minimal
251
239
  project, `.agent-run.env`, root manifest defaults, global templates and
252
240
  snippets, profile-local templates, skill templates, a personal memory skill,
253
241
  profile skill overrides, tool config overrides, guardrails, checks, and
254
242
  generated runtime paths.
255
243
 
256
- To create the packaged starter agent files in your resolved config root:
244
+ To install the default tree and detected profile in your resolved config root:
257
245
 
258
246
  ```sh
259
247
  agent-run setup
260
248
  ```
261
249
 
262
- Or choose a destination:
250
+ Or select a profile non-interactively:
263
251
 
264
252
  ```sh
265
- agent-run setup ~/.agent-config
253
+ agent-run setup myorg/myrepo
266
254
  ```
267
255
 
268
- The copy skips files that already exist, so local edits are preserved.
256
+ Use `--configdir=DIR` to choose a different destination. The copy skips files
257
+ that already exist, so local edits are preserved.
269
258
 
270
259
  Try it from a checkout:
271
260
 
@@ -298,7 +287,7 @@ agent-run check --all ~/source
298
287
  Migrate an existing config tree to the manifest/template layout:
299
288
 
300
289
  ```sh
301
- agent-run migrate-config ~/.agent-config
290
+ agent-run migrate-config ~/.agent-run
302
291
  ```
303
292
 
304
293
  This preserves `AGENTS-MODS.md`, creates `local.md.njk` and a sparse
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ function applyGlobalOptions(options) {
25
25
  }
26
26
  function extractCommand(args) {
27
27
  if (args.length === 0) {
28
- (0, utils_1.fail)('usage: agent-run <codex|claude|check|init|edit|update> [options] (run with --help for details)');
28
+ (0, utils_1.fail)('usage: agent-run <codex|claude|check|setup|generate|edit|update> [options] (run with --help for details)');
29
29
  }
30
30
  if (isHelpFlag(args[0])) {
31
31
  printHelp('general');
@@ -50,6 +50,8 @@ function parseCommand(command, args) {
50
50
  return parseCheckCommand(args);
51
51
  case 'init':
52
52
  return parseInitCommand(args);
53
+ case 'generate':
54
+ return parseGenerateCommand(args);
53
55
  case 'setup':
54
56
  return parseSetupCommand(args);
55
57
  case 'edit':
@@ -85,18 +87,18 @@ function extractGlobalOptions(argv) {
85
87
  else if (arg === '-v' || arg === '--verbose') {
86
88
  options.verbose = true;
87
89
  }
88
- else if (arg === '--config-root') {
90
+ else if (arg === '--configdir') {
89
91
  const nextArg = argv[index + 1];
90
92
  if (nextArg === undefined) {
91
- (0, utils_1.fail)('missing value for --config-root');
93
+ (0, utils_1.fail)('missing value for --configdir');
92
94
  }
93
95
  options.configRootOverride = path.resolve(nextArg);
94
96
  index += 1;
95
97
  }
96
- else if (arg.startsWith('--config-root=')) {
97
- const rootValue = arg.slice('--config-root='.length);
98
+ else if (arg.startsWith('--configdir=')) {
99
+ const rootValue = arg.slice('--configdir='.length);
98
100
  if (!rootValue) {
99
- (0, utils_1.fail)('missing value for --config-root');
101
+ (0, utils_1.fail)('missing value for --configdir');
100
102
  }
101
103
  options.configRootOverride = path.resolve(rootValue);
102
104
  }
@@ -196,8 +198,24 @@ function parseCheckCommand(inputArgs) {
196
198
  function parseInitCommand(inputArgs) {
197
199
  return { command: 'init', targetPath: parseSinglePath(inputArgs, 'init') };
198
200
  }
201
+ function parseGenerateCommand(inputArgs) {
202
+ return { command: 'generate', targetPath: parseSinglePath(inputArgs, 'generate') };
203
+ }
199
204
  function parseSetupCommand(inputArgs) {
200
- return { command: 'setup', targetPath: parseSinglePath(inputArgs, 'setup', (0, project_1.defaultConfigRoot)()) };
205
+ let profile = null;
206
+ for (const arg of inputArgs) {
207
+ if (isHelpFlag(arg)) {
208
+ printHelp('setup');
209
+ }
210
+ if (arg.startsWith('--')) {
211
+ (0, utils_1.fail)(`unknown setup option: ${arg}`);
212
+ }
213
+ if (profile !== null) {
214
+ (0, utils_1.fail)('setup accepts at most one profile argument');
215
+ }
216
+ profile = arg;
217
+ }
218
+ return { command: 'setup', profile };
201
219
  }
202
220
  function parseEditCommand(inputArgs) {
203
221
  return { command: 'edit', targetPath: parseSinglePath(inputArgs, 'edit') };
@@ -274,8 +292,9 @@ function renderHelp(topic) {
274
292
  ' --all Check every repo under path',
275
293
  ''
276
294
  ],
277
- init: ['Usage:', ' agent-run init [path]', '', 'Create a minimal mapped profile marker and render generated files.', ''],
278
- setup: ['Usage:', ' agent-run setup [config-root]', '', 'Create starter agent configuration files in the config root.', ''],
295
+ init: ['Usage:', ' agent-run init [path]', '', 'Deprecated alias for `agent-run generate [path]`.', ''],
296
+ generate: ['Usage:', ' agent-run generate [path]', '', 'Create a minimal mapped profile marker and render generated files.', ''],
297
+ setup: ['Usage:', ' agent-run setup [org/repo]', '', 'Install the default config tree and selected profile.', ''],
279
298
  edit: ['Usage:', ' agent-run edit [path]', '', 'Create or open the editable local profile template.', ''],
280
299
  update: [
281
300
  'Usage:',
@@ -300,16 +319,16 @@ function renderHelp(topic) {
300
319
  `agent-run ${agentRunVersion()}`,
301
320
  '',
302
321
  'Usage:',
303
- ' agent-run <codex|claude|check|setup|init|edit|update> [options]',
322
+ ' agent-run <codex|claude|check|setup|generate|edit|update> [options]',
304
323
  '',
305
324
  'Commands:',
306
325
  ' codex [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [--network] [args...]',
307
326
  ' Run codex with generated private config',
308
327
  ' claude [--none] [--create] [--local] [--show] [--generate] [--yolo] [args...]',
309
328
  ' Run claude with generated private config',
310
- ' setup [config-root] Create starter files in the agent-config root',
329
+ ' setup [org/repo] Install the default tree and selected profile',
311
330
  ' check [--all] [path] Validate generated profile output',
312
- ' init [path] Create a sparse profile marker and render output',
331
+ ' generate [path] Create a sparse profile marker and render output',
313
332
  ' edit [path] Create or open local.md.njk',
314
333
  ' update [--all] [path] Regenerate existing profile output',
315
334
  ' migrate-config [config-root] Convert existing config tree layout',
@@ -318,7 +337,7 @@ function renderHelp(topic) {
318
337
  ' -h, --help Show this help text',
319
338
  ' -V, --version Show the agent-run version',
320
339
  ' -v, --verbose Print path resolution and wrapper actions',
321
- ' --config-root DIR Override the agent-config root',
340
+ ' --configdir=DIR Override the config directory',
322
341
  '',
323
342
  'Run wrapper options:',
324
343
  ' --local Warn about local AI files instead of failing',
@@ -351,7 +370,7 @@ function normalizeCommandName(value) {
351
370
  if (base === 'agent-run') {
352
371
  return null;
353
372
  }
354
- return ['codex', 'claude', 'check', 'setup', 'init', 'edit', 'update', 'migrate-config'].includes(base)
373
+ return ['codex', 'claude', 'check', 'setup', 'generate', 'init', 'edit', 'update', 'migrate-config'].includes(base)
355
374
  ? base
356
375
  : null;
357
376
  }
package/dist/commands.js CHANGED
@@ -22,7 +22,10 @@ function dispatch(command) {
22
22
  runCheck(command);
23
23
  return;
24
24
  case 'init':
25
- runInit(command);
25
+ runGenerate(command);
26
+ return;
27
+ case 'generate':
28
+ runGenerate(command);
26
29
  return;
27
30
  case 'setup':
28
31
  runSetup(command);
@@ -65,7 +68,7 @@ function runTool(parsed) {
65
68
  return;
66
69
  }
67
70
  if (wrapperArgs.create) {
68
- runInit({ command: 'init', targetPath: projectRoot });
71
+ runGenerate({ command: 'generate', targetPath: projectRoot });
69
72
  }
70
73
  const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
71
74
  const agentDir = path.join(configRoot, profile);
@@ -110,7 +113,7 @@ function requireProfile(tool, agentDir) {
110
113
  }
111
114
  const label = tool ?? 'agent';
112
115
  process.stderr.write(`agent-run: no ${label} profile found for this project: ${agentDir}\n`);
113
- process.stderr.write('agent-run: run `agent-run init` or use the tool with `--create`.\n');
116
+ process.stderr.write('agent-run: run `agent-run generate` or use the tool with `--create`.\n');
114
117
  process.exit(1);
115
118
  }
116
119
  function checkLocalAiFiles(command, projectRoot, agentDir, context, allowLocal) {
@@ -146,8 +149,11 @@ function ensureToolEnabled(context, command) {
146
149
  (0, utils_1.fail)(`${command} is disabled for agent-run profile ${context.profile}`);
147
150
  }
148
151
  }
149
- function runInit(parsed) {
150
- const projectRoot = activeProject(parsed.targetPath, 'init');
152
+ function runGenerate(parsed) {
153
+ if (parsed.command === 'init') {
154
+ process.stderr.write('agent-run: `init` is deprecated; use `agent-run generate [path]` instead.\n');
155
+ }
156
+ const projectRoot = activeProject(parsed.targetPath, 'generate');
151
157
  if (projectRoot === null) {
152
158
  return;
153
159
  }
@@ -155,12 +161,72 @@ function runInit(parsed) {
155
161
  printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir));
156
162
  }
157
163
  function runSetup(parsed) {
164
+ const projectRoot = (0, project_1.findProjectRoot)(process.cwd());
165
+ const detectedProfile = (0, project_1.resolveProfile)(projectRoot);
166
+ const profile = parsed.profile === null ? confirmProfile(detectedProfile) : requireValidProfile(parsed.profile);
167
+ const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
158
168
  const sourcePath = (0, config_tree_1.starterConfigRootPath)();
159
169
  if (!fs.existsSync(sourcePath)) {
160
170
  (0, utils_1.fail)(`starter config skeleton not found: ${sourcePath}`);
161
171
  }
162
- (0, config_tree_1.copySkeletonTree)(sourcePath, path.resolve(parsed.targetPath));
163
- process.stdout.write(`OK created starter agent config in ${path.resolve(parsed.targetPath)}\n`);
172
+ (0, config_tree_1.copySkeletonTree)(sourcePath, configRoot, new Set(['starter']));
173
+ const agentDir = path.join(configRoot, profile);
174
+ (0, config_tree_1.copySkeletonTree)(path.join(sourcePath, 'starter', 'basic-project'), agentDir);
175
+ if (profile !== detectedProfile) {
176
+ writeProjectProfileMapping(projectRoot, profile);
177
+ }
178
+ printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir, { configRoot, profile }));
179
+ process.stdout.write(`OK installed default config tree at ${configRoot}\n`);
180
+ }
181
+ function requireValidProfile(value) {
182
+ const parsed = (0, project_1.parseProfile)(value, 'setup profile');
183
+ if (parsed.profile === null) {
184
+ (0, utils_1.fail)(parsed.reason);
185
+ }
186
+ return parsed.profile;
187
+ }
188
+ function confirmProfile(detectedProfile) {
189
+ process.stdout.write(`Detected profile: ${detectedProfile}\n`);
190
+ const confirmation = promptLine('Is this correct? [Y/n] ').trim().toLowerCase();
191
+ if (confirmation === '' || confirmation === 'y' || confirmation === 'yes') {
192
+ return detectedProfile;
193
+ }
194
+ return requireValidProfile(promptLine('Profile (org/repo): '));
195
+ }
196
+ function promptLine(prompt) {
197
+ if (!process.stdin.isTTY) {
198
+ (0, utils_1.fail)('setup requires a profile argument when input is not interactive');
199
+ }
200
+ process.stdout.write(prompt);
201
+ const bytes = [];
202
+ const buffer = Buffer.alloc(1);
203
+ while (fs.readSync(process.stdin.fd, buffer, 0, 1, null) === 1) {
204
+ if (buffer[0] === 10) {
205
+ break;
206
+ }
207
+ if (buffer[0] !== 13) {
208
+ bytes.push(buffer[0] ?? 0);
209
+ }
210
+ }
211
+ return Buffer.from(bytes).toString('utf8');
212
+ }
213
+ function writeProjectProfileMapping(projectRoot, profile) {
214
+ const envPath = path.join(projectRoot, constants_1.ENV_FILE_NAME);
215
+ const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8').replace(/\r\n/g, '\n') : '';
216
+ const lines = existing.length > 0 ? existing.replace(/\n+$/, '').split('\n') : [];
217
+ const profileLine = `AGENT_RUN_PROFILE=${profile}`;
218
+ const index = lines.findIndex((line) => /^\s*AGENT_RUN_PROFILE\s*=/.test(line));
219
+ if (index >= 0) {
220
+ lines[index] = profileLine;
221
+ }
222
+ else {
223
+ lines.push(profileLine);
224
+ }
225
+ const content = `${lines.join('\n')}\n`;
226
+ if (content !== existing) {
227
+ fs.writeFileSync(envPath, content, 'utf8');
228
+ (0, utils_1.verbose)(`write ${envPath}`);
229
+ }
164
230
  }
165
231
  function runEdit(parsed) {
166
232
  const projectRoot = activeProject(parsed.targetPath, 'edit');
@@ -153,14 +153,17 @@ function migrateLooseFiles(agentDir, pattern, targetDirName) {
153
153
  function starterConfigRootPath() {
154
154
  return path.resolve(__dirname, '..', 'examples', 'basic-config', 'agent-config');
155
155
  }
156
- function copySkeletonTree(sourceDir, targetDir) {
156
+ function copySkeletonTree(sourceDir, targetDir, excludedNames = new Set()) {
157
157
  fs.mkdirSync(targetDir, { recursive: true });
158
158
  const entries = fs.readdirSync(sourceDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
159
159
  for (const entry of entries) {
160
+ if (excludedNames.has(entry.name)) {
161
+ continue;
162
+ }
160
163
  const sourcePath = path.join(sourceDir, entry.name);
161
164
  const targetPath = path.join(targetDir, entry.name === 'gitignore' ? '.gitignore' : entry.name);
162
165
  if (entry.isDirectory()) {
163
- copySkeletonTree(sourcePath, targetPath);
166
+ copySkeletonTree(sourcePath, targetPath, excludedNames);
164
167
  }
165
168
  else if (entry.isFile() && !fs.existsSync(targetPath)) {
166
169
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
package/dist/constants.js CHANGED
@@ -11,7 +11,7 @@ exports.LIVE_DIR_NAME = 'live';
11
11
  exports.CONFIG_ROOT_OVERRIDE_ENV = 'AGENT_RUN_CONFIG_ROOT_OVERRIDE';
12
12
  exports.CONFIG_DIR_ENV = 'AGENT_CONFIG_DIR';
13
13
  exports.VERBOSE_ENV = 'AGENT_RUN_VERBOSE';
14
- exports.PACKAGE_VERSION = '0.99.25';
14
+ exports.PACKAGE_VERSION = '0.99.28';
15
15
  exports.UNEXPANDED_TEMPLATE_RE = /\{\{[^}]+\}\}|\{%[^%]+%\}/;
16
16
  exports.LOCAL_AI_FILE_NAMES = new Set([
17
17
  'AGENTS.md',
package/dist/project.js CHANGED
@@ -164,6 +164,11 @@ function profileFromPackage(projectRoot) {
164
164
  if (fs.existsSync(packagePath)) {
165
165
  try {
166
166
  const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
167
+ const repositoryProfile = parsePackageRepositoryProfile(pkg.repository);
168
+ if (repositoryProfile !== null) {
169
+ (0, utils_1.verbose)(`using package.json repository profile=${repositoryProfile}`);
170
+ return parseProfile(repositoryProfile, `${packagePath} repository`);
171
+ }
167
172
  if (typeof pkg.name === 'string' && pkg.name.length > 0) {
168
173
  (0, utils_1.verbose)(`using package.json name=${pkg.name}`);
169
174
  return parseProfile(pkg.name.startsWith('@') ? pkg.name.slice(1) : pkg.name, `${packagePath} name`);
@@ -183,6 +188,28 @@ function profileFromPackage(projectRoot) {
183
188
  }
184
189
  return { profile: null, reason: `cannot resolve agent profile from project path: ${projectRoot}` };
185
190
  }
191
+ function parsePackageRepositoryProfile(repository) {
192
+ let repositoryPath = null;
193
+ if (typeof repository === 'string') {
194
+ repositoryPath = repository;
195
+ }
196
+ else if (repository !== null && typeof repository === 'object' && 'url' in repository) {
197
+ const url = repository.url;
198
+ if (typeof url === 'string') {
199
+ repositoryPath = url;
200
+ }
201
+ }
202
+ if (repositoryPath === null) {
203
+ return null;
204
+ }
205
+ const value = repositoryPath.trim();
206
+ const githubProfile = parseGitHubRemoteProfile(value.replace(/^git\+/, ''));
207
+ if (githubProfile !== null) {
208
+ return githubProfile;
209
+ }
210
+ const shorthand = /^(?:github:)?([^/:\s]+)\/([^/\s]+?)(?:\.git)?$/.exec(value);
211
+ return shorthand?.[1] && shorthand[2] ? `${shorthand[1]}/${shorthand[2]}` : null;
212
+ }
186
213
  function resolveGitHubProfile(projectRoot) {
187
214
  const result = childProcess.spawnSync('git', ['-C', projectRoot, 'config', '--get', 'remote.origin.url'], {
188
215
  encoding: 'utf8',
@@ -271,17 +298,11 @@ function defaultConfigRoot(projectRoot, options = {}) {
271
298
  if (explicit !== null) {
272
299
  return explicit;
273
300
  }
274
- if (projectRoot) {
275
- const projectConfigRoot = defaultCodeConfigRoot(projectRoot);
276
- if (options.preferProjectRoot || fs.existsSync(projectConfigRoot)) {
277
- return projectConfigRoot;
278
- }
279
- return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? projectConfigRoot;
280
- }
281
- return findExistingConfigRoot(defaultConfigRootSearchCandidates()) ?? path.join(os.homedir(), '.agent-config');
301
+ void options;
302
+ return path.join(os.homedir(), '.agent-run');
282
303
  }
283
304
  function explicitConfigRoot(projectRoot) {
284
- for (const value of [process.env[constants_1.CONFIG_ROOT_OVERRIDE_ENV], process.env[constants_1.CONFIG_DIR_ENV], process.env.AGENT_CONFIG_ROOT]) {
305
+ for (const value of [process.env[constants_1.CONFIG_ROOT_OVERRIDE_ENV], process.env[constants_1.CONFIG_DIR_ENV]]) {
285
306
  if (value) {
286
307
  return path.resolve(value);
287
308
  }
@@ -290,21 +311,9 @@ function explicitConfigRoot(projectRoot) {
290
311
  return null;
291
312
  }
292
313
  const env = readAgentRunEnv(projectRoot);
293
- const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim() || env.AGENT_CONFIG_ROOT?.trim();
314
+ const localValue = env[constants_1.CONFIG_DIR_ENV]?.trim();
294
315
  return localValue ? path.resolve(projectRoot, localValue) : null;
295
316
  }
296
- function defaultCodeConfigRoot(projectRoot) {
297
- const resolvedProjectRoot = path.resolve(projectRoot);
298
- const ownerDir = path.dirname(resolvedProjectRoot);
299
- const codeRoot = path.dirname(ownerDir);
300
- if (ownerDir === resolvedProjectRoot || codeRoot === ownerDir) {
301
- return path.join(os.homedir(), '.agent-config');
302
- }
303
- return path.join(codeRoot, 'agent-config');
304
- }
305
- function findExistingConfigRoot(candidates) {
306
- return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
307
- }
308
317
  function defaultConfigRootSearchCandidates(_projectRoot, options = {}) {
309
318
  const platform = options.platform ?? process.platform;
310
319
  const pathApi = platform === 'win32' ? path.win32 : path.posix;
package/dist/renderer.js CHANGED
@@ -101,11 +101,18 @@ function syncAgentProfile(projectRoot, agentDir, options) {
101
101
  }
102
102
  function writeGeneratedFile(filePath, content, executable) {
103
103
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
104
- fs.writeFileSync(filePath, content, 'utf8');
104
+ const unchanged = fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === content;
105
+ if (!unchanged) {
106
+ fs.writeFileSync(filePath, content, 'utf8');
107
+ (0, utils_1.verbose)(`write ${filePath}`);
108
+ }
105
109
  if (executable && !constants_1.IS_WINDOWS) {
106
- fs.chmodSync(filePath, 0o755);
110
+ const currentMode = fs.statSync(filePath).mode & 0o777;
111
+ if (currentMode !== 0o755) {
112
+ fs.chmodSync(filePath, 0o755);
113
+ (0, utils_1.verbose)(`chmod 755 ${filePath}`);
114
+ }
107
115
  }
108
- (0, utils_1.verbose)(`write ${filePath}`);
109
116
  }
110
117
  function removeStaleGeneratedEntries(rendered) {
111
118
  const expectedFiles = new Set(rendered.files.map((file) => path.resolve(file.path)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.27",
3
+ "version": "0.99.28",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js"