@technomoron/agent-run 0.99.18 → 0.99.19

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,11 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ Version 0.99.19 (2026-06-03)
5
+ ----------------------------
6
+ * Link per-profile Codex `auth.json` files to the shared `~/.codex/auth.json` login cache when available.
7
+ * Add `agent-run update --all [config-root]` to regenerate every profile from the config tree without requiring code checkouts.
8
+
4
9
  Version 0.99.18 (2026-05-26)
5
10
  ----------------------------
6
11
  * Remove extensionless Node command shim lookup on Windows so generated guard shims resolve real executables correctly.
package/README.md CHANGED
@@ -119,6 +119,9 @@ from that private directory, passes the project root with `-C`, and keeps
119
119
  generated guard shims on `PATH`. Use `--sandboxed` to request Codex
120
120
  `workspace-write`; add `--network` with `--sandboxed` to set
121
121
  `sandbox_workspace_write.network_access=true`.
122
+ When `~/.codex/auth.json` exists, profile-specific Codex homes link their
123
+ `auth.json` to that shared login cache so changing profiles does not require a
124
+ new ChatGPT login.
122
125
 
123
126
  Claude Code currently has no `--cd` equivalent. `agent-run` keeps Claude's
124
127
  process cwd at the project root, passes the generated `.claude/agent-run-settings.json`
@@ -158,6 +161,16 @@ Regenerate the generated files for the current repo:
158
161
  agent-run update
159
162
  ```
160
163
 
164
+ Regenerate every profile under the config root without requiring matching code
165
+ checkouts:
166
+
167
+ ```sh
168
+ agent-run update --all ~/.agent-config
169
+ ```
170
+
171
+ If the config root argument is omitted, `update --all` uses the normal configured
172
+ root such as `$HOME/.agent-config`.
173
+
161
174
  You can also generate from a tool command and stop before launch:
162
175
 
163
176
  ```sh
package/dist/agent-run.js CHANGED
@@ -318,17 +318,22 @@ function parseEditCommand(inputArgs) {
318
318
  return { command: 'edit', targetPath: path.resolve(targetPath) };
319
319
  }
320
320
  function parseUpdateCommand(inputArgs) {
321
+ let all = false;
321
322
  let targetPath = process.cwd();
322
323
  for (const arg of inputArgs) {
323
324
  if (isHelpFlag(arg)) {
324
325
  printHelp('update');
325
326
  }
327
+ if (arg === '--all') {
328
+ all = true;
329
+ continue;
330
+ }
326
331
  if (arg.startsWith('--')) {
327
332
  fail(`unknown update option: ${arg}`);
328
333
  }
329
334
  targetPath = arg;
330
335
  }
331
- return { command: 'update', targetPath: path.resolve(targetPath) };
336
+ return { all, command: 'update', targetPath: path.resolve(targetPath) };
332
337
  }
333
338
  function parseMigrateConfigCommand(inputArgs) {
334
339
  let configRoot = defaultConfigRoot();
@@ -390,9 +395,10 @@ function renderHelp(topic) {
390
395
  case 'update':
391
396
  return [
392
397
  'Usage:',
393
- ' agent-run update [path]',
398
+ ' agent-run update [--all] [path]',
394
399
  '',
395
400
  'Render generated files, native skills, config, and guard shims for the repo at path.',
401
+ 'With --all, render every profile found under the config root without requiring code checkouts.',
396
402
  ''
397
403
  ].join('\n');
398
404
  case 'migrate-config':
@@ -420,7 +426,7 @@ function renderHelp(topic) {
420
426
  ' check [--all] [path] Validate generated profile output',
421
427
  ' init [path] Create profile source files and render output',
422
428
  ' edit [path] Open local.md.njk or legacy AGENTS-MODS.md',
423
- ' update [path] Regenerate profile output',
429
+ ' update [--all] [path] Regenerate profile output',
424
430
  ' migrate-config [config-root] Convert existing config tree layout',
425
431
  '',
426
432
  'Global options:',
@@ -632,6 +638,10 @@ function runEdit(parsed) {
632
638
  openEditor(editPath);
633
639
  }
634
640
  function runUpdate(parsed) {
641
+ if (parsed.all) {
642
+ runUpdateAll(parsed.targetPath);
643
+ return;
644
+ }
635
645
  const projectRoot = findProjectRoot(parsed.targetPath);
636
646
  verbose(`update target=${parsed.targetPath} projectRoot=${projectRoot}`);
637
647
  if (isIgnoredDir(projectRoot)) {
@@ -652,6 +662,31 @@ function runUpdate(parsed) {
652
662
  const rendered = syncAgentProfile(projectRoot, agentDir);
653
663
  printUpdateSummary(rendered);
654
664
  }
665
+ function runUpdateAll(targetPath) {
666
+ const configRoot = targetPath === path.resolve(process.cwd()) ? defaultConfigRoot() : path.resolve(targetPath);
667
+ verbose(`update --all configRoot=${configRoot}`);
668
+ if (!fs.existsSync(configRoot)) {
669
+ fail(`missing config root: ${configRoot}`);
670
+ }
671
+ ensureConfigRootLayout(configRoot);
672
+ ensureConfigRootGitignore(configRoot);
673
+ ensureDefaultGlobalTemplates(configRoot);
674
+ const profileDirs = findProfileDirs(configRoot);
675
+ let updated = 0;
676
+ for (const agentDir of profileDirs) {
677
+ const profile = path.relative(configRoot, agentDir).replace(/\\/g, '/');
678
+ const projectRoot = projectRootForProfile(profile);
679
+ verbose(`update --all profile=${profile} syntheticProjectRoot=${projectRoot}`);
680
+ ensureProfileOverridesDir(agentDir);
681
+ convertLegacyProfileIfNeeded(configRoot, agentDir, profile);
682
+ createDefaultManifestFile(agentDir, profile);
683
+ createDefaultLocalFile(agentDir, profile);
684
+ syncAgentProfile(projectRoot, agentDir, { configRoot, profile });
685
+ process.stdout.write(`OK ${profile}\n`);
686
+ updated += 1;
687
+ }
688
+ process.stdout.write(`Updated profiles: ${updated}\n`);
689
+ }
655
690
  function runMigrateConfig(parsed) {
656
691
  const configRoot = parsed.configRoot;
657
692
  verbose(`migrate config root=${configRoot}`);
@@ -796,6 +831,23 @@ function findLegacyProfileDirs(configRoot) {
796
831
  });
797
832
  return dirs.sort();
798
833
  }
834
+ function findProfileDirs(configRoot) {
835
+ const dirs = [];
836
+ walkConfigTree(configRoot, 0, (fullPath, entry) => {
837
+ if (!entry.isDirectory()) {
838
+ return undefined;
839
+ }
840
+ if (shouldPruneConfigEntry(entry)) {
841
+ return 'skip';
842
+ }
843
+ if (fs.existsSync(path.join(fullPath, MANIFEST_FILE_NAME)) || fs.existsSync(path.join(fullPath, LOCAL_TEMPLATE_FILE_NAME))) {
844
+ dirs.push(fullPath);
845
+ return 'skip';
846
+ }
847
+ return undefined;
848
+ });
849
+ return dirs.sort();
850
+ }
799
851
  function walkConfigTree(dir, depth, visitor) {
800
852
  const entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
801
853
  for (const entry of entries) {
@@ -1210,9 +1262,9 @@ function copySkeletonTree(sourceDir, targetDir) {
1210
1262
  fs.copyFileSync(sourcePath, targetPath);
1211
1263
  }
1212
1264
  }
1213
- function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = null, trace) {
1214
- const configRoot = defaultConfigRoot(projectRoot);
1215
- const profile = resolveProfile(projectRoot);
1265
+ function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = null, trace, options) {
1266
+ const configRoot = options?.configRoot ?? defaultConfigRoot(projectRoot);
1267
+ const profile = options?.profile ?? resolveProfile(projectRoot);
1216
1268
  const env = createNunjucksEnv(configRoot);
1217
1269
  const manifest = normalizeManifest(loadManifest(configRoot, agentDir, profile, trace), profile);
1218
1270
  if (manifest.profile !== profile) {
@@ -1269,8 +1321,8 @@ function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = nu
1269
1321
  skills: context.skills
1270
1322
  };
1271
1323
  }
1272
- function syncAgentProfile(projectRoot, agentDir) {
1273
- const rendered = renderProfile(projectRoot, agentDir, false);
1324
+ function syncAgentProfile(projectRoot, agentDir, options) {
1325
+ const rendered = renderProfile(projectRoot, agentDir, false, null, undefined, options);
1274
1326
  syncRuntimeDirs(rendered.context);
1275
1327
  for (const file of rendered.files) {
1276
1328
  writeGeneratedFile(file.path, file.content, file.executable ?? false);
@@ -1449,6 +1501,69 @@ function syncRuntimeDirs(context) {
1449
1501
  migrateLiveReviewFiles(context);
1450
1502
  removeLegacyCodexSkillDirs(context);
1451
1503
  removeLegacyCodeReviewSkillDirs(context);
1504
+ ensureSharedCodexAuth(context.paths.codexHomeDir);
1505
+ }
1506
+ function ensureSharedCodexAuth(codexHomeDir) {
1507
+ const sharedAuthPath = path.join(os.homedir(), '.codex', 'auth.json');
1508
+ if (!fs.existsSync(sharedAuthPath)) {
1509
+ return;
1510
+ }
1511
+ const profileAuthPath = path.join(codexHomeDir, 'auth.json');
1512
+ if (path.resolve(profileAuthPath) === path.resolve(sharedAuthPath)) {
1513
+ return;
1514
+ }
1515
+ if (fs.existsSync(profileAuthPath) || isSymlink(profileAuthPath)) {
1516
+ if (isSymlinkTo(profileAuthPath, sharedAuthPath)) {
1517
+ return;
1518
+ }
1519
+ const backupPath = nextBackupPath(profileAuthPath);
1520
+ fs.renameSync(profileAuthPath, backupPath);
1521
+ verbose(`backed up profile Codex auth ${profileAuthPath} -> ${backupPath}`);
1522
+ }
1523
+ fs.mkdirSync(path.dirname(profileAuthPath), { recursive: true });
1524
+ try {
1525
+ fs.symlinkSync(sharedAuthPath, profileAuthPath);
1526
+ verbose(`linked profile Codex auth ${profileAuthPath} -> ${sharedAuthPath}`);
1527
+ }
1528
+ catch (error) {
1529
+ if (IS_WINDOWS) {
1530
+ fs.copyFileSync(sharedAuthPath, profileAuthPath);
1531
+ verbose(`copied shared Codex auth ${sharedAuthPath} -> ${profileAuthPath}`);
1532
+ return;
1533
+ }
1534
+ throw error;
1535
+ }
1536
+ }
1537
+ function isSymlink(filePath) {
1538
+ try {
1539
+ return fs.lstatSync(filePath).isSymbolicLink();
1540
+ }
1541
+ catch {
1542
+ return false;
1543
+ }
1544
+ }
1545
+ function isSymlinkTo(filePath, targetPath) {
1546
+ try {
1547
+ if (!fs.lstatSync(filePath).isSymbolicLink()) {
1548
+ return false;
1549
+ }
1550
+ const linkTarget = fs.readlinkSync(filePath);
1551
+ const resolvedTarget = path.resolve(path.dirname(filePath), linkTarget);
1552
+ return path.resolve(resolvedTarget) === path.resolve(targetPath);
1553
+ }
1554
+ catch {
1555
+ return false;
1556
+ }
1557
+ }
1558
+ function nextBackupPath(filePath) {
1559
+ const timestamp = new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14);
1560
+ let candidate = `${filePath}.bak.${timestamp}`;
1561
+ let index = 1;
1562
+ while (fs.existsSync(candidate) || isSymlink(candidate)) {
1563
+ candidate = `${filePath}.bak.${timestamp}.${index}`;
1564
+ index += 1;
1565
+ }
1566
+ return candidate;
1452
1567
  }
1453
1568
  function removeLegacyCodexSkillDirs(context) {
1454
1569
  const legacyCodexSkillsDir = path.join(context.paths.liveDir, '.agents', 'skills');
@@ -2071,6 +2186,9 @@ function parseProfile(profile, source) {
2071
2186
  }
2072
2187
  return { profile: segments.join('/'), reason: '' };
2073
2188
  }
2189
+ function projectRootForProfile(profile) {
2190
+ return path.join(os.homedir(), ...profile.split('/'));
2191
+ }
2074
2192
  function readAgentRunEnv(dir) {
2075
2193
  const resolvedDir = path.resolve(dir);
2076
2194
  const cached = agentRunEnvCache.get(resolvedDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.18",
3
+ "version": "0.99.19",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js"