@technomoron/agent-run 0.99.13 → 0.99.15

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,14 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ Version 0.99.15 (2026-04-30)
5
+ ----------------------------
6
+ * Add `--generate` for Codex and Claude runs to render generated files without launching the underlying tool.
7
+
8
+ Version 0.99.14 (2026-04-30)
9
+ ----------------------------
10
+ * Add `--show` for Codex and Claude runs to print the tool-specific source templates/includes and generated files without launching the underlying tool.
11
+
4
12
  Version 0.99.13 (2026-04-27)
5
13
  ----------------------------
6
14
  * Render each profile's generated files and runtime state under `live/`, leaving the profile root for source config and templates.
package/README.md CHANGED
@@ -110,6 +110,9 @@ Global flag:
110
110
  `agent-run codex --help` and `agent-run claude --help` still pass `--help`
111
111
  through to the underlying tool.
112
112
 
113
+ For `codex` and `claude`, use `--generate` to generate profile files without
114
+ launching the underlying tool.
115
+
113
116
  Codex defaults to `--danger`, which launches Codex with `-a never -s danger-full-access`,
114
117
  sets `CODEX_HOME` under the private agent directory, starts the Codex process
115
118
  from that private directory, passes the project root with `-C`, and keeps
@@ -155,6 +158,13 @@ Regenerate the generated files for the current repo:
155
158
  agent-run update
156
159
  ```
157
160
 
161
+ You can also generate from a tool command and stop before launch:
162
+
163
+ ```sh
164
+ agent-run codex --generate
165
+ agent-run claude --generate
166
+ ```
167
+
158
168
  This reads `agent-run.jsonc` and Nunjucks templates from the mapped profile
159
169
  directory and rewrites generated files:
160
170
 
package/dist/agent-run.js CHANGED
@@ -202,6 +202,8 @@ function parseRunCommand(command, inputArgs) {
202
202
  none: false,
203
203
  create: false,
204
204
  local: false,
205
+ show: false,
206
+ generate: false,
205
207
  codexSandboxMode: null,
206
208
  codexNetwork: false
207
209
  };
@@ -228,6 +230,14 @@ function parseRunCommand(command, inputArgs) {
228
230
  wrapperArgs.local = true;
229
231
  continue;
230
232
  }
233
+ if (arg === '--show') {
234
+ wrapperArgs.show = true;
235
+ continue;
236
+ }
237
+ if (arg === '--generate') {
238
+ wrapperArgs.generate = true;
239
+ continue;
240
+ }
231
241
  if (arg === '--danger') {
232
242
  if (command !== 'codex') {
233
243
  fail('--danger is only supported for agent-run codex');
@@ -402,9 +412,9 @@ function renderHelp(topic) {
402
412
  ' agent-run --init [config-root]',
403
413
  '',
404
414
  'Commands:',
405
- ' codex [--none] [--create] [--local] [--danger|--sandboxed] [--network] [args...]',
415
+ ' codex [--none] [--create] [--local] [--show] [--generate] [--danger|--sandboxed] [--network] [args...]',
406
416
  ' Run codex with generated private config',
407
- ' claude [--none] [--create] [--local] [args...]',
417
+ ' claude [--none] [--create] [--local] [--show] [--generate] [args...]',
408
418
  ' Run claude with generated private config',
409
419
  ' check [--all] [path] Validate generated profile output',
410
420
  ' init [path] Create profile source files and render output',
@@ -419,8 +429,12 @@ function renderHelp(topic) {
419
429
  ' --config-root DIR Override the agent-config root',
420
430
  ' --init [DIR] Copy the packaged starter config root to DIR (default ~/.agent-config)',
421
431
  '',
422
- 'Codex wrapper options:',
432
+ 'Run wrapper options:',
423
433
  ' --local Warn about local AI files instead of failing',
434
+ ' --show Show read/include and generated files without running the tool',
435
+ ' --generate Generate files without running the tool',
436
+ '',
437
+ 'Codex wrapper options:',
424
438
  ' --danger Run Codex with no sandbox: -a never -s danger-full-access (default)',
425
439
  ' --sandboxed Run Codex with workspace-write sandbox',
426
440
  ' --network Enable network for --sandboxed via config override',
@@ -474,34 +488,59 @@ function normalizeCommandName(value) {
474
488
  }
475
489
  function runTool(parsed) {
476
490
  const { args, command, wrapperArgs } = parsed;
477
- const realBinary = findRealBinary(command);
478
- const permissionArgs = getPermissionArgs(command);
479
491
  const projectRoot = findProjectRoot(process.cwd());
480
492
  verbose(`run ${command}: cwd=${process.cwd()} projectRoot=${projectRoot}`);
493
+ if (wrapperArgs.show && wrapperArgs.none) {
494
+ fail('--show cannot be combined with --none');
495
+ }
496
+ if (wrapperArgs.generate && wrapperArgs.none) {
497
+ fail('--generate cannot be combined with --none');
498
+ }
499
+ if (wrapperArgs.generate && wrapperArgs.show) {
500
+ fail('--generate cannot be combined with --show');
501
+ }
502
+ if (wrapperArgs.show && isIgnoredDir(projectRoot)) {
503
+ fail(`cannot show generated ${command} files for ignored project: ${projectRoot}`);
504
+ }
505
+ if (wrapperArgs.generate && isIgnoredDir(projectRoot)) {
506
+ fail(`cannot generate ${command} files for ignored project: ${projectRoot}`);
507
+ }
481
508
  if (wrapperArgs.none || isIgnoredDir(projectRoot)) {
509
+ const realBinary = findRealBinary(command);
510
+ const permissionArgs = getPermissionArgs(command);
482
511
  verbose(`wrapper bypassed for ${command}${wrapperArgs.none ? ' via --none' : ' because project is ignored'}`);
483
512
  execTool(realBinary, [...permissionArgs, ...args]);
484
513
  return;
485
514
  }
486
515
  const profileResult = resolveProfileResult(projectRoot);
487
- const excludedDir = profileResult.profile === null ? null : resolveAgentDir(projectRoot);
488
- const localAiFiles = findLocalAiFiles(projectRoot, excludedDir);
489
- if (localAiFiles.length > 0) {
490
- if (!wrapperArgs.local) {
491
- failForLocalAiFiles(command, projectRoot, localAiFiles);
492
- }
493
- warnForLocalAiFiles(command, projectRoot, localAiFiles);
494
- }
495
516
  if (profileResult.profile === null) {
496
517
  fail(profileResult.reason);
497
518
  }
498
519
  const profile = profileResult.profile;
499
520
  const configRoot = defaultConfigRoot(projectRoot);
500
521
  const agentDir = resolveAgentDir(projectRoot);
522
+ if (wrapperArgs.show) {
523
+ showToolProfile(command, projectRoot, agentDir);
524
+ return;
525
+ }
501
526
  if (wrapperArgs.create) {
502
527
  runInit({ command: 'init', targetPath: projectRoot });
503
528
  }
504
529
  ensureRunnableProfile(configRoot, agentDir, profile);
530
+ if (wrapperArgs.generate) {
531
+ const rendered = syncAgentProfile(projectRoot, agentDir);
532
+ printUpdateSummary(rendered);
533
+ return;
534
+ }
535
+ const localAiFiles = findLocalAiFiles(projectRoot, agentDir);
536
+ if (localAiFiles.length > 0) {
537
+ if (!wrapperArgs.local) {
538
+ failForLocalAiFiles(command, projectRoot, localAiFiles);
539
+ }
540
+ warnForLocalAiFiles(command, projectRoot, localAiFiles);
541
+ }
542
+ const realBinary = findRealBinary(command);
543
+ const permissionArgs = getPermissionArgs(command);
505
544
  syncAgentProfile(projectRoot, agentDir);
506
545
  if (command === 'codex') {
507
546
  runCodex(realBinary, permissionArgs, agentDir, configRoot, args, projectRoot, wrapperArgs);
@@ -509,6 +548,31 @@ function runTool(parsed) {
509
548
  }
510
549
  runClaude(realBinary, permissionArgs, agentDir, configRoot, args, projectRoot, wrapperArgs);
511
550
  }
551
+ function showToolProfile(command, projectRoot, agentDir) {
552
+ const trace = { sourceFiles: new Set() };
553
+ const rendered = renderProfile(projectRoot, agentDir, true, command, trace);
554
+ const generatedFiles = uniqueSorted(rendered.files.map((file) => file.path));
555
+ const sourceFiles = uniqueSorted([...trace.sourceFiles]);
556
+ process.stdout.write([
557
+ `Agent: ${command}`,
558
+ `Profile: ${rendered.profile}`,
559
+ `Project root: ${projectRoot}`,
560
+ `Profile dir: ${agentDir}`,
561
+ '',
562
+ 'Reads/includes:',
563
+ ...formatPathList(sourceFiles),
564
+ '',
565
+ 'Generates:',
566
+ ...formatPathList(generatedFiles),
567
+ ''
568
+ ].join('\n'));
569
+ }
570
+ function uniqueSorted(values) {
571
+ return [...new Set(values.map((value) => path.resolve(value)))].sort((a, b) => a.localeCompare(b));
572
+ }
573
+ function formatPathList(paths) {
574
+ return paths.length === 0 ? [' (none)'] : paths.map((entry) => ` ${entry}`);
575
+ }
512
576
  function runInit(parsed) {
513
577
  const projectRoot = findProjectRoot(parsed.targetPath);
514
578
  verbose(`init target=${parsed.targetPath} projectRoot=${projectRoot}`);
@@ -859,7 +923,7 @@ function createDefaultLocalFile(agentDir, profile) {
859
923
  function ensureProfileOverridesDir(agentDir) {
860
924
  fs.mkdirSync(path.join(agentDir, 'overrides'), { recursive: true });
861
925
  }
862
- function loadManifest(_configRoot, agentDir, profile) {
926
+ function loadManifest(_configRoot, agentDir, profile, trace) {
863
927
  const manifestPath = path.join(agentDir, MANIFEST_FILE_NAME);
864
928
  if (!fs.existsSync(manifestPath)) {
865
929
  const manifest = defaultManifest(profile);
@@ -879,6 +943,7 @@ function loadManifest(_configRoot, agentDir, profile) {
879
943
  }
880
944
  return manifest;
881
945
  }
946
+ trace?.sourceFiles.add(manifestPath);
882
947
  const text = fs.readFileSync(manifestPath, 'utf8');
883
948
  const errors = [];
884
949
  const parsed = (0, jsonc_parser_1.parse)(text, errors, { allowTrailingComma: true });
@@ -1037,19 +1102,48 @@ function resolveRuntimePath(configRoot, pathTemplate, env, context) {
1037
1102
  const rendered = renderInlineTemplate(env, pathTemplate, context);
1038
1103
  return path.isAbsolute(rendered) ? path.resolve(rendered) : path.resolve(configRoot, rendered);
1039
1104
  }
1040
- function renderTemplateFile(env, configRoot, templatePath, context) {
1105
+ function renderTemplateFile(env, configRoot, templatePath, context, trace) {
1041
1106
  const resolvedPath = resolveConfigPath(configRoot, templatePath, context);
1042
1107
  if (!fs.existsSync(resolvedPath)) {
1043
1108
  throw new Error(`missing template: ${resolvedPath}`);
1044
1109
  }
1045
1110
  const relativePath = path.relative(configRoot, resolvedPath).replace(/\\/g, '/');
1046
1111
  let content = fs.readFileSync(resolvedPath, 'utf8');
1112
+ traceTemplateSource(configRoot, resolvedPath, content, context, trace);
1047
1113
  if (path.basename(resolvedPath) === 'AGENTS-MODS.md') {
1048
- content = renderLegacyAgentsMods(resolvedPath);
1114
+ content = renderLegacyAgentsMods(resolvedPath, [], trace);
1049
1115
  return renderInlineTemplate(env, content, context);
1050
1116
  }
1051
1117
  return env.render(relativePath, context);
1052
1118
  }
1119
+ function traceTemplateSource(configRoot, sourcePath, content, context, trace, stack = []) {
1120
+ if (trace === undefined) {
1121
+ return;
1122
+ }
1123
+ const resolvedSource = path.resolve(sourcePath);
1124
+ trace.sourceFiles.add(resolvedSource);
1125
+ if (stack.includes(resolvedSource)) {
1126
+ return;
1127
+ }
1128
+ const nextStack = [...stack, resolvedSource];
1129
+ const includeRe = /{%\s*(?:include|extends|import)\s+["']([^"']+)["']|{%\s*from\s+["']([^"']+)["']/g;
1130
+ for (;;) {
1131
+ const match = includeRe.exec(content);
1132
+ if (match === null) {
1133
+ break;
1134
+ }
1135
+ const includePath = match[1] ?? match[2];
1136
+ if (!includePath) {
1137
+ continue;
1138
+ }
1139
+ const includedPath = resolveConfigPath(configRoot, includePath, context);
1140
+ if (!fs.existsSync(includedPath)) {
1141
+ continue;
1142
+ }
1143
+ const includedContent = fs.readFileSync(includedPath, 'utf8');
1144
+ traceTemplateSource(configRoot, includedPath, includedContent, context, trace, nextStack);
1145
+ }
1146
+ }
1053
1147
  function renderInlineTemplate(env, source, context) {
1054
1148
  return env.renderString(source, context);
1055
1149
  }
@@ -1090,11 +1184,11 @@ function copySkeletonTree(sourceDir, targetDir) {
1090
1184
  fs.copyFileSync(sourcePath, targetPath);
1091
1185
  }
1092
1186
  }
1093
- function renderProfile(projectRoot, agentDir, checkOnly = false) {
1187
+ function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = null, trace) {
1094
1188
  const configRoot = defaultConfigRoot(projectRoot);
1095
1189
  const profile = resolveProfile(projectRoot);
1096
1190
  const env = createNunjucksEnv(configRoot);
1097
- const manifest = normalizeManifest(loadManifest(configRoot, agentDir, profile), profile);
1191
+ const manifest = normalizeManifest(loadManifest(configRoot, agentDir, profile, trace), profile);
1098
1192
  if (manifest.profile !== profile) {
1099
1193
  const parsed = parseProfile(manifest.profile, `${path.join(agentDir, MANIFEST_FILE_NAME)} profile`);
1100
1194
  if (parsed.profile === null) {
@@ -1105,7 +1199,7 @@ function renderProfile(projectRoot, agentDir, checkOnly = false) {
1105
1199
  ensureConfigRootGitignore(configRoot);
1106
1200
  }
1107
1201
  const context = buildRenderContext(projectRoot, agentDir, configRoot, manifest, env);
1108
- const sections = [renderTemplateFile(env, configRoot, manifest.agent.base, context)];
1202
+ const sections = [renderTemplateFile(env, configRoot, manifest.agent.base, context, trace)];
1109
1203
  for (const include of manifest.agent.includes) {
1110
1204
  const includePath = resolveConfigPath(configRoot, include, context);
1111
1205
  if (!fs.existsSync(includePath)) {
@@ -1114,26 +1208,31 @@ function renderProfile(projectRoot, agentDir, checkOnly = false) {
1114
1208
  }
1115
1209
  throw new Error(`missing template: ${includePath}`);
1116
1210
  }
1117
- sections.push(renderTemplateFile(env, configRoot, include, context));
1211
+ sections.push(renderTemplateFile(env, configRoot, include, context, trace));
1118
1212
  }
1119
1213
  context.renderedAgentSections = sections.map((section, index) => {
1120
1214
  const label = index === 0 ? manifest.agent.base : manifest.agent.includes[index - 1] ?? `section ${index}`;
1121
1215
  assertNoUnexpandedTemplateVars(label, section);
1122
1216
  return section.trimEnd();
1123
1217
  });
1124
- context.skills = renderSkills(env, configRoot, manifest, context);
1218
+ context.skills = renderSkills(env, configRoot, manifest, context, trace);
1125
1219
  const files = [];
1126
- const agentsContent = renderToolInstructions('AGENTS.md', env, configRoot, context, false);
1127
- const claudeContent = renderToolInstructions('CLAUDE.md', env, configRoot, context, true);
1128
- files.push({ path: path.join(context.paths.liveDir, 'AGENTS.md'), content: agentsContent });
1129
- files.push({ path: path.join(context.paths.liveDir, 'CLAUDE.md'), content: claudeContent });
1130
- files.push({ path: path.join(context.paths.liveDir, '.claude', 'CLAUDE.md'), content: claudeContent });
1131
- files.push({ path: path.join(context.paths.liveDir, 'config.toml'), content: renderCodexConfig(env, configRoot, context) });
1132
- files.push({
1133
- path: path.join(context.paths.liveDir, '.claude', 'agent-run-settings.json'),
1134
- content: renderClaudeSettings(env, configRoot, context)
1135
- });
1136
- files.push(...renderNativeSkillFiles(context));
1220
+ if (targetTool === null || targetTool === 'codex') {
1221
+ const agentsContent = renderToolInstructions('AGENTS.md', env, configRoot, context, false, trace);
1222
+ files.push({ path: path.join(context.paths.liveDir, 'AGENTS.md'), content: agentsContent });
1223
+ files.push({ path: path.join(context.paths.liveDir, 'config.toml'), content: renderCodexConfig(env, configRoot, context, trace) });
1224
+ files.push(...renderNativeSkillFiles(context, 'codex'));
1225
+ }
1226
+ if (targetTool === null || targetTool === 'claude') {
1227
+ const claudeContent = renderToolInstructions('CLAUDE.md', env, configRoot, context, true, trace);
1228
+ files.push({ path: path.join(context.paths.liveDir, 'CLAUDE.md'), content: claudeContent });
1229
+ files.push({ path: path.join(context.paths.liveDir, '.claude', 'CLAUDE.md'), content: claudeContent });
1230
+ files.push({
1231
+ path: path.join(context.paths.liveDir, '.claude', 'agent-run-settings.json'),
1232
+ content: renderClaudeSettings(env, configRoot, context, trace)
1233
+ });
1234
+ files.push(...renderNativeSkillFiles(context, 'claude'));
1235
+ }
1137
1236
  files.push(...renderGuardShims(context));
1138
1237
  return {
1139
1238
  agentDir,
@@ -1225,7 +1324,7 @@ function checkRenderedProfile(projectRoot, agentDir) {
1225
1324
  }
1226
1325
  return findings;
1227
1326
  }
1228
- function renderSkills(env, configRoot, manifest, context) {
1327
+ function renderSkills(env, configRoot, manifest, context, trace) {
1229
1328
  const skills = [];
1230
1329
  for (const name of manifest.skills.install) {
1231
1330
  const sourceTemplate = manifest.skills.overrides[name] ?? `global/skills/${name}/SKILL.md.njk`;
@@ -1233,7 +1332,7 @@ function renderSkills(env, configRoot, manifest, context) {
1233
1332
  if (!fs.existsSync(sourcePath)) {
1234
1333
  throw new Error(`missing skill template for ${name}: ${sourcePath}`);
1235
1334
  }
1236
- const renderedContent = renderTemplateFile(env, configRoot, sourceTemplate, context);
1335
+ const renderedContent = renderTemplateFile(env, configRoot, sourceTemplate, context, trace);
1237
1336
  assertNoUnexpandedTemplateVars(`skill ${name}`, renderedContent);
1238
1337
  validateRenderedSkill(renderedContent, sourcePath);
1239
1338
  const description = extractSkillDescription(renderedContent);
@@ -1241,41 +1340,45 @@ function renderSkills(env, configRoot, manifest, context) {
1241
1340
  }
1242
1341
  return skills;
1243
1342
  }
1244
- function renderNativeSkillFiles(context) {
1343
+ function renderNativeSkillFiles(context, targetTool = null) {
1245
1344
  const files = [];
1246
1345
  for (const skill of context.skills) {
1247
- files.push({
1248
- path: path.join(context.paths.codexSkillsDir, skill.name, 'SKILL.md'),
1249
- content: skill.renderedContent
1250
- });
1251
- files.push({
1252
- path: path.join(context.paths.claudeSkillsDir, skill.name, 'SKILL.md'),
1253
- content: skill.renderedContent
1254
- });
1346
+ if (targetTool === null || targetTool === 'codex') {
1347
+ files.push({
1348
+ path: path.join(context.paths.codexSkillsDir, skill.name, 'SKILL.md'),
1349
+ content: skill.renderedContent
1350
+ });
1351
+ }
1352
+ if (targetTool === null || targetTool === 'claude') {
1353
+ files.push({
1354
+ path: path.join(context.paths.claudeSkillsDir, skill.name, 'SKILL.md'),
1355
+ content: skill.renderedContent
1356
+ });
1357
+ }
1255
1358
  }
1256
1359
  return files;
1257
1360
  }
1258
- function renderToolInstructions(templateName, env, configRoot, context, isClaude) {
1361
+ function renderToolInstructions(templateName, env, configRoot, context, isClaude, trace) {
1259
1362
  const templatePath = `global/tool-templates/${templateName}.njk`;
1260
1363
  const absoluteTemplate = path.join(configRoot, templatePath);
1261
1364
  const content = fs.existsSync(absoluteTemplate)
1262
- ? renderTemplateFile(env, configRoot, templatePath, context)
1365
+ ? renderTemplateFile(env, configRoot, templatePath, context, trace)
1263
1366
  : renderInlineTemplate(env, defaultToolInstructionsTemplate(isClaude), context);
1264
1367
  assertNoUnexpandedTemplateVars(templateName, content);
1265
1368
  return content.replace(/\n*$/, '\n');
1266
1369
  }
1267
- function renderCodexConfig(env, configRoot, context) {
1370
+ function renderCodexConfig(env, configRoot, context, trace) {
1268
1371
  const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'codex-config.toml.njk', 'global/tool-templates/codex-config.toml.njk');
1269
1372
  const content = templatePath !== null
1270
- ? renderTemplateFile(env, configRoot, templatePath, context)
1373
+ ? renderTemplateFile(env, configRoot, templatePath, context, trace)
1271
1374
  : defaultCodexConfigContent(context);
1272
1375
  assertNoUnexpandedTemplateVars('config.toml', content);
1273
1376
  return content.replace(/\n*$/, '\n');
1274
1377
  }
1275
- function renderClaudeSettings(env, configRoot, context) {
1378
+ function renderClaudeSettings(env, configRoot, context, trace) {
1276
1379
  const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'claude-settings.json.njk', 'global/tool-templates/claude-settings.json.njk');
1277
1380
  const content = templatePath !== null
1278
- ? renderTemplateFile(env, configRoot, templatePath, context)
1381
+ ? renderTemplateFile(env, configRoot, templatePath, context, trace)
1279
1382
  : defaultClaudeSettingsContent(context);
1280
1383
  assertNoUnexpandedTemplateVars('claude settings.json', content);
1281
1384
  return content.replace(/\n*$/, '\n');
@@ -1347,8 +1450,9 @@ function extractSkillDescription(content) {
1347
1450
  }
1348
1451
  return '';
1349
1452
  }
1350
- function renderLegacyAgentsMods(sourceFile, stack = []) {
1453
+ function renderLegacyAgentsMods(sourceFile, stack = [], trace) {
1351
1454
  const resolvedSource = path.resolve(sourceFile);
1455
+ trace?.sourceFiles.add(resolvedSource);
1352
1456
  if (stack.includes(resolvedSource)) {
1353
1457
  throw new Error(`Include cycle detected: ${[...stack, resolvedSource].join(' -> ')}`);
1354
1458
  }
@@ -1368,7 +1472,9 @@ function renderLegacyAgentsMods(sourceFile, stack = []) {
1368
1472
  if (!includePath) {
1369
1473
  continue;
1370
1474
  }
1371
- output.push(renderLegacyAgentsMods(resolveIncludePath(resolvedSource, includePath), nextStack));
1475
+ const resolvedInclude = resolveIncludePath(resolvedSource, includePath);
1476
+ trace?.sourceFiles.add(resolvedInclude);
1477
+ output.push(renderLegacyAgentsMods(resolvedInclude, nextStack, trace));
1372
1478
  if (!contentStarted) {
1373
1479
  sawLeadingInclude = true;
1374
1480
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.13",
3
+ "version": "0.99.15",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js"