@ashulab/agent-forge 0.2.0 → 0.4.0

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/README.md CHANGED
@@ -9,7 +9,7 @@ On each run the launcher:
9
9
  - detects installed provider CLIs on your PATH
10
10
  - asks which provider to use (or takes it from a flag)
11
11
  - starts the provider with the GitHub token and a per-agent git identity injected into the environment
12
- - writes the agent's `systemPrompt` into the provider's startup memory file (`CLAUDE.md` / `AGENTS.md`) so subagents inherit the identity
12
+ - injects the agent's `systemPrompt` per process (`claude --append-system-prompt`, `codex -c developer_instructions`; antigravity falls back to an `AGENTS.md` block)
13
13
 
14
14
  Provider auth stays in the provider CLI — the launcher never handles provider API keys.
15
15
 
@@ -60,9 +60,10 @@ The shape, for reference — the `"$schema"` line points at the published schema
60
60
  }
61
61
  ```
62
62
 
63
- - The **installation id is not stored** — it is resolved on each run, in order: the owner of the repo you launch in, then `account`, then the App's sole installation. So the same agent works across every org the App is installed on, and it survives an uninstall/reinstall (which rotates the id).
64
- - Add `"account": "<org-or-user login>"` only when you run outside a matching repo and the App has more than one installation.
65
- - `installationId` is still accepted as an explicit override — set it to pin a specific id or skip the lookup (offline, or in a git credential helper).
63
+ - The **installation id is not stored** — it is resolved on each run, in order: the owner of the repo you launch in, then the App's sole installation. So the same agent works across every org the App is installed on, and it survives an uninstall/reinstall (which rotates the id).
64
+ - An App installed on more than one account only resolves when you launch from inside a repo it covers that repo picks the installation.
65
+ - `installationId` is accepted as an explicit override — set it to pin a specific id or skip the lookup (offline, or in a git credential helper).
66
+ - One agent, many accounts: a **private** GitHub App only installs on the account that owns it. To run the agent on both a personal account and an org, make the App public or use one App per account — see [docs/github-app.md](docs/github-app.md).
66
67
  - `botId` is optional. When omitted it is resolved from the GitHub API using `botName` so the git author email links commits to the bot. Set it explicitly only to skip that lookup (e.g. offline).
67
68
  - Do not put provider-specific config here — providers are auto-detected from PATH.
68
69
 
@@ -87,23 +88,25 @@ By default the installation token carries the full installation scope. Narrow it
87
88
 
88
89
  Omitting the `permissions` key entirely is the safe default — the token then carries whatever the App grants. `repositories` works the same way: list it and the token can only touch those repos.
89
90
 
90
- On launch, `agent-forge` prints the scope GitHub actually granted (`token` row in the summary); `agent-forge token --agent <name>` prints it to stderr. If a call fails with `403 Resource not accessible by integration`, check that row first.
91
+ On launch, `agent-forge` prints the scope GitHub actually granted (`scope` row in the summary, alongside the `account` the token is for); `agent-forge token --agent <name>` prints it to stderr. If a call fails with `403 Resource not accessible by integration`, check that row first.
91
92
 
92
93
  ## Add an agent
93
94
 
94
95
  You need a GitHub App first — its **App ID**, a **private key**, and an **installation**. If you don't have one yet, follow [docs/github-app.md](docs/github-app.md).
95
96
 
97
+ Running `agent-forge` with no agents configured offers to run the wizard for you.
98
+
96
99
  ### Guided (recommended)
97
100
 
98
101
  ```bash
99
102
  agent-forge add
100
103
  ```
101
104
 
102
- You provide the **GitHub App ID** and the **path to its private key**. The wizard derives the rest from the GitHub API — slug (`botName`), bot user id, and the account (asked only when the App is installed on more than one) — asks for a label and an optional system prompt, mints a test token to confirm it works, and writes the entry. It writes to the resolved config path — the global registry (`~/.config/agent-forge/agents.json`) unless `$AGENT_FORGE_CONFIG` or an existing `./agents.json` redirects it.
105
+ You provide the **GitHub App ID** and the **path to its private key**. The wizard derives the rest from the GitHub API — slug (`botName`), bot user id — asks for a label and an optional system prompt, mints a test token to confirm it works, and writes the entry. It writes to the resolved config path — the global registry (`~/.config/agent-forge/agents.json`) unless `$AGENT_FORGE_CONFIG` or an existing `./agents.json` redirects it.
103
106
 
104
107
  ### Manual
105
108
 
106
- Copy one agent object inside the `agents` array and set unique values for `name`, `label`, `appId`, `botName`, `privateKeyPath` — plus `account` if the App has more than one installation. Make sure the private key file exists and is readable.
109
+ Copy one agent object inside the `agents` array and set unique values for `name`, `label`, `appId`, `botName`, `privateKeyPath`. Make sure the private key file exists and is readable.
107
110
 
108
111
  ## Launch
109
112
 
@@ -113,26 +116,41 @@ Interactive:
113
116
  agent-forge
114
117
  ```
115
118
 
116
- Direct flags (passing both skips all prompts; passing one pre-fills it):
119
+ Direct flags (passing both skips the menus; passing one pre-fills it, the other is prompted — or auto-picked when only one agent or provider is available):
117
120
 
118
121
  ```bash
119
122
  agent-forge --agent ops-agent --provider claude
120
123
  agent-forge --agent review-agent --provider codex
121
124
  ```
122
125
 
126
+ Before launching, it prints a summary:
127
+
128
+ ```
129
+ │ provider claude
130
+ │ identity ops-agent[bot]
131
+ │ account AshuLab ← which account the token is for
132
+ │ repo AshuLab/app ← the repo you're in (drives account)
133
+ │ scope write: contents, issues, pull_requests · +4 read
134
+ │ expires ~59m ← installation tokens last ~1h
135
+ ```
136
+
137
+ (The `antigravity` provider adds a `memory` row pointing at the `AGENTS.md` it wrote.)
138
+
123
139
  Other commands:
124
140
 
125
141
  ```bash
126
142
  agent-forge --list
143
+ agent-forge --version
127
144
  agent-forge --help
128
145
  ```
129
146
 
130
147
  ### Provider-specific prompt behavior
131
148
 
132
- - `claude`: passed via `--append-system-prompt` (lands in the real system prompt)
133
- - `codex` / `antigravity` (`agy`): no CLI flag — identity comes from `AGENTS.md` only (`agy --prompt` is headless `--print`; a codex positional arg is a fake first user turn)
149
+ `systemPrompt` is injected per process no repo file is touched:
134
150
 
135
- The launcher also writes `systemPrompt` into the provider's startup memory file in the working directory (`CLAUDE.md` for claude, `AGENTS.md` for codex and antigravity), inside a managed `agent-forge:identity` block. This keeps the identity in place for subagents the provider spawns, not just its main thread. The block is rewritten on each run and safe to keep in version control.
151
+ - `claude`: `--append-system-prompt` (lands in the real system prompt)
152
+ - `codex`: `-c developer_instructions=…` (appends a developer message; unlike `model_instructions_file` it does not replace codex's base prompt)
153
+ - `antigravity` (`agy`): no such flag — the launcher writes `systemPrompt` into `AGENTS.md` in the working directory, inside a managed `agent-forge:identity` block. Do not commit it. Run one agent per worktree.
136
154
 
137
155
  ## Token refresh
138
156
 
package/dist/launcher.cjs CHANGED
@@ -4017,7 +4017,7 @@ var require_jsonwebtoken = __commonJS({
4017
4017
  // src/launcher.js
4018
4018
  var import_node_child_process3 = require("node:child_process");
4019
4019
  var import_node_path4 = require("node:path");
4020
- var import_node_util3 = require("node:util");
4020
+ var import_node_util4 = require("node:util");
4021
4021
 
4022
4022
  // node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
4023
4023
  var import_node_util = require("node:util");
@@ -5377,19 +5377,9 @@ function pickInstallation(installations, agent) {
5377
5377
  if (installations.length === 0) {
5378
5378
  throw new Error(`GitHub App ${agent.appId} has no installations. Install it on an account or org first.`);
5379
5379
  }
5380
- if (agent.account) {
5381
- const want = String(agent.account).toLowerCase();
5382
- const match = installations.find((item) => item.account.toLowerCase() === want);
5383
- if (!match) {
5384
- throw new Error(
5385
- `GitHub App ${agent.appId} is not installed on "${agent.account}". Installed on: ${installations.map((item) => item.account).join(", ")}.`
5386
- );
5387
- }
5388
- return match.id;
5389
- }
5390
5380
  if (installations.length === 1) return installations[0].id;
5391
5381
  throw new Error(
5392
- `GitHub App ${agent.appId} is installed on ${installations.length} accounts (${installations.map((item) => item.account).join(", ")}). Run inside a repo under one of them, or add "account" to agent "${agent.name}".`
5382
+ `GitHub App ${agent.appId} is installed on ${installations.length} accounts (${installations.map((item) => item.account).join(", ")}). Launch from inside a repo under one of them.`
5393
5383
  );
5394
5384
  }
5395
5385
  function parseGithubRemote(url) {
@@ -5409,19 +5399,21 @@ function currentRepoSlug() {
5409
5399
  async function installationForRepo(slug, jwtToken) {
5410
5400
  try {
5411
5401
  const data = await githubApi(`/repos/${slug.owner}/${slug.repo}/installation`, { jwtToken });
5412
- return String(data.id);
5402
+ return { id: String(data.id), account: data.account?.login || data.account?.slug || null };
5413
5403
  } catch {
5414
5404
  return null;
5415
5405
  }
5416
5406
  }
5417
- async function resolveInstallationId(agent, jwtToken) {
5418
- if (agent.installationId) return String(agent.installationId);
5407
+ async function resolveInstallation(agent, jwtToken) {
5408
+ if (agent.installationId) return { id: String(agent.installationId), account: null };
5419
5409
  const slug = currentRepoSlug();
5420
5410
  if (slug) {
5421
- const id = await installationForRepo(slug, jwtToken);
5422
- if (id) return id;
5411
+ const hit = await installationForRepo(slug, jwtToken);
5412
+ if (hit) return hit;
5423
5413
  }
5424
- return pickInstallation(await listInstallations(jwtToken), agent);
5414
+ const installations = await listInstallations(jwtToken);
5415
+ const id = pickInstallation(installations, agent);
5416
+ return { id, account: installations.find((item) => item.id === id)?.account ?? null };
5425
5417
  }
5426
5418
  function buildTokenScope(agent) {
5427
5419
  const scope = {};
@@ -5432,13 +5424,15 @@ function buildTokenScope(agent) {
5432
5424
  async function generateGithubAppToken(agent) {
5433
5425
  const privateKey = readPrivateKey(agent.privateKeyPath, agent.name || agent.appId);
5434
5426
  const jwtToken = signAppJwt(agent.appId, privateKey);
5435
- const installationId = await resolveInstallationId(agent, jwtToken);
5427
+ const { id: installationId, account } = await resolveInstallation(agent, jwtToken);
5436
5428
  const data = await githubApi(`/app/installations/${installationId}/access_tokens`, {
5437
5429
  jwtToken,
5438
5430
  body: buildTokenScope(agent)
5439
5431
  });
5440
5432
  return {
5441
5433
  token: data.token,
5434
+ account,
5435
+ expiresAt: data.expires_at || null,
5442
5436
  // What GitHub actually granted, after both the App's config and our scoping.
5443
5437
  permissions: data.permissions || {},
5444
5438
  repositorySelection: data.repository_selection || "all"
@@ -5480,7 +5474,9 @@ function getProviderPromptArgs(providerName, agent) {
5480
5474
  if (!prompt) {
5481
5475
  return [];
5482
5476
  }
5483
- return providerName === "claude" ? ["--append-system-prompt", prompt] : [];
5477
+ if (providerName === "claude") return ["--append-system-prompt", prompt];
5478
+ if (providerName === "codex") return ["-c", `developer_instructions=${JSON.stringify(prompt)}`];
5479
+ return [];
5484
5480
  }
5485
5481
 
5486
5482
  // src/identity.js
@@ -5488,7 +5484,7 @@ var import_node_fs3 = require("node:fs");
5488
5484
  var import_node_path3 = require("node:path");
5489
5485
  var START = "<!-- agent-forge:identity:start -->";
5490
5486
  var END = "<!-- agent-forge:identity:end -->";
5491
- var MEMORY_FILE = { claude: "CLAUDE.md", codex: "AGENTS.md", antigravity: "AGENTS.md" };
5487
+ var MEMORY_FILE = { antigravity: "AGENTS.md" };
5492
5488
  function upsertIdentityBlock(content, prompt) {
5493
5489
  const block2 = `${START}
5494
5490
  ${prompt}
@@ -5515,10 +5511,97 @@ function syncIdentityFile(providerName, agent, cwd = process.cwd()) {
5515
5511
  return file;
5516
5512
  }
5517
5513
 
5514
+ // src/format.js
5515
+ function formatScope(permissions, full = false) {
5516
+ const entries = Object.entries(permissions);
5517
+ if (entries.length === 0) return "full installation scope";
5518
+ if (full) return entries.map(([name, level]) => `${name}:${level}`).join(" ");
5519
+ const writes = entries.filter(([, level]) => level !== "read").map(([name]) => name);
5520
+ if (writes.length === 0) return `read-only (${entries.length})`;
5521
+ const reads = entries.length - writes.length;
5522
+ return `write: ${writes.join(", ")}${reads ? ` \xB7 +${reads} read` : ""}`;
5523
+ }
5524
+ function formatExpiry(iso) {
5525
+ const ms = new Date(iso).getTime() - Date.now();
5526
+ if (!Number.isFinite(ms)) return String(iso);
5527
+ const minutes = Math.round(ms / 6e4);
5528
+ return minutes > 0 ? `~${minutes}m` : "expired";
5529
+ }
5530
+
5531
+ // src/cli.js
5532
+ var import_node_util3 = require("node:util");
5533
+
5534
+ // package.json
5535
+ var package_default = {
5536
+ name: "@ashulab/agent-forge",
5537
+ version: "0.4.0",
5538
+ description: "Give claude/codex/antigravity their own GitHub identity: a bot account, a scoped token minted per run, git attribution to match",
5539
+ license: "MIT",
5540
+ author: "Diego Ghersi <ghersidl@gmail.com>",
5541
+ type: "module",
5542
+ bin: {
5543
+ "agent-forge": "dist/launcher.cjs"
5544
+ },
5545
+ main: "dist/launcher.cjs",
5546
+ files: [
5547
+ "dist",
5548
+ "schema",
5549
+ "LICENSE",
5550
+ "README.md"
5551
+ ],
5552
+ engines: {
5553
+ node: ">=24"
5554
+ },
5555
+ packageManager: "pnpm@11.20.0",
5556
+ repository: {
5557
+ type: "git",
5558
+ url: "git+https://github.com/AshuLab/agent-forge.git"
5559
+ },
5560
+ bugs: {
5561
+ url: "https://github.com/AshuLab/agent-forge/issues"
5562
+ },
5563
+ homepage: "https://github.com/AshuLab/agent-forge#readme",
5564
+ keywords: [
5565
+ "github-app",
5566
+ "cli",
5567
+ "coding-agent",
5568
+ "claude",
5569
+ "codex",
5570
+ "antigravity",
5571
+ "installation-token",
5572
+ "git-identity"
5573
+ ],
5574
+ publishConfig: {
5575
+ access: "public"
5576
+ },
5577
+ scripts: {
5578
+ dev: "node src/launcher.js",
5579
+ test: "node --test",
5580
+ start: "node dist/launcher.cjs",
5581
+ build: "esbuild src/launcher.js --bundle --platform=node --format=cjs --target=node24 --outfile=dist/launcher.cjs --banner:js='#!/usr/bin/env node' && chmod +x dist/launcher.cjs",
5582
+ prepublishOnly: "pnpm build && pnpm test"
5583
+ },
5584
+ dependencies: {
5585
+ "@clack/prompts": "^1.7.0",
5586
+ jsonwebtoken: "^9.0.3"
5587
+ },
5588
+ devDependencies: {
5589
+ esbuild: "^0.28.2"
5590
+ }
5591
+ };
5592
+
5518
5593
  // src/cli.js
5594
+ var LOGO = `
5595
+ \u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584 \u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584 \u2584\u2584 \u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584
5596
+ \u2588\u2584\u2588 \u2588 \u2584 \u2588\u2584 \u2588 \u2588 \u2588 \u2588\u2584 \u2588 \u2588 \u2588\u2584\u2580 \u2588 \u2584 \u2588\u2584
5597
+ \u2588 \u2588 \u2580\u2584\u2588 \u2588\u2584\u2584 \u2588 \u2588 \u2588 \u2588 \u2580\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2588 \u2588\u2584\u2584
5598
+ `;
5599
+ function printVersion() {
5600
+ console.log(package_default.version);
5601
+ }
5519
5602
  function printHelp() {
5520
5603
  console.log(`
5521
- Agent Forge
5604
+ Agent Forge v${package_default.version}
5522
5605
 
5523
5606
  Usage:
5524
5607
  agent-forge
@@ -5526,6 +5609,7 @@ Usage:
5526
5609
  agent-forge add guided setup for a new agent
5527
5610
  agent-forge token --agent <name> print a fresh GitHub App token (for refresh)
5528
5611
  agent-forge --list
5612
+ agent-forge --version
5529
5613
  agent-forge --help
5530
5614
 
5531
5615
  Requirements:
@@ -5539,10 +5623,13 @@ Config resolution (first match wins):
5539
5623
  - ./agents.json (only if it already exists)
5540
5624
  `);
5541
5625
  }
5626
+ function bail(message = "Cancelled") {
5627
+ cancel(`\u2716 ${message}`);
5628
+ process.exit(0);
5629
+ }
5542
5630
  function keep(value) {
5543
5631
  if (isCancel(value)) {
5544
- cancel("Cancelled.");
5545
- process.exit(0);
5632
+ bail();
5546
5633
  }
5547
5634
  return value;
5548
5635
  }
@@ -5569,17 +5656,8 @@ async function addAgentWizard({ embedded = false } = {}) {
5569
5656
  if (meta.installations.length === 0) {
5570
5657
  throw new Error("This App has no installations. Install it on an account or org first.");
5571
5658
  }
5572
- let account;
5573
- if (meta.installations.length === 1) {
5574
- note(`${meta.installations[0].account} \u2014 resolved automatically each run`, "Installation");
5575
- } else {
5576
- account = keep(
5577
- await select({
5578
- message: "Which account is this agent for?",
5579
- options: meta.installations.map((i2) => ({ value: i2.account, label: `${i2.account} (${i2.id})` }))
5580
- })
5581
- );
5582
- }
5659
+ const where = meta.installations.length === 1 ? `${meta.installations[0].account} \u2014 resolved automatically each run` : `${meta.installations.map((i2) => i2.account).join(", ")} \u2014 resolved from the repo you launch in`;
5660
+ note(where, "Installation");
5583
5661
  const name = (keep(await text({ message: "Agent name", placeholder: meta.slug, defaultValue: meta.slug })) || meta.slug).trim();
5584
5662
  const label = (keep(await text({ message: "Label", placeholder: meta.name, defaultValue: meta.name })) || meta.name).trim();
5585
5663
  const systemPrompt = keep(await text({ message: "System prompt (optional)", placeholder: "You are ..." })).trim();
@@ -5589,7 +5667,6 @@ async function addAgentWizard({ embedded = false } = {}) {
5589
5667
  appId,
5590
5668
  botName: meta.slug,
5591
5669
  privateKeyPath,
5592
- ...account ? { account } : {},
5593
5670
  ...systemPrompt ? { systemPrompt } : {}
5594
5671
  };
5595
5672
  const check = spinner();
@@ -5614,7 +5691,7 @@ async function interactiveSelection(defaults = {}) {
5614
5691
  if (agents.length === 0 && !defaults.agent) {
5615
5692
  note("No agents configured yet. Each agent is a GitHub App identity the launcher runs under.", "First run");
5616
5693
  if (!keep(await confirm({ message: "Add one now?" }))) {
5617
- cancel(`Run "agent-forge add" when you're ready.`);
5694
+ outro(`Run ${(0, import_node_util3.styleText)("cyan", "agent-forge add")} when you're ready.`);
5618
5695
  process.exit(0);
5619
5696
  }
5620
5697
  await addAgentWizard({ embedded: true });
@@ -5644,9 +5721,9 @@ async function interactiveSelection(defaults = {}) {
5644
5721
  })
5645
5722
  );
5646
5723
  }
5647
- if (!keep(await confirm({ message: `Launch ${agentChoice} with ${providerChoice}?` }))) {
5648
- cancel("Cancelled.");
5649
- process.exit(0);
5724
+ const launchPrompt = `Launch ${(0, import_node_util3.styleText)("green", agentChoice)} with ${(0, import_node_util3.styleText)(["cyan", "bold"], providerChoice)}?`;
5725
+ if (!keep(await confirm({ message: launchPrompt }))) {
5726
+ bail("Cancelled \u2014 nothing launched");
5650
5727
  }
5651
5728
  return { agentName: agentChoice, providerName: providerChoice };
5652
5729
  }
@@ -5655,6 +5732,7 @@ function parseArgs() {
5655
5732
  const result = {
5656
5733
  command: args[0] && !args[0].startsWith("-") ? args[0] : void 0,
5657
5734
  help: false,
5735
+ version: false,
5658
5736
  list: false,
5659
5737
  agent: void 0,
5660
5738
  provider: void 0
@@ -5664,6 +5742,7 @@ function parseArgs() {
5664
5742
  const arg = args[i2];
5665
5743
  const next = args[i2 + 1];
5666
5744
  if (arg === "--help" || arg === "-h") result.help = true;
5745
+ if (arg === "--version" || arg === "-v") result.version = true;
5667
5746
  if (arg === "--list" || arg === "-l") result.list = true;
5668
5747
  if (arg === "--agent" || arg === "-a") {
5669
5748
  result.agent = value(next);
@@ -5677,7 +5756,8 @@ function parseArgs() {
5677
5756
  return result;
5678
5757
  }
5679
5758
  function showIntro() {
5680
- intro("Agent Forge");
5759
+ console.log((0, import_node_util3.styleText)("cyan", LOGO));
5760
+ intro(`Agent Forge ${(0, import_node_util3.styleText)("dim", `v${package_default.version}`)}`);
5681
5761
  }
5682
5762
 
5683
5763
  // src/launcher.js
@@ -5703,10 +5783,10 @@ async function launchAgent(agentName, providerName) {
5703
5783
  const providerInfo = getProviderInfo(providerName, agent);
5704
5784
  const s = spinner();
5705
5785
  s.start("Minting installation token");
5706
- const { token: githubToken, permissions, repositorySelection } = await generateGithubAppToken(agent);
5786
+ const { token: githubToken, account, expiresAt, permissions, repositorySelection } = await generateGithubAppToken(agent);
5707
5787
  const botId = await resolveBotId(agent);
5708
5788
  const gitIdentity = buildGitIdentity(agent, botId);
5709
- s.stop(`Token ready for ${(0, import_node_util3.styleText)("green", gitIdentity.GIT_AUTHOR_NAME)}`);
5789
+ s.stop(`Token ready for ${(0, import_node_util4.styleText)("green", gitIdentity.GIT_AUTHOR_NAME)}`);
5710
5790
  const identityFile = syncIdentityFile(providerName, agent);
5711
5791
  const runtimeEnv = {
5712
5792
  ...process.env,
@@ -5714,18 +5794,24 @@ async function launchAgent(agentName, providerName) {
5714
5794
  GH_TOKEN: githubToken,
5715
5795
  ...gitIdentity
5716
5796
  };
5717
- const scopeEntries = Object.entries(permissions);
5718
- const scopeText = scopeEntries.length ? scopeEntries.map(([name, level]) => `${name}:${level}`).join(" ") : "full installation scope";
5719
- const row = (label, value) => `${(0, import_node_util3.styleText)("dim", label.padEnd(10))}${value}`;
5797
+ const narrowed = Boolean(agent.permissions) || Boolean(agent.repositories);
5798
+ const scopeText = formatScope(permissions, narrowed);
5799
+ const repoSlug = currentRepoSlug();
5800
+ const row = (label, value) => `${(0, import_node_util4.styleText)("dim", label.padEnd(10))}${value}`;
5720
5801
  const summary = [
5721
- row("provider", (0, import_node_util3.styleText)(["cyan", "bold"], providerName)),
5722
- row("identity", (0, import_node_util3.styleText)("green", gitIdentity.GIT_AUTHOR_NAME)),
5723
- row("token", (0, import_node_util3.styleText)("dim", repositorySelection === "selected" ? `${scopeText} \xB7 selected repos` : scopeText))
5802
+ row("provider", (0, import_node_util4.styleText)(["cyan", "bold"], providerName)),
5803
+ row("identity", (0, import_node_util4.styleText)("green", gitIdentity.GIT_AUTHOR_NAME))
5724
5804
  ];
5805
+ if (account) summary.push(row("account", (0, import_node_util4.styleText)("yellow", account)));
5806
+ if (repoSlug) summary.push(row("repo", (0, import_node_util4.styleText)("dim", `${repoSlug.owner}/${repoSlug.repo}`)));
5807
+ summary.push(
5808
+ row("scope", (0, import_node_util4.styleText)("dim", repositorySelection === "selected" ? `${scopeText} \xB7 selected repos` : scopeText))
5809
+ );
5810
+ if (expiresAt) summary.push(row("expires", (0, import_node_util4.styleText)("dim", formatExpiry(expiresAt))));
5725
5811
  if (identityFile) {
5726
- summary.push(row("memory", (0, import_node_util3.styleText)("dim", (0, import_node_path4.relative)(process.cwd(), identityFile) || identityFile)));
5812
+ summary.push(row("memory", (0, import_node_util4.styleText)("dim", (0, import_node_path4.relative)(process.cwd(), identityFile) || identityFile)));
5727
5813
  }
5728
- note(summary.join("\n"), (0, import_node_util3.styleText)("bold", agent.label || agent.name));
5814
+ note(summary.join("\n"), (0, import_node_util4.styleText)("bold", agent.label || agent.name));
5729
5815
  const providerArgs = getProviderPromptArgs(providerName, agent);
5730
5816
  const child = (0, import_node_child_process3.spawn)(providerInfo.command, providerArgs, {
5731
5817
  stdio: "inherit",
@@ -5739,7 +5825,7 @@ async function launchAgent(agentName, providerName) {
5739
5825
  cancel(`Could not execute ${providerInfo.command}: ${error.message}`);
5740
5826
  process.exit(1);
5741
5827
  });
5742
- outro(`Running ${(0, import_node_util3.styleText)("cyan", providerName)}\u2026`);
5828
+ outro(`Running ${(0, import_node_util4.styleText)("cyan", providerName)}\u2026`);
5743
5829
  }
5744
5830
  async function main() {
5745
5831
  const flags = parseArgs();
@@ -5747,6 +5833,10 @@ async function main() {
5747
5833
  printHelp();
5748
5834
  return;
5749
5835
  }
5836
+ if (flags.version) {
5837
+ printVersion();
5838
+ return;
5839
+ }
5750
5840
  if (flags.list) {
5751
5841
  listAgents();
5752
5842
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashulab/agent-forge",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Give claude/codex/antigravity their own GitHub identity: a bot account, a scoped token minted per run, git attribution to match",
5
5
  "license": "MIT",
6
6
  "author": "Diego Ghersi <ghersidl@gmail.com>",
@@ -25,13 +25,9 @@
25
25
  "type": "string",
26
26
  "description": "GitHub App ID (numeric, stored as a string)"
27
27
  },
28
- "account": {
29
- "type": "string",
30
- "description": "Account/org login where the App is installed. Fallback for installation-id resolution when you run outside a matching repo. Needed only when the App has more than one installation. Resolution order: repo owner, then account, then the sole installation."
31
- },
32
28
  "installationId": {
33
29
  "type": "string",
34
- "description": "Optional override. Normally the installation id is resolved (repo owner, then account, then the sole installation). Set this to pin a specific id or skip the lookup (offline, or in a git credential helper)."
30
+ "description": "Optional override. Normally the installation id is resolved (repo owner, then the App's sole installation). Set this to pin a specific id or skip the lookup (offline, or in a git credential helper)."
35
31
  },
36
32
  "botName": {
37
33
  "type": "string",